Monte Carlo Analysis in AmiBroker

This post will cover in detail two different ways of doing Monte Carlo analysis and the code needed to it in AmiBroker. A reader recently sent me this article, Monte Carlo Analysis For Trading Systems. The article covers three methods of Monte Carlo analysis. One of which I had never thought about and I had to slap my head on how simple it was.

What is Monte Carlo analysis?

The general idea behind Monte Carlo analysis is to add randomness to one’s strategy and see how it performs. You do this several hundred to several thousand times and one can compute the average, standard deviation of all the runs for CAR, Drawdowns, and other portfolio statistics. The idea being does the system behave well when some randomness is added? We will explore in depth two ways of doing Monte Carlo analysis in AmiBroker.

The Mean Reversion Strategy

To run on Monte Carlo analysis on, we will use a simple mean reversion strategy.

Test from 1/1/2004 to 6/30/2014. Maximum of 10 open positions at one time.

Setup Rules

  • Stock is a member of the Russell 3000
  • The 21 day moving average of Close*Volume is greater than $5,000,000
  • Close is greater than $1
  • Two period RSI is less than 7.5
  • Has closed down 4 days in a row
  • Close is above the 200 day moving average
  • The 100 day Historical Volatility is greater than 40

Buy Rules

 

  • Rank stocks from Highest 100 day Historical Volatility to lowest
  • Buy the top 10 stocks
  • Buy on the next open

 

Sell Rules

 

  • Two period RSI closes above 30
  • Sell on the next open

 

Base Results

141103a

Not a strategy that most people would trade but this gives us something to work with.

Randomizing trade order (Method 0)

This method uses all the trades that the strategy took but then randomly selects the order they happened in. This method is best employed when trading a single symbol. The issue with doing this when trading a portfolio of symbols is that trades are often correlated together. For example a mean reversion strategy during a market has a sell off. Typically most of trades are losers during this time. Monte Carlo analysis does not account for this behavior. Doing this type of testing in AmiBroker is not straightforward.

Adding noise to the rules (Method 1)

For method 2, one varies the rule values by a small percentage. Take the RSI(2) < 7.5 rule. What we do is vary 7.5 by a little. Instead of 7.5, we use 6.82 or 9.84. One would set the code to vary the rule value by a percentage. Using this method, the strategy generates a different set of trades each time. One advantage of this method over the previous one is that if the strategy did well because of a handful of trades, this analysis would potential discover that.

The randomness to the rule values can be added three ways. Each case adds more randomness. We will use the RSI(2) < 7.5 as an example below.

  1. The random value for RSI would be the same for every stock on every day.
  2. The random value for RSI would be different for every stock but the same on every day
  3. The random value for RSI would be different for every stock and every day.

AmiBroker Code to add randomness for item 3 above follows, which is also the easiest to do in AmiBroker.

 

// returns a random array between 1*(1-percent) and 1*(1+percent)
// For example a percent=10 returns random numbers between .9 and 1.1

function RandPercent(percent)
{
     perc = percent/100;
     rand = 1+(perc - Random() * (perc*2));

     return rand;
}
minRSI = 7.5*RandPercent(20);
minHV = 40*RandPercent(10);
exitRSI = 30*RandPercent(5);

 

The above code would set a 20% range for the RSI(2) < 7.5 rule. The RSI values would be between 6 and 9. The range is up to the user. Here, 20% seemed to give a good range of values. For the historical volatility we vary the value by 10%, which is range from 36 to 40. For the exit RSI we by 5%, a little small but I wanted to show different values. The range would be from 28.5 to 31.5.

 

Method 1 Results

141103b

Note that the average and median values for the CAR of 12.7 are below the base results of 14.3. One would like to see this much closer together. The Monte Carlo simulation tells us to expect about a 11% less return. Even the worst run for CAR and MDD would be within what I would consider acceptable range. Not that I could trade a strategy with a 45% drawdown which few people can.

Adding noise to stock data (Method 2)

