Revision A!
After reading
my input for the Traders' Tip based on his article, Mr. Katsanos contacted me about the details of the Chandelier stop exit. When he added it to
his strategy, he found its effect very negative, not positive. After several email follow ups, I revised the WealthScript code, which had both logical and interpretation errors based on the article text and my unfamiliarity of EL code.
The following WealthScript code revision accurately represents Mr. Katsanos' strategy presented in the article. The results really are outstanding. My apologies to Mr. Katsanos and the SAC readers for not getting it right the first time.
Robert Sucher
Wealth-Lab.com
Download
Soybean.zip (Soybean Strategy Data) by clicking on "Attachments" on top of the page and unzip to an ASCII data directory and create its DataSet. You can find its zipped source code by clicking on "Attachments" on top of the page.
To download attachments, you need to log in to the Wiki with a SEPARATE Wiki account authorized by Wealth-Lab staff for this purpose! Logging in on the wealth-lab.com site is NOT enough.Wealth-Lab customers who can't see "Attachments" - please visit Support section at
wealth-lab.com. Run the script using All Data on futures symbol ZS (Soybeans) after entering the following specs in the Symbol Info Manager:
Futures Mode: enabled
Symbol: ZS; Type: Future; Margin: 4000; Point Value: 50; Tick: 0.25; Decimals: 2
WealthScript Code (C#)
/* Seasonal Soybean Strategy per Katsanos article EL code */
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
public class MyStrategy : WealthScript
{
StrategyParameter D1DXY;
StrategyParameter D2SNL;
StrategyParameter LRSDXYSELL;
StrategyParameter LRSNLSELL;
StrategyParameter MASELL;
StrategyParameter MASNL;
StrategyParameter MADXY;
StrategyParameter chnlLength;
public MyStrategy()
{
D1DXY = CreateParameter("DX LRSlope Period", 25, 10, 50, 1);
D2SNL = CreateParameter("Snl LRSlope Period", 12, 5, 25, 1);
LRSDXYSELL = CreateParameter("DX LRSlope", 0.3, 0.1, 0.8, 0.1);
LRSNLSELL = CreateParameter("Snl LRSlope", -0.8, -1.2, -0.5, 0.1 );
MASELL = CreateParameter("Soybean EMA Period", 15, 5, 25, 1);
MASNL = CreateParameter("Snl EMA Period", 15, 5, 25, 1);
MADXY = CreateParameter("DX MA Period", 50, 20, 100, 5);
chnlLength = CreateParameter("Channel Period", 4, 2, 20, 2);
}
// ROC of LR Line divided by its period
private double LRLineROCdivPeriod(int bar, DataSeries ds, int period)
{
int n = bar - period + 1;
double y1 = LinearRegLine(ds, n, bar, bar);
double y2 = LinearRegLine(ds, n, bar, n);
double per = period;
return 100 * ( y1 / y2 - 1d ) / per;
}
protected override void Execute()
{
HideVolume();
int buyMonth = 9; int sellMonth = 6;
// Access external data
Bars dxy = GetExternalSymbol("DXY", true);
Bars snl = GetExternalSymbol("SNL", true);
// Create and plot indicators
DataSeries channelHiPlus2 = (Highest.Series(Close >> 1, chnlLength.ValueInt) + 2d);
DataSeries channelLoMinus2 = (Lowest.Series(Close >> 1, chnlLength.ValueInt) - 2d);
PlotSeries(PricePane, channelHiPlus2, Color.Blue, LineStyle.Dashed, 1);
PlotSeries(PricePane, channelLoMinus2, Color.Brown, LineStyle.Dashed, 1);
DataSeries rs = EMA.Series(Close/snl.Close, 3, EMACalculation.Modern);
DataSeries LRSNL = new DataSeries(Bars, "Seasonal Avg Change(" + D2SNL.ValueInt + ")");
for (int bar = D2SNL.ValueInt; bar < Bars.Count; bar++)
LRSNL[bar] = LRLineROCdivPeriod(bar, rs, D2SNL.ValueInt);
DataSeries LRSDXY = new DataSeries(Bars, "LRSDXY");
for (int bar = D1DXY.ValueInt; bar < Bars.Count; bar++)
LRSDXY[bar] = LRLineROCdivPeriod(bar, dxy.Close, D1DXY.ValueInt);
ChartPane lrPane = CreatePane(40, true, true);
PlotSeries(lrPane, LRSNL, Color.Green, LineStyle.Solid, 2);
PlotSeries(lrPane, LRSDXY, Color.Black, LineStyle.Solid, 2);
DataSeries ema = EMA.Series(Close, MASELL.ValueInt, EMACalculation.Modern);
PlotSeries(PricePane, ema, Color.Green, LineStyle.Solid, 1);
DataSeries emaDXY = EMA.Series(dxy.Close, MADXY.ValueInt, EMACalculation.Modern);
DataSeries emaSNL = EMA.Series(snl.Close, MASNL.ValueInt, EMACalculation.Modern);
// Trading Strategy logic
for(int bar = 2 * GetTradingLoopStartBar(50); bar < Bars.Count; bar++)
{
int month = Date[bar].Month;
bool seasonalBuy = month < sellMonth || month > buyMonth;
bool seasonalShort = month > sellMonth && month < buyMonth;
bool buy = seasonalBuy
&& LRSDXY[bar] < LRSDXYSELL.Value
&& Close[bar] > channelHiPlus2[bar]
&& LRSNL[bar] > LRSNLSELL.Value;
bool sell = seasonalShort
&& Close[bar] < channelLoMinus2[bar]
&& LRSDXY[bar] > -LRSDXYSELL.Value
&& LRSNL[bar] < -LRSNLSELL.Value;
if (!IsLastPositionActive)
{
if (buy)
BuyAtMarket(bar + 1);
else if (sell)
ShortAtMarket(bar + 1);
}
else
{
Position p = LastPosition;
if ( bar - p.EntryBar < 1 )
continue;
else if (p.PositionType == PositionType.Long)
{
if (sell)
{
ExitAtMarket(bar + 1, p, "Reverse");
ShortAtMarket(bar + 1);
}
else if( Close[bar] < ema[bar] )
{
if ( LRSNL[bar] < LRSNLSELL.Value )
ExitAtMarket(bar + 1, p, "L_exit RS");
else if( emaDXY[bar] > emaDXY[bar - 1] && LRSDXY[bar] > LRSDXYSELL.Value )
ExitAtMarket(bar + 1, p, "L_exit DXY");
}
}
else // Position is short
{
if (buy)
{
ExitAtMarket(bar + 1, p, "Reverse");
BuyAtMarket(bar + 1);
}
else if( Close[bar] > ema[bar] )
{
if ( LRSNL[bar] > -LRSNLSELL.Value )
ExitAtMarket(bar + 1, p, "S_exit RS: " + LRSNL[bar].ToString("0.000###"));
else if( emaDXY[bar] < emaDXY[bar - 1] && snl.Close[bar] > emaSNL[bar] )
ExitAtMarket(bar + 1, p, "S_exit DXY");
}
}
}
}
}
}
}