- ago
I am throwing this out, namely it would be nice to have more choices of averaging indicators (with adjustable parameters) to operate on the back test results EQUITY curve. This feature would be useful for those to use equity curve (manually) to make trading decision like that at end of day.
1
1,952
Solved
19 Replies

Reply

Bookmark

Sort
Cone8
 ( 28.32% )
- ago
#1
Please elaborate. What averaging indicators? What purpose should they serve?

Is it possible that you've just missed the Advanced Smoothers?

0
- ago
#2
The 50-period simple moving average on the Equity tab is an eye candy, a convenience feature. I'm skeptical about adding more choices and custom periods to it.

In WL7 you can plot and trade the portfolio's actual equity curve which of course includes the ability to apply any smoother on it. Go for it.

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; namespace WealthScript3 { public class MyStrategy : UserStrategyBase {       TimeSeries equity;       TimeSeries equityEMA;       //Configure your usual position size as % of equity (say 5% per position):       double percentEquity = 5;       public override void Initialize(BarHistory bars)       {          equity = new TimeSeries(bars.DateTimes);          equityEMA = new TimeSeries(bars.DateTimes);          PlotTimeSeries(equity, "Equity", "Equity", Color.Green, PlotStyles.ThickHistogram);          PlotTimeSeries(equityEMA, "EMA(Equity)", "Equity", Color.Black, PlotStyles.Line);       }       public override void Execute(BarHistory bars, int idx)       {          equity[idx] = CurrentEquity;          equityEMA[idx] = EMA.Series(equity, 10)[idx];          //Calculate the usual size in shares based on the close price          double c = bars.Close[idx];          var usualSize = (int)(CurrentEquity / c) / 100 * percentEquity;          //Trade twice the size if equity is above its moving average          var pctEquity = (equity[idx] > equityEMA[idx]) ? usualSize * 2 : usualSize;          if (!HasOpenPosition(bars, PositionType.Long))          {             var stop = Highest.Series(bars.High, 20)[idx];             PlaceTrade(bars, TransactionType.Buy, OrderType.Stop, stop).Quantity = pctEquity;             //As alternative...             //var t = PlaceTrade(bars, TransactionType.Buy, OrderType.Stop, stop);             //t.Quantity = pctEquity;          }          else          {             var stop = Lowest.Series(bars.Low, 20)[idx];             PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, stop);          }       } //declare private variables below } }
0
Glitch8
 ( 12.10% )
- ago
#3
Here is the recommended solution:

Create 2 new TineSeries in Initialize, use bars.DateTimes in constructor parameter.

In Execute, store the CurrentEquity in the first TimeSeries, using the idx parameter.

Use the Value method of the desired smoother, for example SMA.Value. The source parameter should be the first TimeSeries. Assign the resulting value (from the Value call) to the second TimeSeries.

You need not create the second TimeSeries, but of course you’ll need to do so if you want to plot the equity curve moving average using a PlotTimeSeries call.

There you go, access to the smoothed equity curve is already possible.
0
Glitch8
 ( 12.10% )
- ago
#4
Eugene, you should avoid using EMA.Series in the Execute code because it will cause the series to be calculated and cached on incomplete data. Use the Value method as I described above.
0
- ago
#5
Dion, EMA doesn't have a .Value method.
0
Glitch8
 ( 12.10% )
- ago
#6
Ok but Series will not work. The first time it’s calculated on the partial equity curve that result will get cached and returned in subsequent calls to Series.

Perhaps we should add a Value method? In the interim you could use the “new” operator to create the instance each time instead of using Series.
0
- ago
#7
Advanced Smoothers - I don't think these can be applied to an equity curve? My question is more for to do quick smoothing of the equity on the fly - down the road, once I got up to speed with WL7, the suggestions from Eugene and Glitch offer better approaches. Thanks.
0
Glitch8
 ( 12.10% )
- ago
#8
Any indicator can be applied using the technique I described. I’m not at the computer but will post an example tomorrow.
0
Glitch8
 ( 12.10% )
- ago
#9
Here is an example of creating a dynamic, smoothed equity curve using both an indicator that has the Value method (SMA) and one that doesn't (T3 from Advanced Smoothers). It's a modified RSI Agita.

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; using WealthLab.AdvancedSmoothers; namespace WealthScript1 { public class RSIAgita : UserStrategyBase {       public RSIAgita()       {          AddParameter("RSI Period", ParameterTypes.Int32, 14, 7, 42, 7);          AddParameter("Oversold", ParameterTypes.Int32, 25, 10, 60, 5);          AddParameter("Overbought", ParameterTypes.Int32, 60, 10, 80, 5);          }        //create indicators and other objects here, this is executed prior to the main trading loop public override void Initialize(BarHistory bars) {          period = Parameters[0].AsInt;          oversold = Parameters[1].AsInt;          overbought = Parameters[2].AsInt;          //Create and plot RSI          rsi = RSI.Series(bars.Close, period);          PlotTimeSeries(rsi, "rsi", "RSI", Color.Navy);          DrawHorzLine(oversold, Color.Red, 2, LineStyles.Dotted, "RSI");          DrawHorzLine(overbought, Color.Green, 2, LineStyles.Dotted, "RSI");          StartIndex = period + 1;          eq = new TimeSeries(bars.DateTimes);          t3Eq = new TimeSeries(bars.DateTimes);          smaEq = new TimeSeries(bars.DateTimes);          PlotTimeSeries(eq, "Equity", "Equity", Color.DarkGreen, PlotStyles.ThickHistogram);          PlotTimeSeries(t3Eq, "T3(20)", "Equity", Color.Blue);          PlotTimeSeries(smaEq, "SMA(20)", "Equity", Color.Black);       } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int idx) {          eq[idx] = CurrentEquity;          TimeSeries t3 = new T3(eq, 20, 0.7); //T3 does not have a static Value method so we need to do it this way          t3Eq[idx] = t3[idx];          smaEq[idx] = SMA.Value(idx, eq, 20);                    //Entries are allowed only if price is below last entry price          double lastEntryPrice = Double.MaxValue;          if (LastPosition != null)             if (LastPosition.IsOpen)                lastEntryPrice = LastPosition.EntryPrice;          //Check penetration of RSI below levels 35 down to 5          level = oversold;          while (level > 0)          {             if (rsi.CrossesUnder(level, idx))                if (bars.Close[idx] < lastEntryPrice)                   PlaceTrade(bars,TransactionType.Buy,OrderType.Market);             level -= 5;          }          //Exit all long positions if RSI crosses above 45     if (rsi.CrossesOver(overbought, idx))             foreach (Position pos in GetPositions())                if (pos.IsOpen)                   ClosePosition(pos,OrderType.Market); }       public override void BacktestComplete()       {          List<Position> positions = GetPositionsAllSymbols(true);          foreach(Position pos in positions)             if (pos.NSF)                WriteToDebugLog(pos.ToString());       }       //declare private variables below       int oversold, overbought, level, period;       RSI rsi;       private TimeSeries eq;       private TimeSeries t3Eq;       private TimeSeries smaEq;    } }


2
Best Answer
- ago
#10
QUOTE:
... avoid using EMA.Series in the Execute code because it will cause the series to be calculated and cached on incomplete data.
You could just avoid caching altogether:

CODE:
//equityEMA[idx] = EMA.Series(equity, 10)[idx]; equityEMA[idx] = new EMA(equity, 10)[idx]; //avoid caching the value
It might be possible to create a .ValueApprox method for EMA which would "roughly approximate" the value using a natural logarithm weighting. But if there's nonlinear behavior in the input time series, this approximation may not be so good.

You could smooth the equity curve all at once in the Cleanup() method, but then that smoothed result wouldn't be available for trading. :(
1
- ago
#11
Hi Glitch,

In WL8 Build 10, I followed your T3 equity curve example but for whatever reasons, the MAMA/FAMA indicators are shown as NaN.

Do you see the issue here or are these two indicators just not suitable for the equity curve, due to the way they are calculated?

Thanks...

0
Glitch8
 ( 12.10% )
- ago
#12
I discovered an issue in MAMA/FAMA that prevented it from working in this context, and it's fixed for Build 11 which should be available shortly. Once you upgrade, use the pattern like below, calculate and plot the indicators in Cleanup rather than Execute, this will ensure that the indicators are calculated only once at the end of the processing once the equity curve is fully realized. If you do need to access the MAMA/FAMA values as part of the Strategy logic, you'd need to keep the logic in Execute like you have it now, but it will be slow.

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; using WealthLab.AdvancedSmoothers; using WealthLab.TASC; namespace WealthScript1 {    public class RSIAgita : UserStrategyBase    {       public RSIAgita()       {          AddParameter("RSI Period", ParameterType.Int32, 14, 7, 42, 7);          AddParameter("Oversold", ParameterType.Int32, 25, 10, 60, 5);          AddParameter("Overbought", ParameterType.Int32, 60, 10, 80, 5);       }       //create indicators and other objects here, this is executed prior to the main trading loop       public override void Initialize(BarHistory bars)       {          period = Parameters[0].AsInt;          oversold = Parameters[1].AsInt;          overbought = Parameters[2].AsInt;          //Create and plot RSI          rsi = RSI.Series(bars.Close, period);          PlotTimeSeries(rsi, "rsi", "RSI", WLColor.Blue);          DrawHorzLine(oversold, WLColor.Red, 2, LineStyle.Dotted, "RSI");          DrawHorzLine(overbought, WLColor.Green, 2, LineStyle.Dotted, "RSI");          StartIndex = period + 1;          eq = new TimeSeries(bars.DateTimes);          eq.Description = "Equity";          PlotTimeSeries(eq, "Equity", "Equity", WLColor.Green, PlotStyle.ThickHistogram);       }       //execute the strategy rules here, this is executed once for each bar in the backtest history       public override void Execute(BarHistory bars, int idx)       {          eq[idx] = CurrentEquity;          //Entries are allowed only if price is below last entry price          double lastEntryPrice = Double.MaxValue;          if (LastPosition != null)             if (LastPosition.IsOpen)                lastEntryPrice = LastPosition.EntryPrice;          //Check penetration of RSI below levels 35 down to 5          level = oversold;          while (level > 0)          {             if (rsi.CrossesUnder(level, idx))                if (bars.Close[idx] < lastEntryPrice)                   PlaceTrade(bars, TransactionType.Buy, OrderType.Market);             level -= 5;          }          //Exit all long positions if RSI crosses above 45          if (rsi.CrossesOver(overbought, idx))             foreach (Position pos in GetPositions())                if (pos.IsOpen)                   ClosePosition(pos, OrderType.Market);       } //calculate and plot MAMA/FAMA of equity curve public override void Cleanup(BarHistory bars) {          MAMA mama = MAMA.Series(eq);          FAMA fama = FAMA.Series(eq);          PlotIndicator(mama, WLColor.DeepSkyBlue, PlotStyle.Line, false, "Equity");          PlotIndicator(fama, WLColor.CadetBlue, PlotStyle.Line, false, "Equity"); } public override void BacktestComplete()       {          List<Position> positions = GetPositionsAllSymbols(true);          foreach(Position pos in positions)             if (pos.NSF)                WriteToDebugLog(pos.ToString());       }       //declare private variables below       int oversold, overbought, level, period;       RSI rsi;       private TimeSeries eq;    } }
0
- ago
#13
Your proposed workaround eliminates the possibility of using the MAMA-FAMA Equity-Curve ratio to make trade decisions. This is actually exactly what I need.
0
- ago
#14
Glitch,

I have ran your strategy. Very cool. I see the graphics for the equity curve with sma, rsi, etc.

Is Wealthlab able to include normal buy and sell signals and then also incorporate the trading curve equity, so that signals can not be taken, when the close is below the equity curve sma? Also, show this on the chart?

If this code can do this, can you please point that out, or have an example of code that can trade the Equity Curve.

I know, that the advanced position sizer has trade equity option, but I am looking for a code example on this.

I want to run backtesting with optimized variable on trading the equity curve. I am looking to visually display the equity curve and sma of the equity curve on the chart with appropriate buy/sell signals using the equity curve filter.

Thank you,
Larry
0
- ago
#15
I ran the code above, but I do not see a filter to say, if equity curve is below (let's say 50 day of sma of equity curve, don't take the trade.

Am I missing something? Can someone show me the buy signal or filter to actually take the trade by looking at the equity curve?

Thank you,
Larry
0
- ago
#16
There is the smaEq time series in the code (Post #9) that is the SMA of the equity curve. You can access it by index (idx) as you would do with any other time series in your Strategy code, isn't it?
0
- ago
#17
QUOTE:
... if equity curve is below let's say 50-day SMA of equity curve, don't take the trade.
In other words, only take the trade if the equity curve is above the SMA of the equity curve.

QUOTE:
There is the smaEq time series in the code (Post #9) that is the SMA of the equity curve.

Right, so you simply need to add one more IF condition.
CODE:
   if (eq[idx] > smaEq[idx])       PlaceTrade(bars,TransactionType.Buy,OrderType.Market);
The smaEq in Post #9 is using 20 as its period instead of 50, but you can change that. I would avoid using indicators without the .Value method for inefficiency reasons, but the Post #9 example of using the T3 indicator shows how it could still be done if you don't care about efficiency.

I often "thought" about writing a Performance Visualizer with a checkbox that would save to disk the most recent equity curve slope data for post processing on a stat package (for correlating it with stock-screener critic recommendations). And one could read that saved data into the MyStrategy constructor if they wanted to. I'm still giving that idea some "thought".
1
- ago
#18
Superticker,

That worked.

Thank you,
Larry
0
- ago
#19
QUOTE:
Superticker,
That worked.

That answers your question, but the comment made in another topic suggests when you don't take the trade, you stop computing new valid values for the equity curve. You effectively invalidate your simulation.

What really needs to be done is to always take the trade in the simulation so the equity curve stays up-to-date, but actually not take the trade in the Order manager. Perhaps you could add a warning in the signal message saying to avoid this trade.

It was also suggested to employ a PosSizer for this task instead, so the trade is still taken (to update the equity curve with new values), but the size of the position is greatly reduced. I'm still giving that approach some "thought". I think the suggestion in the previous paragraph is better.
0

Reply

Bookmark

Sort