FRAMA Part IV: Continuing the Long/Short Filter Search
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
This post examines an n-day median filter for two desirable properties: robustness to outliers and an inherent trend-confirming lag. While this is an incomplete filter (or maybe even inferior), it offers some key insights into improving the trading system.
The strategy will be thus:
First and foremost, this will be a short-only strategy, due to the long bias within the sample period, so the stress-test of the system will be to attempt to capture the non-dominant trend (and only when appropriate).
Here’s the strategy: we will continue to use the same 126 day FRAMA with the fast constant set at 4, and a slow constant at 300 (that is, it can oscillate anywhere between an EMA4 and EMA300). We will only enter into a short position when this indicator is descending, below the 126-day median of the price action, and when the price action is lower than this indicator (usually this means a cross, not in all cases though). We will exit when the price action rises back above the indicator.
Here’s the strategy in R code:
require(DSTrading) require(IKTrading) require(quantstrat) require(PerformanceAnalytics) initDate="1990-01-01" from="2003-01-01" to="2010-12-31" options(width=70) #to rerun the strategy, rerun everything below this line source("demoData.R") #contains all of the data-related boilerplate. #trade sizing and initial equity settings tradeSize <- 10000 initEq <- tradeSize*length(symbols) strategy.st <- portfolio.st <- account.st <- "FRAMA_III" rm.strat(portfolio.st) rm.strat(strategy.st) initPortf(portfolio.st, symbols=symbols, initDate=initDate, currency='USD') initAcct(account.st, portfolios=portfolio.st, initDate=initDate, currency='USD',initEq=initEq) initOrders(portfolio.st, initDate=initDate) strategy(strategy.st, store=TRUE) #parameters FC=4 SC=300 n=126 triggerLag=1 period=10 pctATR=.02 #indicators add.indicator(strategy.st, name="FRAMA", arguments=list(HLC=quote(HLC(mktdata)), n=n, SC=SC, FC=FC, triggerLag=triggerLag), label="primary") add.indicator(strategy.st, name="runMedian", arguments=list(x=quote(Cl(mktdata)), n=n), label="confirmatory") add.indicator(strategy.st, name="lagATR", arguments=list(HLC=quote(HLC(mktdata)), n=period), label="atrX") # #long signals # # add.signal(strategy.st, name="sigComparison", # arguments=list(columns=c("FRAMA.primary", "X1.confirmatory"), # relationship="gte"), # label="FRAMAgteMedian") # # add.signal(strategy.st, name="sigComparison", # arguments=list(columns=c("FRAMA.primary", "trigger.primary"), # relationship="gte"), # label="FRAMArising") # # add.signal(strategy.st, name="sigComparison", # arguments=list(columns=c("Close", "FRAMA.primary"), # relationship="gte"), # label="ClGtFRAMA") # # add.signal(strategy.st, name="sigAND", # arguments=list(columns=c("FRAMAgteMedian", # "FRAMArising", "ClGtFRAMA"), # cross=TRUE), # label="longEntry") # # add.signal(strategy.st, name="sigCrossover", # arguments=list(columns=c("Close", "FRAMA.primary"), # relationship="lt"), # label="longExit") # # #long rules # # add.rule(strategy.st, name="ruleSignal", # arguments=list(sigcol="longEntry", sigval=TRUE, ordertype="market", # orderside="long", replace=FALSE, prefer="Open", osFUN=osDollarATR, # tradeSize=tradeSize, pctATR=pctATR, atrMod="X"), # type="enter", path.dep=TRUE) # # add.rule(strategy.st, name="ruleSignal", # arguments=list(sigcol="longExit", sigval=TRUE, orderqty="all", ordertype="market", # orderside="long", replace=FALSE, prefer="Open"), # type="exit", path.dep=TRUE) #short signals add.signal(strategy.st, name="sigComparison", arguments=list(columns=c("FRAMA.primary", "X1.confirmatory"), relationship="lt"), label="FRAMAltMedian") add.signal(strategy.st, name="sigComparison", arguments=list(columns=c("FRAMA.primary", "trigger.primary"), relationship="lt"), label="FRAMAfalling") add.signal(strategy.st, name="sigComparison", arguments=list(columns=c("Close", "FRAMA.primary"), relationship="lt"), label="ClLtFRAMA") add.signal(strategy.st, name="sigAND", arguments=list(columns=c("FRAMAltMedian", "FRAMAfalling", "ClLtFRAMA"), cross=TRUE), label="shortEntry") add.signal(strategy.st, name="sigCrossover", arguments=list(columns=c("Close", "FRAMA.primary"), relationship="gt"), label="shortExit") #short rules add.rule(strategy.st, name="ruleSignal", arguments=list(sigcol="shortEntry", sigval=TRUE, ordertype="market", orderside="short", replace=FALSE, prefer="Open", osFUN=osDollarATR, tradeSize=-tradeSize, pctATR=pctATR, atrMod="X"), type="enter", path.dep=TRUE) add.rule(strategy.st, name="ruleSignal", arguments=list(sigcol="shortExit", sigval=TRUE, orderqty="all", ordertype="market", orderside="short", replace=FALSE, prefer="Open"), type="exit", path.dep=TRUE) #apply strategy t1 <- Sys.time() out <- applyStrategy(strategy=strategy.st,portfolios=portfolio.st) t2 <- Sys.time() print(t2-t1) #set up analytics updatePortf(portfolio.st) dateRange <- time(getPortfolio(portfolio.st)$summary)[-1] updateAcct(portfolio.st,dateRange) updateEndEq(account.st)
The results aren’t pretty, meaning that the filter is still incomplete. Here are the trade stats:
EFA EPP EWA EWC EWG Num.Txns 90.00 68.00 85.00 66.00 90.00 Num.Trades 44.00 34.00 41.00 33.00 45.00 Net.Trading.PL -2030.68 -25.54 -1485.82 -2283.03 -356.83 Avg.Trade.PL -46.15 -0.75 -36.24 -69.18 -7.93 Med.Trade.PL -103.99 -44.95 -77.27 -100.40 -69.91 Largest.Winner 1238.56 1656.56 1106.59 2195.51 3197.29 Largest.Loser -661.04 -786.27 -548.06 -823.55 -783.65 Gross.Profits 4336.92 4455.45 3246.48 3566.35 5948.76 Gross.Losses -6367.61 -4480.99 -4732.30 -5849.38 -6305.59 Std.Dev.Trade.PL 364.75 419.42 288.36 487.05 557.98 Percent.Positive 29.55 32.35 43.90 24.24 37.78 Percent.Negative 70.45 67.65 56.10 75.76 62.22 Profit.Factor 0.68 0.99 0.69 0.61 0.94 Avg.Win.Trade 333.61 405.04 180.36 445.79 349.93 Med.Win.Trade 57.09 238.60 66.34 124.31 101.88 Avg.Losing.Trade -205.41 -194.83 -205.75 -233.98 -225.20 Med.Losing.Trade -156.80 -122.45 -170.49 -166.84 -184.70 Avg.Daily.PL -46.15 -0.75 -36.24 -69.18 -7.93 Med.Daily.PL -103.99 -44.95 -77.27 -100.40 -69.91 Std.Dev.Daily.PL 364.75 419.42 288.36 487.05 557.98 Ann.Sharpe -2.01 -0.03 -2.00 -2.25 -0.23 Max.Drawdown -5089.55 -3095.58 -3609.64 -4915.76 -4222.60 Profit.To.Max.Draw -0.40 -0.01 -0.41 -0.46 -0.08 Avg.WinLoss.Ratio 1.62 2.08 0.88 1.91 1.55 Med.WinLoss.Ratio 0.36 1.95 0.39 0.75 0.55 Max.Equity 146.74 1473.19 834.54 78.43 1467.12 Min.Equity -4942.81 -2562.72 -2775.10 -4837.32 -3960.49 End.Equity -2030.68 -25.54 -1485.82 -2283.03 -356.83 EWH EWJ EWS EWT EWU Num.Txns 74.00 102.00 72.00 66.00 72.00 Num.Trades 36.00 51.00 35.00 33.00 36.00 Net.Trading.PL 596.16 -1493.76 982.93 1354.40 439.00 Avg.Trade.PL 16.56 -29.29 28.08 41.04 12.19 Med.Trade.PL -54.45 -89.63 -52.85 -40.22 -56.50 Largest.Winner 3436.43 1076.25 1165.10 1980.68 1680.33 Largest.Loser -544.78 -781.15 -429.64 -441.47 -468.28 Gross.Profits 4519.53 5681.81 4763.28 4317.26 5105.73 Gross.Losses -3923.37 -7175.57 -3780.35 -2962.87 -4666.73 Std.Dev.Trade.PL 610.29 368.61 353.59 408.77 415.23 Percent.Positive 38.89 37.25 37.14 42.42 33.33 Percent.Negative 61.11 62.75 62.86 57.58 66.67 Profit.Factor 1.15 0.79 1.26 1.46 1.09 Avg.Win.Trade 322.82 299.04 366.41 308.38 425.48 Med.Win.Trade 79.88 99.19 267.75 115.47 399.34 Avg.Losing.Trade -178.33 -224.24 -171.83 -155.94 -194.45 Med.Losing.Trade -137.71 -175.69 -141.06 -95.88 -180.13 Avg.Daily.PL 16.56 -29.29 28.08 41.04 12.19 Med.Daily.PL -54.45 -89.63 -52.85 -40.22 -56.50 Std.Dev.Daily.PL 610.29 368.61 353.59 408.77 415.23 Ann.Sharpe 0.43 -1.26 1.26 1.59 0.47 Max.Drawdown -2390.89 -2994.16 -1689.63 -2113.93 -3192.52 Profit.To.Max.Draw 0.25 -0.50 0.58 0.64 0.14 Avg.WinLoss.Ratio 1.81 1.33 2.13 1.98 2.19 Med.WinLoss.Ratio 0.58 0.56 1.90 1.20 2.22 Max.Equity 2534.61 1380.90 1938.04 2065.70 1845.45 Min.Equity -2131.17 -2540.03 -1265.23 -1501.15 -3192.52 End.Equity 596.16 -1493.76 982.93 1354.40 439.00 EWY EWZ EZU IEF IGE Num.Txns 68.00 80.00 96.00 63.00 56.00 Num.Trades 34.00 40.00 48.00 32.00 28.00 Net.Trading.PL 1359.59 -2763.77 -178.24 -5286.17 -588.44 Avg.Trade.PL 39.99 -69.09 -3.71 -165.19 -21.02 Med.Trade.PL 19.52 -103.71 -73.53 -253.57 -87.68 Largest.Winner 1799.34 2495.03 1423.73 908.54 2146.42 Largest.Loser -467.07 -496.73 -847.67 -758.78 -466.57 Gross.Profits 4729.27 2790.33 5960.68 2309.18 2757.36 Gross.Losses -3369.68 -5554.10 -6138.92 -7595.36 -3345.80 Std.Dev.Trade.PL 414.55 440.16 402.00 349.52 456.89 Percent.Positive 55.88 15.00 33.33 25.00 25.00 Percent.Negative 44.12 85.00 66.67 75.00 75.00 Profit.Factor 1.40 0.50 0.97 0.30 0.82 Avg.Win.Trade 248.91 465.05 372.54 288.65 393.91 Med.Win.Trade 58.75 43.02 67.59 156.68 43.03 Avg.Losing.Trade -224.65 -163.36 -191.84 -316.47 -159.32 Med.Losing.Trade -217.23 -110.98 -139.29 -284.87 -115.98 Avg.Daily.PL 39.99 -69.09 -3.71 -192.64 -21.02 Med.Daily.PL 19.52 -103.71 -73.53 -260.53 -87.68 Std.Dev.Daily.PL 414.55 440.16 402.00 318.33 456.89 Ann.Sharpe 1.53 -2.49 -0.15 -9.61 -0.73 Max.Drawdown -2237.74 -3903.71 -3510.08 -6682.82 -2836.80 Profit.To.Max.Draw 0.61 -0.71 -0.05 -0.79 -0.21 Avg.WinLoss.Ratio 1.11 2.85 1.94 0.91 2.47 Med.WinLoss.Ratio 0.27 0.39 0.49 0.55 0.37 Max.Equity 3532.28 836.88 1270.40 669.24 709.44 Min.Equity -790.83 -3066.84 -3222.22 -6013.57 -2127.36 End.Equity 1359.59 -2763.77 -178.24 -5286.17 -588.44 IYR IYZ LQD RWR SHY Num.Txns 96.00 108.00 63.00 98.00 51.00 Num.Trades 48.00 54.00 31.00 49.00 25.00 Net.Trading.PL -3444.89 -2032.70 1532.27 -3740.29 -4049.16 Avg.Trade.PL -71.77 -37.64 49.43 -76.33 -161.97 Med.Trade.PL -129.99 -83.00 -84.44 -114.84 -141.20 Largest.Winner 1714.13 2673.04 2693.04 1455.78 86.02 Largest.Loser -745.50 -463.08 -480.73 -578.29 -644.17 Gross.Profits 4652.33 4978.26 5114.62 3534.95 365.46 Gross.Losses -8097.22 -7010.97 -3582.35 -7275.24 -4414.63 Std.Dev.Trade.PL 405.93 479.46 604.30 341.37 195.72 Percent.Positive 22.92 22.22 35.48 16.33 28.00 Percent.Negative 77.08 77.78 64.52 83.67 72.00 Profit.Factor 0.57 0.71 1.43 0.49 0.08 Avg.Win.Trade 422.94 414.86 464.97 441.87 52.21 Med.Win.Trade 110.81 29.28 139.76 188.82 44.33 Avg.Losing.Trade -218.84 -166.93 -179.12 -177.44 -245.26 Med.Losing.Trade -182.73 -134.02 -129.79 -138.78 -232.61 Avg.Daily.PL -71.77 -37.64 45.47 -76.33 -162.83 Med.Daily.PL -129.99 -83.00 -86.18 -114.84 -144.12 Std.Dev.Daily.PL 405.93 479.46 614.22 341.37 199.88 Ann.Sharpe -2.81 -1.25 1.18 -3.55 -12.93 Max.Drawdown -3857.85 -5575.45 -2876.51 -4695.60 -4049.16 Profit.To.Max.Draw -0.89 -0.36 0.53 -0.80 -1.00 Avg.WinLoss.Ratio 1.93 2.49 2.60 2.49 0.21 Med.WinLoss.Ratio 0.61 0.22 1.08 1.36 0.19 Max.Equity 260.07 118.92 3375.06 302.96 0.00 Min.Equity -3597.77 -5456.52 -2138.62 -4392.65 -4049.16 End.Equity -3444.89 -2032.70 1532.27 -3740.29 -4049.16 TLT XLB XLE XLF XLI Num.Txns 85.00 104.00 50.00 120.00 92.00 Num.Trades 43.00 51.00 25.00 60.00 46.00 Net.Trading.PL -4037.97 -5591.16 -308.15 -3036.79 -2136.85 Avg.Trade.PL -93.91 -109.63 -12.33 -50.61 -46.45 Med.Trade.PL -133.03 -138.47 -96.47 -79.48 -108.98 Largest.Winner 1425.91 1831.45 1828.51 1058.03 1218.87 Largest.Loser -543.28 -707.20 -430.13 -711.69 -632.77 Gross.Profits 3355.40 3130.31 2472.08 5282.27 4597.82 Gross.Losses -7393.37 -8721.48 -2780.24 -8319.05 -6734.68 Std.Dev.Trade.PL 338.88 345.20 420.71 309.84 342.80 Percent.Positive 25.58 25.49 20.00 30.00 23.91 Percent.Negative 74.42 74.51 80.00 70.00 76.09 Profit.Factor 0.45 0.36 0.89 0.63 0.68 Avg.Win.Trade 305.04 240.79 494.42 293.46 417.98 Med.Win.Trade 168.50 83.98 33.87 135.46 294.74 Avg.Losing.Trade -231.04 -229.51 -139.01 -198.07 -192.42 Med.Losing.Trade -197.38 -207.82 -120.99 -171.67 -149.06 Avg.Daily.PL -101.44 -109.63 -12.33 -50.61 -46.45 Med.Daily.PL -140.48 -138.47 -96.47 -79.48 -108.98 Std.Dev.Daily.PL 339.33 345.20 420.71 309.84 342.80 Ann.Sharpe -4.75 -5.04 -0.47 -2.59 -2.15 Max.Drawdown -4926.34 -6711.79 -1938.05 -3451.10 -4068.90 Profit.To.Max.Draw -0.82 -0.83 -0.16 -0.88 -0.53 Avg.WinLoss.Ratio 1.32 1.05 3.56 1.48 2.17 Med.WinLoss.Ratio 0.85 0.40 0.28 0.79 1.98 Max.Equity 459.78 0.00 1298.51 414.31 0.00 Min.Equity -4466.56 -6711.79 -1329.01 -3036.79 -4068.90 End.Equity -4037.97 -5591.16 -308.15 -3036.79 -2136.85 XLK XLP XLU XLV XLY Num.Txns 86.00 92.00 82.00 94.00 82.00 Num.Trades 43.00 45.00 40.00 47.00 40.00 Net.Trading.PL -1205.62 -4427.34 -3490.76 -4291.56 -230.80 Avg.Trade.PL -28.04 -98.39 -87.27 -91.31 -5.77 Med.Trade.PL -101.30 -153.01 -93.87 -98.69 -140.01 Largest.Winner 2403.16 1008.09 1805.03 842.35 2090.68 Largest.Loser -806.29 -460.10 -462.68 -554.57 -698.45 Gross.Profits 4984.72 2839.42 2493.63 2959.31 6253.33 Gross.Losses -6190.34 -7266.76 -5984.39 -7250.87 -6484.14 Std.Dev.Trade.PL 464.22 294.41 350.37 280.87 495.21 Percent.Positive 30.23 15.56 20.00 19.15 30.00 Percent.Negative 69.77 84.44 80.00 80.85 70.00 Profit.Factor 0.81 0.39 0.42 0.41 0.96 Avg.Win.Trade 383.44 405.63 311.70 328.81 521.11 Med.Win.Trade 191.31 116.12 61.87 266.16 307.13 Avg.Losing.Trade -206.34 -191.23 -187.01 -190.81 -231.58 Med.Losing.Trade -188.49 -191.04 -156.33 -161.51 -171.21 Avg.Daily.PL -28.04 -98.39 -87.27 -91.31 -5.77 Med.Daily.PL -101.30 -153.01 -93.87 -98.69 -140.01 Std.Dev.Daily.PL 464.22 294.41 350.37 280.87 495.21 Ann.Sharpe -0.96 -5.30 -3.95 -5.16 -0.18 Max.Drawdown -3448.99 -5384.93 -3540.13 -5186.60 -3964.07 Profit.To.Max.Draw -0.35 -0.82 -0.99 -0.83 -0.06 Avg.WinLoss.Ratio 1.86 2.12 1.67 1.72 2.25 Med.WinLoss.Ratio 1.01 0.61 0.40 1.65 1.79 Max.Equity 646.11 651.59 0.00 895.04 2960.96 Min.Equity -3003.57 -4733.34 -3540.13 -4291.56 -1003.11 End.Equity -1205.62 -4427.34 -3490.76 -4291.56 -230.80
At this point, for the sake of brevity, I’ll leave off the equity curves and portfolio statistics (they’ll obviously be bad). However, let’s look at some images of what exactly is going on with individual trades.
Here is the full-backtest equity curve and corresponding indicators for XLP. The FRAMA is in purple, with the 126-day median in orange, along with the 10-day ATR (lagged by a day) on the bottom.
And here we can immediately see certain properties:
1) ATR order-sizing is not a be-all, end-all type of order. It was created for one purpose, which is to equalize risk across instruments (the original idea of which, I defer to Andreas Clenow’s article). However, that is only a base from which to begin, using other scaled order-sizing procedures which can attempt to quantify the confidence in any particular trade. As it currently stands, for short strategies in equities, the best opportunities happen in the depths of rapid falling price action, during which ATR will rise. One may consider augmenting the ATR order sizing function in order to accomplish this task (or merely apply leverage at the proper time, through modifying the pctATR parameter).
2) While the running median certainly has value as a filter to keep out obviously brainless trades (E.G. in the middle of an uptrend), once the FRAMA crosses the median, anything can happen, as the only logic is that the current FRAMA is just slightly lower than the previous day’s. This may mean that the running median itself is still rising, or that the FRAMA is effectively flat, and what is being traded on is purely noise. And furthermore, with ATR order sizing amplifying the consequences of that noise, this edge case can have disastrous consequences on an equity curve.
Here’s a zoom in on 2005, where we see a pretty severe drawdown (chart time series recolored for clarity).
As can be seen, even though the FRAMA seems to be slightly rising, a price crossing when the FRAMA is lower than the previous day by even an invisibly small amount (compare the purple–the FRAMA, to the red–the same quantity lagged a day) is enough to trigger a trade that will buy a sizable number of shares, even when the volatility is too small to justify such a trade. Essentially, most of the losses in this trading system arise as a result of trading during these flat periods during which the system attempts to force action.
This pattern repeats itself. Here is the equity curve for XLB.
Again, aside from maybe a bad trade in the end thanks to any trade being taken once all three conditions line up (decreasing FRAMA, FRAMA lower than median, price lower than FRAMA) too late due to a flat FRAMA/median relationship, most of the losers seem to be trades made during very flat and calm market action, even when the running median may be going in the opposite direction of the FRAMA, during which the ATR order-sizing function tried to force action. A second filter that serves to catch these edge-case situations (or maybe a filter that replaces the running median entirely) will be investigated in the future.
So, to recap this post:
The running median filter is an intrinsically lagging but robust indicator, chosen deliberately for these two properties. It is able to filter out trades that obviously go against the trend. However, due to some edge cases, there were still a great deal of losses that were incurred, which drown out the one good shorting opportunity over this sample period. This is an issue that needs addressing.
Thanks for reading.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.