Sunday, April 18, 2010

Thoughts on LSPM from R/Finance 2010

I just got back from R/Finance 2010 in Chicago. If you couldn't make it this year, I strongly encourage you to attend next year. I will post a more comprehensive review of the event in the next couple days, but I wanted to share some of my notes specific to LSPM.
  • How sensitive are optimal-f values to the method used to construct the joint probability table?
  • Is there an optimizer better suited for this problem (e.g. CMA-ES, or adaptive differential evolution)?
  • How accurate are the estimates of the probability of drawdown, ruin, profit, etc.?
  • What could be learned from ruin theory (see the actuar package)?
These notes are mostly from many great conversations I had with other attendees, rather than thoughts I had while listening to the presentations. That is not a criticism of the presentations, but an illustration of the quality of the other conference-goers.

    Sunday, April 11, 2010

    Historical / Future Volatility Correlation Stability

    Michael Stokes, author of the MarketSci blog recently published a thought-provoking post about the correlation between historical and future volatility (measured as the standard deviation of daily close price percentage changes). This post is intended as an extension of his "unfinished thought", not a critique.

    He suggests using his table of volatility correlations as a back-of-the-envelope approach to estimate future volatility, which led me to question the stability of the correlations in his table. His table's values are calculated using daily data from 1970-present... but what if you were to calculate correlations using only one year of data, rather than thirty? The chart below shows the results.


    The chart shows the rolling one-year (252-day) correlations for the diagonal in Michael's table (e.g. historical and future 2-day volatility, ..., historical and future 252-day volatility). You can see the shorter periods are generally more stable, but are also closer to zero. The rolling one-year correlation between historical and future one-year volatility swings wildly from +/-1 over time.

    This isn't to argue that Michael's back-of-the-envelope approach is incorrect, rather it is an attempt to make the approach more robust by weighing long-term market characteristics against recent market behavior.

    For those interested, here is the R code I used to replicate Michael's table and create the graph above. An interesting extension of this analysis would be to calculate volatility using TTR's volatility() function instead of standard deviation. I'll leave that exercise to the interested reader.

    require(quantmod)

    # pull SPX data from Yahoo Finance
    getSymbols("^GSPC",from="1970-01-01")

    # volatility horizons
    GSPC$v2 <- runSD(ROC(Cl(GSPC)),2)
    GSPC$v5 <- runSD(ROC(Cl(GSPC)),5)
    GSPC$v10 <- runSD(ROC(Cl(GSPC)),10)
    GSPC$v21 <- runSD(ROC(Cl(GSPC)),21)
    GSPC$v63 <- runSD(ROC(Cl(GSPC)),63)
    GSPC$v252 <- runSD(ROC(Cl(GSPC)),252)

    # volatility horizon lags
    GSPC$l2 <- lag(GSPC$v2,-2)
    GSPC$l5 <- lag(GSPC$v5,-5)
    GSPC$l10 <- lag(GSPC$v10,-10)
    GSPC$l21 <- lag(GSPC$v21,-21)
    GSPC$l63 <- lag(GSPC$v63,-63)
    GSPC$l252 <- lag(GSPC$v252,-252)

    # volatility correlation table
    cor(GSPC[,7:18],use="pair")[1:6,7:12]

    # remove missing observations
    GSPC <- na.omit(GSPC)

    # rolling 1-year volatility correlations
    GSPC$c2 <- runCor(GSPC$v2,GSPC$l2,252)
    GSPC$c5 <- runCor(GSPC$v5,GSPC$l5,252)
    GSPC$c10 <- runCor(GSPC$v10,GSPC$l10,252)
    GSPC$c21 <- runCor(GSPC$v21,GSPC$l21,252)
    GSPC$c63 <- runCor(GSPC$v63,GSPC$l63,252)
    GSPC$c252 <- runCor(GSPC$v252,GSPC$l252,252)

    # plot rolling 1-year volatility correlations
    plot.zoo(GSPC[,grep("c",colnames(GSPC))],n=1,
     main="Rolling 252-Day Volitility Correlations")

    Friday, April 9, 2010

    Maximum Probability of Profit

    To continue with the LSPM examples, this post shows how to optimize a Leverage Space Portfolio for the maximum probability of profit. The data and example are again taken from The Leverage Space Trading Model by Ralph Vince.

    These optimizaitons take a very long time. 100 iterations on a 10-core Amazon EC2 cluster took 21 hours. Again, the results will not necessarily match the book because of differences between DEoptim and Ralph's genetic algorithm and because there are multiple possible paths one can take through leverage space that will achieve similar results.

    The results from the EC2 run were:
    iteration: 100 best member: 0.0275 0 0.0315 -0.928 -1 best value: -0.9999
    The book results (on p. 173) were:
    iteration: 100 best member: 0.085 0.015 0.129 -0.76 -0.992 best value: -0.9999

    Specifying an initial population can give DEoptim an initial set of parameters that are within the constraint. This guarantees a starting point but it can slow optimization if the f (and/or z) values are too low. Therefore, experiment with the initial population to find a set of f (and/or z) values that produce a result within, but not far from, the constraint.


    # Load the LSPM and snow packages
    library(LSPM)
    library(snow)

    # Multiple strategy example (data found on pp. 84-87, 169)
    trades <- cbind(
      c(-150,-45.333,-45.333,13,13,13,13,13,79.667,79.667,79.667,136),
      c(253,-1000,-64.429,-64.429,-64.429,253,253,448,-64.429,-64.429,-64.429,253),
      c(533,220.143,220.143,-500,533,220.143,799,220.143,-325,220.143,533,220.143))

    probs <- c(0.076923076923,0.076923076923,0.153846153846,0.076923076923,
      0.076923076923,0.076923076923,0.076923076923,0.076923076923,
      0.076923076923,0.076923076923,0.076923076923,0.076923076923)

    # Create a Leverage Space Portfolio object
    port <- lsp(trades,probs)

    # Number of population members
    np <- 30

    # Initial population
    initpop <- cbind(runif(np,0,0.01),runif(np,0,0.01),runif(np,0,0.01),
      runif(np,-1,-0.8),runif(np,-1,-0.8))

    # DEoptim parameters (see ?deoptim)
    DEctrl <- list(NP=np, itermax=11, refresh=1, digits=6, initial=initpop)

    # Create a socket cluster with snow to use both cores
    # on a dual-core processor
    cl <- makeSOCKcluster(2)

    # Drawdown-constrained maximum probability of profit (results on p. 173)
    res <- maxProbProfit(port, 1e-6, 12, probDrawdown, 0.1,
      DD=0.2, calc.max=4, snow=cl, control=DEctrl)