- ago
Hi Wealth-Lab community,

I am attempting to code a trend following strategy that provides signals on an end of week basis, with exits on a daily basis. However, I have the problem that what is bought on the Monday is not what is listed in the end of weeks signals list.

I use a daily data scale on the Strategy Settings. To determine weekly buys the bars are first compressed to a weekly timeframe.

BarHistory BarsWeekly = BarHistoryCompressor.ToWeekly(bars);

I use BarsWeekly to create buy logic before using the synchronize method to revert to the daily timeframe. A daily TimeSeries dataset has also been created which has a 1 for an end of week bar and 0 otherwise. That when multiplied with the expanded/synchronized filter logic provides signals only at the end of the week.

BuyFilter1Daily = TimeSeriesSynchronizer.Synchronize(BuyFilter1Weekly, bars.Close) * EndOfWeekFilter;
BuyFilter2Daily = TimeSeriesSynchronizer.Synchronize(BuyFilter2Weekly, bars.Close) * EndOfWeekFilter;
Etc..

For the execute method:
CODE:
if ((!HasOpenPosition(bars, PositionType.Long)) ) { if(BuyFilter1Daily[index] == 1)    {       if(BuyFilter2Daily[index] == 1)       {          BuyTransaction = PlaceTrade(bars, TransactionType.Buy, OrderType.Market, 0, "Buy");       }       } }


I have plotted the various Timeseries arrays and can see all is working as expected. With buy signals given on the last trading day of the week. However, come Monday, the backtester includes one or more buys that were not in the signal list. Any thoughts on what I could be doing incorrectly?
0
149
Solved
13 Replies

Reply

Bookmark

Sort
Cone8
 ( 6.69% )
- ago
#1
QUOTE:
I use BarsWeekly to create buy logic before using the synchronize method
This the problem. Trading needs to happen in the base time scale.

There are a few ways to do it, but weekly crossovers and comparisons will work after synchronizing back to Daily too - and they'll happen on the last day of the week, which is what you want.

Your filter indexing may have a bug too but it's not required at all.
0
- ago
#2
Hi Cone, thanks for your feedback.

My prior approach was: Indicators to Weekly -> Buy logic in weekly timeframe -> Convert logic to daily.
I have now changed the approach based on your suggestion. First creating weekly indictors -> Convert/sync indicators back to daily timeframe -> determine buy logic.

However… both exhibit the same problem, with what is bought not prior end of week signal list.

Could you please expand on this comment "Your filter indexing may have a bug too but it's not required at all."
Any other ideas I could try?
0
Cone8
 ( 6.69% )
- ago
#3
I'm just guessing about indexing because I don't have your code and you're working with multiple scales.

Show me your code, and I'll tell you the problem.
0
- ago
#4
I have reduced the code for fault finding to the following:
Within the initialize method:

CODE:
BarHistory BarsWeekly = BarHistoryCompressor.ToWeekly(bars); TimeSeries ShortTermMAWeekly = EMA.Series(BarsWeekly.Close, 10); TimeSeries LongTermMAWeekly = SMA.Series(BarsWeekly.Close, 50); TimeSeries HighestHighWeekly = CalculateHighestHigh(BarsWeekly.Close, 60); BarsDaily = BarHistorySynchronizer.Synchronize(BarsWeekly, bars); TimeSeries ShortTermMADaily = TimeSeriesSynchronizer.Synchronize(ShortTermMAWeekly, bars); TimeSeries LongTermMADaily = TimeSeriesSynchronizer.Synchronize(LongTermMAWeekly, bars); LongFilter = ShortTermMADaily > LongTermMADaily; HighestHighFilter = TimeSeriesSynchronizer.Synchronize(HighestHighWeekly, bars); EndOfWeekFilter = CalculateWeekEnd(bars);


I have created a separate method to calculate a timeseries array with the highhest breakout bar:

CODE:
private static TimeSeries CalculateHighestHigh(TimeSeries BarsClose, int BreakoutPeriod) { TimeSeries HighestHigh = new TimeSeries(BarsClose.DateTimes); for (int counter = 0; counter < BarsClose.Count; counter++) { if (counter > 0)     HighestHigh[counter] = (BarsClose[counter] > BarsClose.GetHighest(counter - 1, BreakoutPeriod)) ? 1 : 0; } return (HighestHigh); }


I have a separate method to create a TimeSeries array that provides end of week indication:

