- Joined
- 8 June 2008
- Posts
- 13,239
- Reactions
- 19,540
@Willzy thanks for joining in with the forum discussions again.Is everyone sticking with the old CAR/MDD or does anyone have a particularly good alternative.
// User Defined Functions
function pattern1( fn, a, b, d ) // Trapezium based step function where value is between 0.5 and 1.0 (optimal between a and b) if greater than d value = 0.5
{
y = IIf( fn < a, 1 - ( 0.5 / a ) * ( a - fn ),
IIf( ( fn >= a AND fn <= b ), 1,
IIf( ( fn > b AND fn < d ), 1 - ( 0.5 / b ) * ( fn - b ),
0.5 ) ) );
return y;
}
function ramp( fn, ymin, a, b ) // Ramp function returns a value between ymin and 1 (ramp up between a and b)
{
y = IIf( fn < a, ymin,
IIf( ( fn >= a AND fn <= b ), ( ( fn - a ) * ( 1 - ymin ) / ( b - a ) + ymin ),
1 ) );
return y;
}
function step( fn, a ) // Step up function returns 1 if function is greater than or equal to a
{
y = IIf( fn < a, 0, 1 );
return y;
}
function between( fn, a, b )
{
y = IIf( fn >= a AND fn <= b, 1, 0 );
return y;
}
// --- Initiate Custom Backtest Proceedure
SetCustomBacktestProc( "" );
if( Status( "action" ) == actionPortfolio )
{
bo = GetBacktesterObject();
bo.backtest();
st = bo.getperformancestats( 0 );
// --- Standard Metrics from Amibroker
initialEquity = st.GetValue( "InitialCapital" );
netProfit = st.GetValue( "NetProfit" );
netProfitPercent = st.getvalue( "NetProfitPercent" );
totalTrades = st.getvalue( "allqty" );
car = st.GetValue( "CAR" );
mean = st.getvalue( "AllAvgProfitLossPercent" );
A = 0.01 * st.getvalue( "WinnersPercent" );
P = st.GetValue( "WinnersAvgProfitPercent" ) / st.GetValue( "LosersAvgLossPercent" ) * -1;
// Calculates Trade Frequency on a Daily Basis
tradesPerDay = TotalTrades / ( 365.25 * ln( 1 + ( NetProfitPercent / 100 ) ) / ln( 1 + ( CAR / 100 ) ) );
tradesPerYear = floor(365.25 * tradesPerDay);
// Calculates Expectancy as a Percentage Value
E = P * A + A - 1;
// Initialize Variables to calculate pearsonR2
r = 0;
if(tradesPerYear > 5 AND totalTrades >= 5)
{
r = n = x = y = xy = x2 = y2 = sumXY = sumX = sumY = sumX2 = sumY2 = 0;
for( trade = bo.getfirsttrade(); trade; trade = bo.getnexttrade() )
{
profit = trade.getpercentprofit() / 100 * 10000;
n ++;
y += profit;
x = n;
sumXY += x*y;
sumX += x;
sumY += y;
sumX2 += x*x;
sumY2 += y*y;
}
nom = n*sumXY - sumX*sumY;
denom = sqrt( (n*sumX2 - sumX*sumX) * (n*sumY2 - sumY*sumY) );
r = IIf(denom == 0,0,nom/denom);
}
// Custom Metrics
// --- Objective Function
ObFn = r;
// Plot Metrics in Desired Order
bo.AddCustomMetric( "ObFn", ObFn, Null, Null, 4 );
bo.AddCustomMetric("Profit $",NetProfit);
bo.addcustommetric( "A % ", A, Null, Null, 2 );
bo.addcustommetric( "W/L", P, Null, Null, 2 );
bo.addcustommetric( "E", E, Null, Null, 2 );
bo.AddCustomMetric( "#T/Yr", tradesPerYear, Null, Null, 0 );
}
// Ranking of stocks in current watchlist
if ( GetOption( "ApplyTo" )== 2 )
{
wlnum = GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categoryWatchlist,wlnum ) ;
}
else
if ( GetOption( "ApplyTo" )== 0 )
{
List = CategoryGetSymbols( categoryAll, 0 );
}
else
{
Error( "Theformula works fine if your ApplyTo setting is 'Filter' or 'All' " );
}
if ( Status("stocknum")== 0 ) //GENERATE RANKING WHEN WE ARE ON VERY FIRST SYMBOL
{
StaticVarRemove( "values*" );
for (n = 0; ( Symbol = StrExtract(List, n ) ) != ""; n++ )
{
SetForeign (symbol );
values = ROC(Close, 60); // ROC Indicator used to create set of values for Ranking
RestorePriceArrays();
StaticVarSet ( "values" + symbol,values );
_TRACE(symbol );
}
StaticVarGenerateRanks( "rank", "values", 0, 1224 );
}
symbol = Name();
LastDayOfMonth = IIf( (Month() == Ref( Month(), 1) AND (Month() != Ref( Month(), 2)) ), 1, 0);
TradeDay = LastDayOfMonth ;
values = StaticVarGet ( "values" + symbol);
rank = IIf (TradeDay, StaticVarGet ( "rankvalues" + symbol), Null);
PositionScore = IIf(values < 0, 0, values ); // Long only
SetSortColumns( 4 );
Filter = TradeDay;
AddColumn ( PositionScore, "ROC Values" );
AddColumn ( rank, "rank",1);
// Market Regime Filter
/* NORGATE Data Handling: -------------------------------------------------------*/
#include_once "C:\Users\woods_7878\OneDrive\Formulas\Norgate Data\Norgate Data Functions.afl"
OnSecondLastBarOfDelistedSecurity = !IsNull(GetFnData("DelistingDate")) AND (BarIndex() == (LastValue(BarIndex()) -1) OR DateTime() >= GetFnData("DelistingDate") ) ;
OnLastTwoBarsOfDelistedSecurity = !IsNull(GetFnData("DelistingDate")) AND (BarIndex() >= (LastValue(BarIndex()) -1) OR DateTime() >= GetFnData("DelistingDate") );
/* BACKTEST Parameters:------------------------------------------------------------------*/
SetOption ( "CommissionMode", 2 ); // $ per trade
SetOption ( "CommissionAmount", 30 ); // $ Amount per trade
SetOption ( "InitialEquity", 10000 );
SetOption ( "AccountMargin", 100);
PositionSize = 10000;
SetPositionSize( PositionSize, spsValue );
MaxPos = 20;
SetOption( "MaxOpenPositions", MaxPos );
SetTradeDelays( 1, 1, 1, 1 );
SetOption ( "AllowSameBarExit", 0);
SetOption ( "SeparateLongShortRank", 1);
/* Volatility Filter:------------------------------------------------------------------*/
VIXImported = Foreign("$XVI","C"); // The S&P/ASX 200 VIX Index (XVI) calculates the amount of volatility expected in the market over the next 30 days.
VIXQuiet = MA(VIXImported,5) <= Percentile( MA(VIXImported,5), 252, 35 ); // Current MA(VIX) ranks within Bottom 35% of last 252 days
VIXVolatile = MA(VIXImported,5) > Percentile( MA(VIXImported,5), 252, 35 );
/* PRICE Filter:------------------------------------------------------------------*/
NorgateIndex = NorgateIndustryIndex("$XJO", 1 ,"PR");
SectorIndexImported = Foreign(NorgateIndex,"C"); // Brings in sector index name XEJ, XMJ etc
BullMarket = MA(SectorIndexImported,5) > MA(SectorIndexImported,252); // Defines if in Bull or Bear
BearMarket = MA(SectorIndexImported,5) < MA(SectorIndexImported,252);
VolatileBear = BearMarket AND VIXVolatile;
QuietBear = BearMarket AND VIXQuiet;
QuietBull = BullMarket AND VIXQuiet;
VolatileBull = BullMarket AND VIXVolatile;
//RegimeFilter = IIf( VolatileBear, Optimize("VolatileBear", 1000, 0, 4000, 1000), IIf( QuietBear, Optimize("QuietBear", 3000, 0, 4000, 1000),
//IIf( QuietBull, Optimize("QuietBull", 0, 0, 4000, 1000), IIf( VolatileBull, Optimize("VolatileBull", 3000, 0, 4000, 1000), 0))));
RegimeFilter = IIf( VolatileBear, 2000, IIf( QuietBear, 3000, IIf( QuietBull, 0, IIf( VolatileBull, 3000, 0)))); // Weighting to PositionScore
PositionScore = ROC(Close, 100) + RegimeFilter;
period = 30; // number of averaging periods
m = MA( Close, period ); // simple moving average
_SECTION_BEGIN("Buy / Sell");
{
BuyNorgate = NorgatePaddingStatusTimeSeries()==0 AND NOT OnLastTwoBarsOfDelistedSecurity;
Buy = Cross( Close, m ) AND BuyNorgate;
SellNorgate = OnSecondLastBarOfDelistedSecurity;
Sell = Cross( m, Close ) OR SellNorgate;
Buy = ExRem(Buy,Sell);
Sell = ExRem(Sell,Buy);
Short = Cover = 0;
Short = ExRem(Short,Cover);
Cover = ExRem(Cover,Short);
}
_SECTION_BEGIN("Exploration");
{
if( Status( "Action" ) == actionExplore ); // SetSortColumns( -5 );
Filter = 1;
AddColumn(PositionScore, "Score", 1.2, colorDefault, colorDefault, 70);
AddColumn( VIXImported, "$XVI",1.2,colordefault,colorDefault,50 );
AddtextColumn( NorgateIndex, "Index");
AddColumn( VolatileBear, "Volatile Bear",1,
IIf( VolatileBear, colorGreen, colorRed),
IIf( VolatileBear, colorGreen, colorRed), 80);
AddColumn( QuietBear, "Quiet Bear",1,
IIf( QuietBear, colorGreen, colorRed),
IIf( QuietBear, colorGreen, colorRed), 80);
AddColumn( VolatileBull, "Volatile Bull",1,
IIf( VolatileBull, colorGreen, colorRed),
IIf( VolatileBull, colorGreen, colorRed), 80);
AddColumn( QuietBull, "Quiet Bull",1,
IIf( QuietBull, colorGreen, colorRed),
IIf( QuietBull, colorGreen, colorRed), 80);
}
@Trav. , What is the period you used for your with/without backtests? January start so including crash?Alright I'm back again with an Market Regime Filter - Inspired by Matt Radtke - quantforhire
I have created some framework that can be used / modified as you see fit, but basically the positionscore is weighted depending on the market regime filter.
You could use this to adjust stops, position sizes or ???? yep that's right whatever you can dream up.
Basically we have ;
1. 4 x market regimes defined by combinations of Stock Sector Index and Volatility using the S&P/ASX 200 VIX Index (XVI)
Regimes = VolatileBear, QuietBear, QuietBull & VolatileBull
2. Position score weighted by regime
3. Standard MA Crossover, ASX200, $10k Fixed Position size, Max 20 Positions, $100k Equity, Long Only
4. results ranked on CAR/MDD
*** For education purposes only *****
No Regime Filter.............................With Regime Filter
View attachment 104630 View attachment 104631
Code:// Market Regime Filter /* NORGATE Data Handling: -------------------------------------------------------*/ #include_once "C:\Users\woods_7878\OneDrive\Formulas\Norgate Data\Norgate Data Functions.afl" OnSecondLastBarOfDelistedSecurity = !IsNull(GetFnData("DelistingDate")) AND (BarIndex() == (LastValue(BarIndex()) -1) OR DateTime() >= GetFnData("DelistingDate") ) ; OnLastTwoBarsOfDelistedSecurity = !IsNull(GetFnData("DelistingDate")) AND (BarIndex() >= (LastValue(BarIndex()) -1) OR DateTime() >= GetFnData("DelistingDate") ); /* BACKTEST Parameters:------------------------------------------------------------------*/ SetOption ( "CommissionMode", 2 ); // $ per trade SetOption ( "CommissionAmount", 30 ); // $ Amount per trade SetOption ( "InitialEquity", 10000 ); SetOption ( "AccountMargin", 100); PositionSize = 10000; SetPositionSize( PositionSize, spsValue ); MaxPos = 20; SetOption( "MaxOpenPositions", MaxPos ); SetTradeDelays( 1, 1, 1, 1 ); SetOption ( "AllowSameBarExit", 0); SetOption ( "SeparateLongShortRank", 1); /* Volatility Filter:------------------------------------------------------------------*/ VIXImported = Foreign("$XVI","C"); // The S&P/ASX 200 VIX Index (XVI) calculates the amount of volatility expected in the market over the next 30 days. VIXQuiet = MA(VIXImported,5) <= Percentile( MA(VIXImported,5), 252, 35 ); // Current MA(VIX) ranks within Bottom 35% of last 252 days VIXVolatile = MA(VIXImported,5) > Percentile( MA(VIXImported,5), 252, 35 ); /* PRICE Filter:------------------------------------------------------------------*/ NorgateIndex = NorgateIndustryIndex("$XJO", 1 ,"PR"); SectorIndexImported = Foreign(NorgateIndex,"C"); // Brings in sector index name XEJ, XMJ etc BullMarket = MA(SectorIndexImported,5) > MA(SectorIndexImported,252); // Defines if in Bull or Bear BearMarket = MA(SectorIndexImported,5) < MA(SectorIndexImported,252); VolatileBear = BearMarket AND VIXVolatile; QuietBear = BearMarket AND VIXQuiet; QuietBull = BullMarket AND VIXQuiet; VolatileBull = BullMarket AND VIXVolatile; //RegimeFilter = IIf( VolatileBear, Optimize("VolatileBear", 1000, 0, 4000, 1000), IIf( QuietBear, Optimize("QuietBear", 3000, 0, 4000, 1000), //IIf( QuietBull, Optimize("QuietBull", 0, 0, 4000, 1000), IIf( VolatileBull, Optimize("VolatileBull", 3000, 0, 4000, 1000), 0)))); RegimeFilter = IIf( VolatileBear, 2000, IIf( QuietBear, 3000, IIf( QuietBull, 0, IIf( VolatileBull, 3000, 0)))); // Weighting to PositionScore PositionScore = ROC(Close, 100) + RegimeFilter; period = 30; // number of averaging periods m = MA( Close, period ); // simple moving average _SECTION_BEGIN("Buy / Sell"); { BuyNorgate = NorgatePaddingStatusTimeSeries()==0 AND NOT OnLastTwoBarsOfDelistedSecurity; Buy = Cross( Close, m ) AND BuyNorgate; SellNorgate = OnSecondLastBarOfDelistedSecurity; Sell = Cross( m, Close ) OR SellNorgate; Buy = ExRem(Buy,Sell); Sell = ExRem(Sell,Buy); Short = Cover = 0; Short = ExRem(Short,Cover); Cover = ExRem(Cover,Short); } _SECTION_BEGIN("Exploration"); { if( Status( "Action" ) == actionExplore ); // SetSortColumns( -5 ); Filter = 1; AddColumn(PositionScore, "Score", 1.2, colorDefault, colorDefault, 70); AddColumn( VIXImported, "$XVI",1.2,colordefault,colorDefault,50 ); AddtextColumn( NorgateIndex, "Index"); AddColumn( VolatileBear, "Volatile Bear",1, IIf( VolatileBear, colorGreen, colorRed), IIf( VolatileBear, colorGreen, colorRed), 80); AddColumn( QuietBear, "Quiet Bear",1, IIf( QuietBear, colorGreen, colorRed), IIf( QuietBear, colorGreen, colorRed), 80); AddColumn( VolatileBull, "Volatile Bull",1, IIf( VolatileBull, colorGreen, colorRed), IIf( VolatileBull, colorGreen, colorRed), 80); AddColumn( QuietBull, "Quiet Bull",1, IIf( QuietBull, colorGreen, colorRed), IIf( QuietBull, colorGreen, colorRed), 80); }
Exploration
View attachment 104632
Hopefully this is useful to someone and the code will require a few tweaks but does give you some more food for thought.
Happy Trading...
Alright I'm back again with an Market Regime Filter - Inspired by Matt Radtke - quantforhire
I have created some framework that can be used / modified as you see fit, but basically the positionscore is weighted depending on the market regime filter.
#include_once "Formulas\Norgate Data\Norgate Data Functions.afl" //delete reference to your drive location
NorgateIndex = NorgateIndustryIndex("$XJO.au", 1 ,"PR");
_SECTION_BEGIN("Buy / Sell");
{
//BuyNorgate = NorgatePaddingStatusTimeSeries()==0 AND NOT OnLastTwoBarsOfDelistedSecurity;
Buy = Cross( Close, m );// AND BuyNorgate;
//SellNorgate = OnSecondLastBarOfDelistedSecurity;
Sell = Cross( m, Close ); // OR SellNorgate;
AddColumn ( SectorIndexImported, "Sector", 1.2,colordefault,colorDefault,50 );
So possibly there is something in respect of the Sector?
When you look at the exploration the Index shows as $XSJ and not $XSJ.au So possibly there is a disconnect here?
When exploration is run the sector value is blank
You use Premium Data and I am using Norgate Data, so there is a problem somewhere. Maybe email them to get a quick answer or work around for this line
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?