Sei sulla pagina 1di 7

// --- SUMMARY --- //

// 0. Declare Functions & Variables


// 1. General Backtest Settings
// 2. Cash Position Logic
// 3. Custom Backtest Logic
// 3.1 Call Backtest Object
// 3.2 Preprocess ()
// 3.3 Process Trade Signals per Bar & GoInCash Logic
// 3.4 Postprocess ()
// 3.5 Create Additional Metrics per Trade
// 3.6 List all trades to Analyzer window
// 3.7 Create Additional Metrics for Portfolio
// 3.8 Send Metrics Output to Composite Ticker & File
//
// note: each System.Afl should contain following statements as first rows in the
code.
// SystemName="sD_ETF_TSIRSI";
// _Cashticker ="IEF"; _Cashticker2 = "VIX";
_CashPercentage=0;
// _MaxScaleIn=2; _RiskFreeRateSharpe=0.02;
// SetOption( "Interestrate",2.4) ;
// #include<OutputStatistics.afl>

// --- 0. DECLARE FUNCTIONS & VARIABLES--- //


ExportPath="C:\\Cockpit\\Export\\";
function FindValueAtDateTime( input, dt, Value )
{
found = -1;
for( i = 0; i < BarCount AND found == -1; i++ )
{
if( dt[ i ] == Value ) found = i;
}
return IIf( found != -1, input[ found ], Null );
}

// --- 1. GENERAL SETTINGS --- //


SetOption( "UseCustomBacktestProc", True ); SetCustomBacktestProc("");

// --- 2. DEFINE CASH/INTEREST LOGIC --- //


eom=Month()!=Ref(Month(),-1);
if (VarGet("_Cashpercentage")>0) { SetOption( "InterestRate", 0 );}
// Disable Interestrate if % of cash will be
used for Go-In-Cash solution
Cashticker = WriteIf( VarGetText( "_Cashticker" ) == "", "SHY",
VarGetText( "_Cashticker" )); // Ticker used to simulate "cash" position, use
IEF as default
Cashticker2= VarGetText( "_Cashticker2" );
// Additional Ticker, used in
formula to simulate Go-in-Cash constraints
Cashdata= 1000*Foreign( Cashticker, "C" ); C2=Foreign(Cashticker2,"C");
// Get Close data for both tickers
CURRENCY= Foreign("eurusd","C");
CashO= Foreign( Cashticker, "O" );
CashC= Foreign( Cashticker, "C" );
EOM=Month()!=Ref(Month(),-1);
//ConditionGoInCash= RSIa(Cashdata,3)<55 AND RSIa(C2,2)<80
// AND (Ref(eom,-1)==False AND eom==False AND Ref(eom,1)==False) ;//
ConditionGoInCash=True;
if (Interval()> 86400){ConditionGoInCash=True;}
// Go-In-Cash Condition
GoInCash=Ref(ConditionGoInCash,-1);

InterestFactor = IIf(Nz(GoInCash) ,Nz(Ref(ROC(Cashdata,1),0))/100,0);


// Create daily gain-array for Days that
system is in Cash. Nz() handles non-existant data points.
txcost = IIf( (Nz(GoInCash) AND Ref(Nz(GoInCash)==False,-1) AND
VarGet("_Cashpercentage")>0) // Create tx cost-array for Days that system enters
in and exits Cash
OR (Nz(GoInCash) AND Ref(Nz(GoInCash)==False,1)),1,0);

// --- 3. CUSTOM BACKTEST LOGIC --- //