This is the method that I had not thought about before and had a head slapping moment on how simple it is. We apply the noise to the stock data values. For this example, we will apply the noise to the closing prices. Closing prices are randomly adjusted by a small percentage. The closing price need only vary by a little bit to have an impact in the results. Using a value of .25%, means that the closing price each day can be between .25% lower to .25% higher. A quarter percent is very little but for rules like RSI2 and closing down days it can have a significant impact.

This method also generates a different trade list every time. Since we are not applying the randomness to the Opening price, the return of a stock is not affected.

AmiBroker Code to add randomness for this method.

// returns a random array between 1*(1-percent) and 1*(1+percent)
// For example a percent=10 returns random numbers between .9 and 1.1
function RandPercent(percent)
{
     perc = percent/100;
     rand = 1+(perc - Random() * (perc*2));
     return rand;
}
Close = Close * RandPercent(.25); // varying the close price by .25%

 

Method 2 Results

141103c

As one can see, these numbers differ from Method 1. The CAR of 13.9 is much closer to the base CAR of 14.3. The standard deviation for CAR is much higher but that is because one has higher return simulations.

Spreadsheet & Code

Fill the form in below for the spreadsheet of the results, yearly breakdown and the AmiBroker code used to generate them. The code assumes that Norgate Data is your data provider and that you have the Platinum level.

Final Thoughts

Which method should one use? It is up to you and which method makes the more sense to you. Both methods give similar results, which is good to see. Method 2 is my preference because it is easier to implement and I like that it is changing the closing price. If my rules used the low (or open or high) price, then I would add randomness to the low price.

I do not always run Monte Carlo analysis on my strategies as formally defined. I use a method very similar to “adding noise to the rules.” But now that I have purchased a new blazingly fast computer, I will do more Monte Carlos analysis in the future using method 2.

Backtesting platform used: AmiBroker. Data provider:Norgate Data (referral link)

Good quant trading,

 

 

 

Visited 47 times, 1 visit(s) today

Click Here to Leave a Comment Below

The Whole Street’s Daily Wrap for 11/3/2014 | The Whole Street - November 4, 2014 Reply

[…] Monte Carlo Analysis in AmiBroker [Alvarez Quant Trading] This post will cover in detail two different ways of doing Monte Carlo analysis and the code needed to it in AmiBroker. A reader recently sent me this article, Monte Carlo Analysis For Trading Systems. The arti… […]

Shawn - November 5, 2014 Reply

Hi Cesar

Awesome post! Very timely too, as I’ve been doing a lot of researching into MC stuff recently trying to get my head around it all. Particularly ways to run simulations on portfolio level systems where Method 0 due to serial correlation isnt appropriate.

Some questions: I’m a little unsure why you choose close prices in Method 2. Is it because there are a few parameters in the system that will be affected by the close price (such as the RSI exit, rule for closing down 4 days in row, etc), which will add randomness to the system each time a simulation is run?

Why not use the open (I dont really follow how the return of a stock is not affected)?

In your example, you use 0.25%, do you have a recommended distribution to use in general? Say from 0.1-2%. Or is this number going to be determined by the type of system? As you say, RSI(2) is going to be affected by a small number like 0.25%, but RSI(50) might not.

I notice you use Random in your code. It probably makes little difference, but I suppose one could substitute Random with mtRandom, right?

Anyway, thanks again for the awesome & insightful blog posts! Always learning.

