Traders' Tip text
With Mr. Katsanos’ generous assistance, we coded the Aussie strategy to the article’s specifications in WealthScript. During the process, we discovered that the secondary symbol data had missing bars with respect to the primary symbol, AUDUSD. Wealth-Lab gives you the ability to control synchronization such that indicators based on secondary symbols should generally be created using the raw data before synchronizing the resulting data series with the chart. The code reflects this delayed synch approach. Separately, it’s important to note that indicators such as MACD and EMA require a fair amount of seed data (more than 100 bars for MACD) to stabilize their calculation before they can be used reliably for backtesting or trading. Even though the strategy ignores the first 100 bars, the initial trades can still be sensitive to the length of the seed data.
Figure 1. The strategy presciently reversed near the top and held the Aussie short from July - Dec 2008.
Download the attachment for the ASCII Data used for the article and the Strategy below. (Attachments are available only to registered Wealth-Lab Pro/Developer customers). If using your own data, be sure to make the secondary symbol changes in the code as required.
Strategy Code
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
public class TradingTheAussie : WealthScript
{
private DataSeries SecBol( DataSeries ds, int period )
{
DataSeries sd = 2 * StdDev.Series(ds, period, StdDevCalculation.Population);
return 1 + ( ( ds - SMA.Series(ds, period) + sd ) / ( sd + sd + 0.0001 ) );
}
protected override void Execute()
{
string sampleSym = "YBA"; // out-of-sample; use "YTC" for in-sample
const double pct = 0.007;
int per = 30;
bool synch = false;
DataSeries xau = GetExternalSymbol("XAU", synch).Close; //sec2
DataSeries crb = GetExternalSymbol("CRB", synch).Close; //sec3
DataSeries yba = 100 - GetExternalSymbol(sampleSym, synch).Close; //sec5
DataSeries ej = GetExternalSymbol("EURJPY", synch).Close; //sec6
// initialize the initial values to account that yba data starts at 4/23/2008
int stBar = DateTimeToBar(new DateTime(2003, 4, 23), false);
for(int bar = 0; bar < stBar; bar++)
yba[bar] = yba[stBar];
// Based calculations from raw data and then synchronize
DataSeries ybaSMA = Synchronize( SMA.Series(yba, 40) );
DataSeries xauROC = Synchronize( ROC.Series(xau, 1) );
DataSeries crbROC = Synchronize( ROC.Series(crb, 1) );
DataSeries ejROC = Synchronize( ROC.Series(ej, 2) );
ej = Synchronize( ej );
yba = Synchronize( yba );
DataSeries sec1BOL = SecBol( Close, per );
DataSeries sec2BOL = Synchronize( SecBol( xau, per ) );
DataSeries sec3BOL = Synchronize( SecBol( crb, per ) );
DataSeries DIV2 = 100 * (sec2BOL - sec1BOL)/sec1BOL;
DataSeries DIV3 = 100 * (sec3BOL - sec1BOL)/sec1BOL;
DataSeries DIV1 = DIV2 * 1d;
DataSeries DIV1m = DIV3 * 1d;
DIV1.Description = "DIV1(max)";
DIV1m.Description = "DIV1(min)";
for(int bar = 0; bar < Bars.Count; bar++) {
DIV1[bar] = Math.Max( DIV3[bar], DIV2[bar] );
DIV1m[bar] = Math.Min( DIV3[bar], DIV2[bar] );
}
DataSeries macd = MACD.Series(Close);
DataSeries ema = EMA.Series(macd, 9, EMACalculation.Modern);
DataSeries HH4 = Highest.Series(High, 4);
DataSeries LL4 = Lowest.Series(Low, 4);
/* Plotting */
ChartPane macdPane = CreatePane( 40, true, true );
ChartPane divPane = CreatePane( 40, true, true );
ChartPane yldPane = CreatePane( 40, true, true );
PlotSeries(yldPane, yba, Color.Fuchsia, LineStyle.Solid, 1);
PlotSeries(yldPane, ybaSMA, Color.Black, LineStyle.Solid, 1);
PlotSeries(macdPane, macd - ema, Color.Black, LineStyle.Histogram, 1);
PlotSeries(macdPane, macd, Color.Red, LineStyle.Solid, 1);
PlotSeries(macdPane, ema, Color.Green, LineStyle.Solid, 1);
PlotSeries(divPane, DIV1, Color.Red, LineStyle.Solid, 1);
PlotSeries(divPane, DIV1m, Color.Gray, LineStyle.Solid, 1);
PlotSeries(PricePane, SMA.Series(Close, 50), Color.Blue, LineStyle.Solid, 1);
PlotSeries(PricePane, SMA.Series(Close, 15), Color.Black, LineStyle.Solid, 1);
HideVolume();
for(int bar = 100; bar < Bars.Count; bar++)
{
double C = Close[bar];
double C1 = Close[bar - 1];
// Buy conditions - broken down to verify which is the triggering condition
bool buy1 = Highest.Series(DIV1, 3)[bar] > 10
&& DIV1[bar] < DIV1[bar - 1]
&& ROC.Series(Close, 2)[bar] > 0
&& C > (1 + pct) * LL4[bar]
&& ybaSMA[bar] > ybaSMA[bar - 1];
bool buy2 = DIV1[bar] > 40
&& DIV2[bar] + DIV3[bar] > 80
&& DIV1[bar] < DIV1[bar - 1]
&& C > C1
&& C > (1 + pct) * LL4[bar];
bool buy3 = CrossOver(bar, SMA.Series(Close, 15), SMA.Series(Close, 50))
&& ybaSMA[bar] > ybaSMA[bar - 1];
// Short Conditions
bool sht1 = Lowest.Series(DIV1, 3)[bar] < -10
&& DIV1[bar] > DIV1[bar - 1]
&& ROC.Series(Close, 2)[bar] < 0
&& C < (1 - pct) * HH4[bar]
&& ybaSMA[bar] < ybaSMA[bar - 1];
bool sht2 = DIV1[bar] < -20
&& DIV2[bar] + DIV3[bar] < -40
&& DIV1[bar] > DIV1[bar - 1]
&& C < C1
&& C < (1 - pct) * HH4[bar];
bool sht3 = CrossOver(bar, SMA.Series(Close, 50), SMA.Series(Close, 15))
&& ybaSMA[bar] < ybaSMA[bar - 1];
// Sell Conditions
bool s1 = CrossOver(bar, ema, macd)
&& Highest.Series(macd, 5)[bar] > Highest.Series(macd, 50)[bar - 5];
bool s2 = DIV1m[bar] < -30
&& ( xauROC[bar] < -0.5 || crbROC[bar] < -0.5 )
&& C < (1 - pct) * HH4[bar];
bool s3 = DIV1m[bar] < 0
&& ejROC[bar] < -1
&& C < (1 - pct) * HH4[bar]
&& SMA.Series(ej, 40)[bar] < SMA.Series(ej, 40)[bar - 1];
// Cover Conditions
bool c1 = CrossOver(bar, macd, ema)
&& Lowest.Series(macd, 5)[bar] < Lowest.Series(macd, 50)[bar - 5];
bool c2 = DIV1[bar] > 30
&& ( xauROC[bar] > 0.5 || crbROC[bar] > 0.5 )
&& C > (1 + pct) * HH4[bar]; // should it be LL4 as in Buy?
if( IsLastPositionActive )
{
Position p = LastPosition;
if( p.PositionType == PositionType.Long )
{
if ( sht1 || sht2 || sht3 )
{
SellAtClose(bar, p, "R:" + sht1 + "," + sht2 + "," + sht3 );
ShortAtClose(bar, "reverse");
}
else if ( s1 || s2 || s3 )
SellAtClose(bar, p, "X:" + s1 + "," + s2 + "," + s3);
}
else if ( buy1 || buy2 || buy3 )
{
CoverAtClose(bar, p, "R:" + buy1 + "," + buy2 + "," + buy3 );
BuyAtClose(bar, "reverse");
}
else if ( c1 || c2 )
CoverAtClose(bar, p, "X:" + c1 + "," + c2 );
}
else if( buy1 || buy2 || buy3 )
BuyAtClose(bar, buy1 + "," + buy2 + "," + buy3 );
else if ( sht1 || sht2 || sht3 )
ShortAtClose(bar, sht1 + "," + sht2 + "," + sht3);
}
}
}
}