if (Status("action") == actionPortfolio)
{

// --- 3.1 CALL BACKTEST OBJECT --- //


bo = GetBacktesterObject();

// --- 3.2 PREPROCESS & CBT LOGIC ---//


bo.PreProcess( );

// --- 3.3 PROCESS TRADE SIGNALS PER BAR & GO-IN-CASH LOGIC --- //
dt = DateTime();
//MaxPortHeat=1;
//if (VarGet("_MaxPortHeat")>0){MaxPortHeat=VarGet("_MaxPortHeat");}

for ( bar = 0; bar < BarCount; bar++ )


{
// Simulate Go-In-Cash
prevcash = bo.cash*VarGet("_Cashpercentage");
// Get the invested cash from previous day.
_Cashpercentage is set in System Logic as % to go in cash.
nrofcashshares = int(prevcash/Cashdata[bar]);
// Calculate nr of shares to invest in "Cash" position.
txfee = Min(Max(0.005*nrofcashshares,1),0.005*prevcash);
// Calculate tx cost to invest in "Cash"position. Use Interactive
brokers fee structure.
interest = (prevcash * InterestFactor[ bar ]) -
txcost[bar]*txfee; // Calculate interest - tx costs
bo.cash = bo.cash + interest;
// Add to current cash before processing trades
Openrisk=0;

// Loop through signals per bar. Here you can change signals.

for( sig = bo.GetFirstSignal( bar ); sig; sig = bo.GetNextSignal( bar )


)
{

// if (Heat>5)
// {
// sig.PosSize = 0; // Long: no-scale in if signal
price > entryprice of first trade
// }

if (sig.Type == 5)
//If signal type is scale-in
{
trade = bo.FindOpenPos(sig.Symbol);
if (trade)
{
if (trade.entryprice < sig.price AND
trade.IsLong()==True AND sig.type==5 )
{
sig.PosSize = 0; // Long: no-scale in if
signal price > entryprice of first trade
}
if (trade.IsLong()==False AND sig.type==5 AND
trade.entryprice > sig.price)
{
sig.PosSize = 0; // Long: no-scale in if signal
price > entryprice of first trade
}
if
(trade.Getpositionvalue()>(VarGet("_MaxScaleIn"))*Sig.PosSize)
{
sig.PosSize = 0; // Long+Short: no-scale in if current
position exceeds _MaxScaleIn*positionsize
}
}
}
}

// Process Signals per bar.


bo.ProcessTradeSignals( bar );

prevcash = bo.cash;
// Set resulting invested cash
after trades

// --- 3.4 POST PROCESS --- //


bo.PostProcess(1); //no listtrades

// --- 3.5 CREATE TRADE METRICS --- //


prevRet=0;
e = Foreign( "~~~Equity", "C" );
for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
{
Ret=trade.GetPositionValue()/trade.GetEntryValue();
// Return of Trade
LogRet = ln(Ret); LogRange=abs(logRet-prevRet);
// LogReturn of Trade & Range of LogReturn
eq=FindValueAtDateTime(e,DateTime(),trade.EntryDateTime);
PercentOfEquity=100*trade.GetEntryValue()/eq;
trade.AddCustomMetric("Ret", Ret);

trade.AddCustomMetric("LogRet", NumToStr(LogRet,1.5) );
trade.AddCustomMetric("RangeRet", NumToStr(LogRange,1.5) );
trade.AddCustomMetric("%ofEquity",NumToStr(PercentOfEquity,1.2) );
prevRet=logRet;
}
//RMathDebugOn();
//RMathSetArray(trades,"trades");
//RMathDebugOff();

// --- 3.6 LIST TRADES (This adds custom trade metrics) --- //
bo.ListTrades();
s=1;
results=0;
first = LastValue(ValueWhen(Status("firstbarinrange"),
BarIndex()));
last = LastValue(ValueWhen(Status("lastbarinrange"),
BarIndex()));
testperiod = Last-First;
for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
{
results[s] = trade.GetPercentProfit();
s++;
s=Min(s,testperiod);
}
pgainmdd=0;
if (s>1) {pgainmdd = Resample(results,"resample.csv",Min(s,testperiod-
1),0.20,10000);}

// --- 3.7 CREATE PORTFOLIO METRICS --- //


e = Foreign( "~~~Equity", "C" );
refline = Cum( 1 );
Days = 0;
stats = bo.GetPerformanceStats( 0 );
trade = bo.GetFirstTrade();
Daysn = 0;
Ratio = 0;
CAGR = 0;
Years = 0;
Lastexit = 0;
Firstentry = " ";
RiskFree = VarGet("_RiskFreeRateSharpe");
RiskFreeTicker= Cashdata;

if (trade)
{
Lastdate=trade.entrydatetime;
for( Lasttrade = bo.GetFirstTrade(); Lasttrade; Lasttrade = bo.GetNextTrade()
)
{
LastDate = Max(lastdate,LastTrade.Exitdatetime);
}
Entryd= DateTimeConvert(0,trade.entrydatetime);
Days = BarsSince(DateNum()==entryd);
Daysn = StrToNum( WriteVal(Days,5.5));
Text = StrRight(NumToStr( trade.entrydatetime, formatDateTime ),4);
Years = ceil(DateTimeDiff(Lastdate,trade.entrydatetime) / 31557600);
DaysInTest =
NumToStr( ceil(DateTimeDiff(Lastdate,trade.entrydatetime)*365.25/ 31557600),1.0);
Ratio = stats.getvalue("EndingCapital") / stats.getvalue("InitialCapital");
CAGR = NumToStr((Ratio ^ (1/Years)-1)*100,1.2);
firstentry = NumToStr( trade.entrydatetime, formatDateTime );
Lastexit = NumToStr( Lastdate, formatDateTime);
}

if (Interval() == 86400){sharpperiod=252;} // (inDaily constant)


if (Interval() == 432001){sharpperiod=52;} // (inWeekly constant)
if (Interval() == 2160001){sharpperiod=12;} // (inMonthly constant)

first = LastValue(ValueWhen(Status("firstbarinrange"),
BarIndex()));
last = LastValue(ValueWhen(Status("lastbarinrange"),
BarIndex()));
testperiod = Last-First;
ret = Nz(ROC(e,1)/100,0);
DiffRet = ret;//-(ROC(RiskFreeTicker,1)/100);

MeanReturns = LastValue(Sum(DiffRet,testperiod)/testperiod,True);
StdDevReturns = LastValue(StDev(DiffRet,testperiod),True);
SR_ret_ann = ((MeanReturns*sharpperiod)-RiskFree)/
(StdDevReturns*(sharpperiod^0.5));
r2 = Correlation( e, refline, Daysn ) ^ 2; //r2
= Correlation( e, refline, BarCount - 1 ) ^ 2;
sr = stats.getvalue("SharpeRatio");
dvr = NumToStr(r2 * SR_ret_ann,1.2);
srStr = NumToStr(sr,1.2);
r2Str = NumToStr(r2,1.2);
proz_w = NumToStr(stats.getvalue("WinnersPercent"),1.2);
proz_Avg = NumToStr(stats.getvalue("AllAvgProfitLossPercent"),1.2);
TotalTrades = NumToStr(stats.getvalue("WinnersQty") +
stats.getvalue("LosersQty"),1.0,False);
MaxDD = NumToStr(stats.getvalue("MaxSystemDrawdownPercent"),1.2);
CAGRMaxDD = NumToStr(-(Ratio ^ (1/Years)-
1)*100/stats.getvalue("MaxSystemDrawdownPercent"),1.2);
RAR = NumToStr(stats.getvalue("RAR"),1.2);
UI = NumToStr(stats.getvalue("UlcerIndex"),1.2);
UIP = NumToStr(stats.getvalue("UlcerPerformanceIndex"),1.2);

bo.AddCustomMetric( "RAR", RAR);


bo.AddCustomMetric( "CAR", CAGR);
bo.AddCustomMetric( "DVR",dvr);
bo.AddCustomMetric( "Sharpe (trades)", srStr);
bo.AddCustomMetric( "Sharpe (returns)", NumToStr(SR_ret_ann,1.2));
bo.AddCustomMetric( "R2", r2str);
bo.AddCustomMetric( "Ulcer", UI);
bo.AddCustomMetric( "UlcerPerf", UIP);
bo.AddCustomMetric( "%W", proz_w);
bo.AddCustomMetric( "%Avg",proz_avg);
bo.AddCustomMetric( "MaxDD", MaxDD);
bo.AddCustomMetric("PGainMdd",pgainmdd);
bo.AddCustomMetric( "TT", TotalTrades );
bo.AddCustomMetric( "Years", NumToStr(Years,0) );
bo.AddCustomMetric( "FirstTrade", firstEntry );
bo.AddCustomMetric( "LastExit", LastExit );
bo.AddCustomMetric( "DaysInTest", TestPeriod);

ClipboardSet(""+RAR + "\n" + CAGR + " \n" + DVR + " \n" + srstr + "\n"
+NumToStr(SR_ret_ann,1.2)+ "\n"+ r2str + "\n" + proz_w + "\n" + proz_avg + "\n" +
MaxDD +"\n" + pgainmdd + "\n" + CAGRMaxDD + "\n" + Totaltrades );
// --- 3.8 CREATE OUTPUT FILES --- //
e = Foreign( "~~~Equity", "C" );
AddToComposite(e/1000,"~~"+SystemName,"X",atcFlagDefaults |
atcFlagEnableInPortfolio | atcFlagEnableInBacktest);
if( True )
{
fdelete(ExportPath+SystemName+"_stats.csv" );
fh = fopen(ExportPath+SystemName+"_stats.csv","w" );
for (m=1;m<45;m++)
{
switch(m)
{
case 1: metric="Initialcapital";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 2: metric="EndingCapital";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 3: metric="NetProfit";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 4: metric="NetProfitPercent";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 5: metric="ExposurePercent";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 6: metric="NetRAR";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 7: metric="CAR";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 8: metric="RAR";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 9: metric="AllQty";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 10: metric="AllPercent";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 11: metric="AllAvgProfitLoss";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 12: metric="AllAvgProfitLossPercent";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 13: metric="AllAvgBarsHeld";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 14: metric="WinnersQty";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 15: metric="WinnersPercent";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 16: metric="WinnersTotalProfit";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 17: metric="WinnersAvgProfit";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 18: metric="WinnersAvgProfitPercent";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 19: metric="WinnersAvgBarsHeld";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 20: metric="WinnersMaxConsecutive";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 21: metric="WinnersLargestWin";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 22: metric="WinnersLargestWinBars";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 23: metric="LosersQty";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 24: metric="LosersPercent";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 25: metric="LosersTotalLoss";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 26: metric="LosersAvgLoss";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 27: metric="LosersAvgLossPercent";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 28: metric="LosersAvgBarsHeld";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 29: metric="LosersMaxConsecutive";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 30: metric="LosersLargestLoss";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 31: metric="LosersLargestLossBars";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 31: metric="MaxTradeDrawdown";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 32: metric="MaxTradeDrawdownPercent";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 33: metric="MaxSystemDrawdown";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 34: metric="MaxSystemDrawdownPercent";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 35: metric="RecoveryFactor";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 36: metric="CAR/MDD";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 37: metric="RAR/MDD";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 38: metric="ProfitFactor";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 39: metric="StandardError";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 40: metric="RRR";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 41: metric="UlcerIndex";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 42: metric="UlcerPerformanceIndex";qs =
StrFormat("%.4f", stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 43: metric="SharpeRatio";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
case 44: metric="KRatio";qs = StrFormat("%.4f",
stats.GetValue(Metric) ); fputs(Metric+","+qs+"\n", fh );
}
qs = StrFormat("%.4f", LastValue( r2 )); fputs("R-
Squared"+","+qs+"\n", fh );
qs = DVR; fputs("DVR"+","+qs+"\n", fh );
}
}
fclose( fh );
}

Potrebbero piacerti anche