Shawn

    Cesar Alvarez - November 5, 2014 Reply

    The reason I chose to do only the close price is because RSI, down days, moving average all affected by changes in the close. I could have done all prices, Open, High, Low, Close.

    The problem by changing the Open price is since the rules enter on the open, the returns will be changed. For example, say a stock enters on the Open @ $100 and exits @ $110 for a 10% gain. No we are randomizing the Open price by up to 2% (an absurdly high number but it helps make my point). After the random process we have entry of $101 and exit of $108, which gives a return of 6.9%.

    Yes you could use mtRandom.

    Cesar

      Shawn - November 5, 2014 Reply

      Hi Cesar

      Thanks for the reply.

      I’m still not sure about the change of returns thing. If the close price is changed, but not the open, there will still be a change of returns. If the open stays at $100, and the exit price is $108, the return will be 8%. The original was 10%. Is it just a matter of the size of the difference? That is, only changing the exit will still allow the monte carlo simulation to be valid, whereas changing both the open and close will cause it to be invalid because the difference in change of returns is too large?

      Thanks

      Shawn

        Cesar Alvarez - November 6, 2014 Reply

        Shawn,

        Sorry but I misread your comment “Why not use the open (I dont really follow how the return of a stock is not affected)? ”

        Let me try this again.

        The strategy enters and exits on the open, so changing closing prices has no effect on the return because return is based on the open. If you add code to randomize the open price then the returns will change.

        If I am still not being clear, you can use my contact page for us to set up time to talk about this.

        Cesar

          Shawn - November 6, 2014 Reply

          Ah! /facepalm

          Sorry, my bad! You even state the system buys and sells on open. I was thinking it exited on the close.

          Sorry for the stupidity.

          Thanks

          Shawn

          Cesar Alvarez - November 6, 2014 Reply

          No problems. We both mis-read. At least it is all clear now.

Shawn - November 16, 2014 Reply

Hi again Cesar

A while back a user on the Amibroker Yahoo User’s list posted some code for MC within Amibroker itself. I havent looked at it in detail myself, but you might find it interesting. Maybe it can be used to help with one of the methods. I’ll copy and paste it below. If that doesnt work, I’ll email you the afl file.

EnableTextOutput( 0 );
SetBarsRequired ( sbrAll, sbrAll );

bi = BarIndex();
end = endValue ( bi );
begin = BeginValue( bi );
projection = Param ( “project forward”, 10 , 5, 100, 1 ); ;
simulations = Param ( “simulations”, 300 , 10, 450, 10 ); ;

//shift = 10 ;
Plot( c, “”, colorWhite, styleBar, Null, Null, 0, 0, 3 );

j = 0;
ReturnRate = Null;
begin = max ( 1, begin );
average = 0;
for ( i = begin; i <= end; i++ )
{
ReturnRate [j] = Close [i] / Close [i-1] ;
average+=ReturnRate [j];
j++;
}
average /= j;

for ( n = 0; n < simulations; n ++ )
{

for ( i = 0; i < j ; i ++ )
{
r = mtRandom() * j;
temp = returnRate [i] ;
returnrate [i] = returnRate [r] ;
returnrate [r] = temp;

}

// Plot ( returnrate , "shouffle", colorRed );

k = 0;
value = endValue ( Close );

price = Null;
for ( i = 0; i <= projection ; i ++ )
{
price[k] = value;
k++ ;
value *= returnrate [i] ;

}

price = Ref ( price, – end );
Plot ( price , "", colorRed );

VarSet ( "endvalue" + n, value );
}

mean = 0;

for ( i = 0 ; i < n ; i ++ )
{
value = VarGet ( "endvalue" + i );
mean += value;
}

mean /= n;

sumSquares = 0;

for ( i = 0 ; i < n ; i ++ )
{
value = VarGet ( "endvalue" + i );
variance = ( value – mean ) ^ 2;
sumSquares += variance;
}

sigma = sqrt ( sumsquares / n );

projected = mean += sigma;
finalreturn = projected/ c;

Title = "final values:" +
"\nmean = " + mean +
"\nstddev = " + sigma +
// "\nHistorical avg = " + average+
"\n";

ramzam - May 20, 2016 Reply

hi i came to know this mcs method in afl.. can you please provide full afl here?
thanking you

    Cesar Alvarez - May 20, 2016 Reply

    Sorry but I don’t provide the code. I do AmiBroker consulting and you can hire to produce code that you need.

Leave a Reply: