Good morning, Tomasz. Thank you for your quick response.
Yes, that’s the conclusion I’ve reached as well: the positionsize
variable doesn’t calculate correctly when delays are applied.
However, the Explorer shows no difference in the variable with or without the delay, so I’m not sure why the delay affects the position size. This is the first time I’ve encountered this behavior in my systems.
I’ve attached the code I got from the forum.
/* GTAA - Global Tactical Asset Allocation with Out-Of-Market universe
**
** from:
** A Quantitative Approach to Tactical Asset Allocation
** by Mebane T. Faber 2007, 2013
**
** source: http://papers.ssrn.com/sol3/papers.cfm?abstract_id=962461
**
** AmiBroker implementation by TrendXplorer
** www.trendxplorer.info
** trendxplorer@gmail.com
**
** latest version: March 5, 2017
**
** NB! Designed for monthly data
*/
// --- begin of code ---
PosScore=0;
// --- inputs ---
wln_bonds = Param( "WatchListNumber for Bonds:" , 95, 0, 150, 1 );
wln_stocks = Param( "WatchListNumber for Stocks:", 96, 0, 150, 1 );
lookback = Param( "MA lookback (m):", 10, 1, 24, 1 );
// --- detect tradelist ---
wlnumber = GetOption( "FilterIncludeWatchlist" );
watchlist = GetCategorySymbols( categoryWatchlist, wlnumber );
// --- detect stocks universe ---
stockslist = GetCategorySymbols( categoryWatchlist, wln_stocks );
NumberOfStocks = StrCount( stockslist, "," ) + 1;
NoS = NumberOfStocks;
// --- detect bonds universe ---
bondslist = GetCategorySymbols( categoryWatchlist, wln_bonds );
NumberOfBonds = StrCount( bondslist, "," ) + 1;
NoB = NumberOfBonds;
// --- top selection ---
PqS = Param( "Number of Stock Positions:", 3, 1, NoS, 1 );
PqB = Param( "Number of Bond Positions:" , 1, 1, NoB, 1 );
// --- inputs for return mix ---
w1m = ParamToggle( "Include 1 month return?", "No|Yes", 1 );
w3m = ParamToggle( "Include 3 month return?", "No|Yes", 1 );
w6m = ParamToggle( "Include 6 month return?", "No|Yes", 1 );
w12m = ParamToggle( "Include 12 month return?", "No|Yes", 1 );
// --- backtester settings ---
SetOption( "CommissionAmount", 0.00 );
SetOption( "InitialEquity", 100000 );
SetTradeDelays( 0, 0, 0, 0 );
SetOption( "MaxOpenLong", NoS );
SetOption( "MaxOpenPositions", NoS );
SetOption( "AllowPositionShrinking", True );
SetOption( "AllowSameBarExit", True );
SetOption( "ReverseSignalForcesExit", False );
SetOption( "HoldMinBars", 1 );
SetBacktestMode( backtestRegular );
SetOption("ExtraColumnsLocation", 1 );
RoundLotSize = 0.0001;
// --- init values ---
cashCount = cashScore = 0;
// --- asset selection and capital allocation routine ---
// based on https://groups.yahoo.com/neo/groups/amibroker/conversations/topics/178791
if ( Status( "stocknum" ) == 0 )
{
StaticVarRemove( "SMA*" );
StaticVarRemove( "avg*" );
StaticVarRemove( "Pos*" );
StaticVarRemove( "Rank*" );
StaticVarRemove( "cash*" );
// --- stocks universe --- //
for ( i = 0; ( symbol = StrExtract( stockslist, i ) ) != ""; i++ )
{
SetForeign( symbol );
// --- load quotes ---
Data = Close;
// --- calculate SMA and trend filter ---
SMA = Sum( Data, lookback ) / lookback;
SMAfilter = Data > SMA;
// --- 1m, 3m, 6m and 12m and avg total return ---
stocksRet1m = Nz( -1 + Data / Ref( Data, -1 ) );
stocksRet3m = Nz( -1 + Data / Ref( Data, -3 ) );
stocksRet6m = Nz( -1 + Data / Ref( Data, -6 ) );
stocksRet12m = Nz( -1 + Data / Ref( Data, -12 ) );
avgStocksRet = ( w1m * stocksRet1m + w3m * stocksRet3m + w6m * stocksRet6m + w12m * stocksRet12m ) / ( w1m + w3m + w6m + w12m );
RestorePriceArrays();
// --- store values ---
StaticVarSet( "SMAfilter" + symbol, SMAfilter );
StaticVarSet( "avgStocksRet" + symbol, avgStocksRet );
}
// --- generate ranks for stocks ---
StaticVarGenerateRanks( "Rank_", "avgStocksRet", 0, 1224 );
for ( i = 0; ( symbol = StrExtract( stockslist, i ) ) != ""; i++ )
{
// --- retrieve stored values ---
Rank_avgStocksRet = StaticVarGet( "Rank_avgStocksRet" + symbol );
SMAfilter = StaticVarGet( "SMAfilter" + symbol );
// --- count number of top ranked symbols below SMA filter ---
cashCount = IIf( Rank_avgStocksRet > PqS, cashCount, IIf( SMAfilter, cashCount, cashCount + 1 ) );
// --- allocate capital to top ranked symbols above SMA filter ---
PosScore = IIf( Rank_avgStocksRet <= PqS AND SMAfilter, Nz( 100 / PqS ), 0 );
// --- store values ---
StaticVarSet( "PosScore" + symbol, PosScore );
}
// --- calculate allocation for out-of-market "cash" fund ---
PosCash = cashCount * 100 / PqS;
// --- store value ---
StaticVarSet( "PosCash" , PosCash );
// --- bonds universe --- //
for ( i = 0; ( symbol = StrExtract( bondslist, i ) ) != ""; i++ )
{
SetForeign ( symbol );
// --- load quotes ---
Data = Close;
// --- 1m, 3m, 6m and 12m and avg total return ---
bondsRet1m = Nz( -1 + Data / Ref( Data, -1 ) );
bondsRet3m = Nz( -1 + Data / Ref( Data, -3 ) );
bondsRet6m = Nz( -1 + Data / Ref( Data, -6 ) );
bondsRet12m = Nz( -1 + Data / Ref( Data, -12 ) );
avgBondsRet = ( w1m * bondsRet1m + w3m * bondsRet3m + w6m * bondsRet6m + w12m * bondsRet12m ) / ( w1m + w3m + w6m + w12m );
RestorePriceArrays();
// --- store values ---
StaticVarSet( "avgBondsRet" + symbol, avgBondsRet );
}
// --- generate ranks for bonds ---
StaticVarGenerateRanks( "Rank_", "avgBondsRet", 0, 1224 );
for ( i = 0; ( symbol = StrExtract( bondslist, i ) ) != ""; i++ )
{
// --- retrieve values ---
Rank_avgBondsRet = StaticVarGet( "Rank_avgBondsRet" + symbol );
PosCash = StaticVarGet( "PosCash" );
// --- check if bond symbol is part of stockslist too ---
_PosScore = StaticVarGet( "PosScore" + symbol );
PosScore = IIf( Rank_avgBondsRet <= PqB, Nz( PosCash / PqB ), 0 );
PosScore = IIf( !IsNull( _PosScore ), PosScore + _PosScore, PosScore );
// --- store values ---
StaticVarSet( "PosScore" + symbol, PosScore );
}
}
// --- retrieve values ---
symbol = Name();
if( StrFind( stockslist, Name() ) )
{
avgReturn = StaticVarGet( "avgStocksRet" + symbol ) * 100;
PosScore = StaticVarGet( "PosScore" + symbol );
Rank = StaticVarGet( "Rank_avgStocksRet" + symbol );
}
if( StrFind( bondslist, Name() ) )
{
avgReturn = StaticVarGet( "avgBondsRet" + symbol ) * 100;
PosScore = StaticVarGet( "PosScore" + symbol );
Rank = StaticVarGet( "Rank_avgBondsRet" + symbol ) + 100;
}
// --- set position sizes ---
PositionSize = -PosScore; // "-" sign for spsPercentOfEquity
// --- re-balance at the end/close of every month ---
Buy = PosScore > 0;
Sell = 0;
Short = Cover = 0;
BuyPrice = Close;
SellPrice = Close;
// --- exit at the end of the month for rebalancing ---
ApplyStop( stopTypeNBar, stopModeBars, numbars = 1, exitatstop = 0 );
// --- exploration filter ---
ExploreFilter = ParamToggle( "ExploreFilter", "LastBarInTest|All", 1 );
if ( ExploreFilter )
Filter = 1;
else
Filter = Status( "LastBarInTest" );
// --- sort for exploration only (not on backtest) ---
if ( Status( "actionex" ) == actionExplore )
{
SetSortColumns( 2, -5, 4 );
// --- columns for exploration ---
ColorRet = IIf( avgReturn > 0, colorBrightGreen, colorRed );
if( StrFind( stockslist, Name() ) ) PosColor = IIf( PosScore > 0, colorGold , colorWhite );
if( StrFind( bondslist , Name() ) ) PosColor = IIf( PosScore > 0, colorYellow, colorWhite );
AddColumn( avgReturn, "Performance (%)", 3.3, 1, ColorRet );
AddColumn( Rank , "Rank" , 1.0 );
AddColumn( PosScore , "PosScore (%)" , 3.3, 1, PosColor );
}
// --- end of code ---
To replicate the issue I’m describing, just modify line 52 to:
SetTradeDelays( 0, 0, 0, 0 );
And lines 195 and 196 to:
BuyPrice = Open;
SellPrice = Open;
Thanks for your help