CODE:
private static TimeSeries CalculateWeekEnd(BarHistory bars) { TimeSeries IsEndOfWeek = new TimeSeries(bars.DateTimes); for (int counter = 0; counter < bars.Count; counter++) { IsEndOfWeek[counter] = (bars.DateTimes[counter].IsLastTradingDayOfWeek(bars)) ? 1 : 0;           } return (IsEndOfWeek); }


Then within the execute method, I just check each of the filter arrays and place a trade if each is one/true on the same bar.
0
Cone8
 ( 6.69% )
- ago
#5
You showed me everything except the part with the problem - the logic to generate the signal.

Edit - I just remembered the snippet in the first post.. reviewing..
.. and nevermind. There you used two more undefined series (BuyFilter1Daily and BuyFilter2Daily) with an undefined index parameter.

Anyway, a couple unrelated comments on the other snippets -

You're using an EMA indicator with Weekly bars. Make sure you fix a Starting Date in the Strategy Settings or your backtest results will change. EMA is one of those indicators that slightly for the same date if the start date isn't constant. F1 Help > Indicators > Stability of Indicators.

You don't need CalculateHighestHigh. Just use Highest.Series().
CODE:
TimeSeries HighestHighWeekly = Highest.Series(BarsWeekly.Close, 60);

How is BarsDaily used?
In reality, BarsDaily is Weekly bars synchronized to the daily base scale.
0
- ago
#6
Yes buy logic is constructed like prior post:

CODE:
public override void Execute(BarHistory bars, int index) { if (!HasOpenPosition(bars, PositionType.Long)) { if (LongFilter[index] == 1)     {     if (HighestHighFilter[index] == 1)       {        if (EndOfWeekFilter[index] == 1)        {        BuyTransaction = PlaceTrade(bars, TransactionType.Buy, OrderType.Market, 0, "Buy");           BuyTransaction.Weight = 1000 - BarsDaily.Close[index]; // Lowest weekly close          }        }     } } else {       // Sell logic } }
0
Cone8
 ( 6.69% )
- ago
#7
Voila. HighestHighFilter will [almost] never equal 1.
0
- ago
#8
I get plenty of buy signals and can create a compounding equity curve. I have also plotted the outcome and can see that it signals 1 when there is a highest close within the breakout period.
0
Cone8
 ( 6.69% )
- ago
#9
You're right. In my mind I had replaced your CalculateHighestHigh() with Highest.Series(), but now I see they're not the same thing. Making all these binary series isn't really required when you can just test for the condition directly.

Anyway, it looks like the Highest indicator in CalculateHighestHigh() is not being synchronized correctly when it's the final bar - which may mean that Weekly series in general are not synchronized correctly on the final bar.

Sorry for the error and thanks for reporting it! We'll get this fixed ASAP.
0
- ago
#10
Ok glad you found something, your feedback is appreciated. I was struggling to find what else I could be doing wrong for what is relatively simple code.

I updated the code to now use Highest.Series.

I took the binary series approach as I felt I could ensure the logic was doing what I wanted it to, as I can test the outcome using PlotTimeSeries. Is there a way I can directly compare and plot an outcome?
0
Cone8
 ( 6.69% )
- ago
#11
Let's stick to this for now - I want to give you a work-around to get you going.

Replace your CalculateHighestHigh() method with this code:
CODE:
      private double _lastHH = 0;       private TimeSeries CalculateHighestHigh(TimeSeries BarsClose, int BreakoutPeriod)       {                   Highest hh = Highest.Series(BarsClose, BreakoutPeriod);          TimeSeries result = hh > (hh >> 1);          _lastHH = result.LastValue;          return (result);       }


Now after the line that Synchronizes HighestHighFilter, add this statement.
CODE:
HighestHighFilter[bars.Count - 1] = _lastHH;

That should work.
0
Cone8
 ( 6.69% )
- ago
#12
Tracked it down. Forget that workaround. Just do this:

CODE:
//IS: HighestHighWeekly2 = TimeSeriesSynchronizer.Synchronize(HighestHighWeekly2, bars); //CHANGE TO: HighestHighWeekly2 = TimeSeriesSynchronizer.Synchronize(HighestHighWeekly2, bars, null, bars.Market);
Even though you passed a BarHistory as the TimeSeriesBase master, we weren't using it to get the market (but will do it starting with Build 111).
0
Best Answer
- ago
#13
I included ", bars, null, bars.Market" to all TimeSeriesSynchronizer.Synchronize statements. Everything is working as expected now.

Thanks Cone!
1

Reply

Bookmark

Sort