Problem to code filter in rotational trading model

Hello everybody - I try implement a filter to only trade equitites with the Close > MA 200 during at least 78 % of the past 1280 days.

My code looks like this:

// Close during at least 78 % of time > MA 200 in past 1280 days?
Rise = C > MA( Close, 200 ); // "1" if Close bigger than MA 200, else "0"
TotalRise = Sum( Rise , 1280 ); // Sum up in how many of the past 1280 days this happened
GDSum = TotalRise * 100 / 1280 ; // Transform this to percent
GDSumPct = GDSum > 78; // Condition: If > 78 %, Result = "1"

MyFilter = GDSumPct;
// MyFilter = PeriodStart;
// MyFilter = PeriodStart AND GDSumPct ;

MyScore = Close / MA(Close,1280);

PositionScore = IIf( MyFilter, MyScore, scoreNoRotate ); 
// Rotation, scoreNoRotate = already open trades should be kept and no new trades entered

The model doesn't trade. Looking where the mistake might be I came to two posts in the forum from @beppe and @fxshrat:
http//forum.amibroker.com/t/problems-coding-counters-based-on-conditions/7133/2
https://forum.amibroker.com/t/filter-for-rotational-system/21609/3

Comparing with these everything seems to be ok.

May I kindly ask an experienced programmer to have a look at the code and maybe explain to me where something is wrong in the logic for the filter I try to use?

To get better understanding of what is happening in your code and how functions work, use advice given here: How do I debug my formula?

Hint: use Exploration to display MA( Close, 1280 ) and all variables.

You should at least enable Rotational trading. Just adding PositionScore is not enough.
Old function is EnableRotationalTrading. New way is using SetBacktestMode( backtestRotational ).

SetBacktestMode( backtestRotational );

SetPositionSize(100, spsShares);

// Close during at least 78 % of time > MA 200 in past 1280 days?
Rise = C > MA( Close, 200 ); // "1" if Close bigger than MA 200, else "0"
TotalRise = Sum( Rise , 1280 ); // Sum up in how many of the past 1280 days this happened
GDSum = TotalRise * 100 / 1280 ; // Transform this to percent
GDSumPct = GDSum > 78; // Condition: If > 78 %, Result = "1"

MyFilter = GDSumPct;
// MyFilter = PeriodStart;
// MyFilter = PeriodStart AND GDSumPct ;

MyScore = Close / MA(Close,1280);

PositionScore = IIf( MyFilter, MyScore, scoreNoRotate ); 
// Rotation, scoreNoRotate = already open trades should be kept and no new trades entered

Next check your data since you have set lookback period of 1280 bars. Is your history sufficient enough? Check periodicity. etc...

1 Like

Hi @fxshrat and @Tomasz - Thank you for your coaching

I added the necessary code to use exploration. The learnings are as follows:

  1. The code in the lines 31 - 37 (Calculation of GDSumPct) works as expected.
  2. With the Filter "PeriodStart" from line 40 the Backtest runs as expected and Exploration shows the expected items.
    grafik
  3. When I change the Filter to "GDSumPcT" (line 39), the system doesn't trade and Explorer shows empy rows:
    grafik

Now, I'm really irritated. Do you know from where this might come from?

Here is the full code I used:

// Formula 17, Revision 3, February 2021
// 
// Code for Exploration:
Filter = 1; // show all bars
//

SetBacktestMode( backtestRotational );
SetOption("MaxOpenPositions",10);
SetPositionSize( 10, spsPercentOfEquity );

// Timeframe for rotation, applied learning from
// https://forum.amibroker.com/t/how-to-identify-and-define-trading-days/23630/3

mth = Month();
dow = DayOfWeek();
mth_mod3 = mth % 3;
hy = mth % 6;

NewWeek = dow < Ref(dow,-1);
NewMonth = mth != Ref(mth,-1);
NewQuarter = mth_mod3 * !Ref(mth_mod3,-1);
NewHalfyear = hy * !Ref(hy,-1);
NewYear = Year() != Ref(Year(), -1);

// PeriodStart = NewWeek;
// PeriodStart = NewMonth;
// PeriodStart = NewQuarter;
PeriodStart = NewHalfyear;
// PeriodStart = NewYear;

// Close during at least 78 % of time > MA 200 in past 1280 days?
Rise = C > MA( Close, 200 ); // "1" if Close bigger than MA 200, else "0"
TotalRise = Sum( Rise , 1280 ); // Sum up in how many of the past 1280 days this happened
GDSum = TotalRise * 100 / 1280 ; // Calculate this in percent
GDSumPct = GDSum > 78; // Condition: If > 78 %, Result = "1"

// MyFilter = GDSumPct;
MyFilter = PeriodStart;
// MyFilter = PeriodStart AND GDSumPct ;

MyScore = (Close / MA(Close,1280));

// For the filter and scoring found in the forum:
// From Beppe: http//forum.amibroker.com/t/problems-coding-counters-based-on-conditions/7133/2
// From fxshrat: https://forum.amibroker.com/t/filter-for-rotational-system/21609/3

PositionScore = IIf( MyFilter, MyScore, scoreNoRotate ); 

// Code for Exploration:
AddColumn( C, "Close" );
AddColumn( Rise, "Rise" );
AddColumn( TotalRise, "TotalRise");
AddColumn( GDSum, "GDSum" );
AddColumn( GDSumPct, "GDSumPct" );
AddColumn( MyScore, "MyScore");
AddColumn( PeriodStart, "PeriodStart" );
AddColumn( MyFilter, "MyFilter" );
AddColumn( PositionScore, "PositionScore");

Do you know from where this might come from?

It is because of scoreNoRotate... it is active right at the start of your analysis range.

So there aren't any new trades opened because there is at least one symbol with "do not rotate" score at start of range.. scoreNoRotate is equal to value 1e11 (Enable DETAIILED LOG in Analysis settings.) So it (scoreNoRotate) tops any other score in the ranked list.

That's why you have empty result list (no new trades entered).

Simply lower

GDSumPct = GDSum > 78;

to e.g.

GDSumPct = GDSum > 0;

then you will see that result list will not be empty.

1 Like

Yes, important thing to understand is that storeNoRotate has "global" meaning, which means it stops rotation if it occurs on ANY of symbols under test - just one symbol having this value stops rotation completely (no new entries and no exits).

Hi @Tomasz, hi @fxshrat - Thank you for your explanations.

Reading this https://www.amibroker.com/guide/afl/enablerotationaltrading.html I came to the conclusion that scoreNoRotate keeps titles which are still under the e.g. TopTen to prevent unnessary buy/sell transactions.
With your explanation it is clear how the function works.
@Beppe shows in this post https://forum.amibroker.com/t/how-to-sell-all-titles-after-a-defined-period/23853/4 how it is possible to force the system to sell all titles on a defined day in the future. I will continue with this using standard AFL; that's ok.

@RetiTrader, about a month ago, there was a presentation titled "Dealing With Dates for Monthly Rotation Strategies", at the online meetings of the Amibroker Canada User Group*.
It is about an alternative - somewhat laborious - approach that could interest any trader dealing with "future" dates (with a specific offset from the beginning of the month).

Here you can find a detailed article, from host Dave di Marcantonio (@DaveDM), who explains it.

* If you register for free on the ABC User Group, you can access all recordings of past presentations from a dedicated section of their forum.

5 Likes

Thank you, @beppe , for this inspiring link!

1 Like

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.