- ago
Hello,
I like the Sample Strategy "Tactical Asset Rotation" as a base for a portfolio rotation system. The system buys always the top 3 stocks (and hence directly sells worse ones).
I would like to extend it with the functionality to not directly sell a stock in case it's not in the buy list (TOP3) anymore, but instead to sell it if the rank of the stock is worse than a specific value. E.g. only if the rank of a stock got worse than 10, it shall get sold and instead a stock with the next best rank in the TOP3 (which has not already a long position) shall get bought.

So in short: stocks to be bought shall always be in the current TOP3; Selling shall only happen when stock is worse then TOP10.

It would be great if you could guide me in the right direction how to realize it.
- E.g. what should be realized in the PreExecute function and what in the Execute function?
- Do I need an extra sells-list additionally to the buys-list?
- Should the PlaceTrace(Buy) being executed in the same Execute()-Run, where the PlaceTrade(Sell) happens? Then the PlaceTrade(Buy) would needed to be called with a BarHistory-parameter different then the 'bars' given by the current Execute()-Run

Some help and guidance would really be appreciated.

Thanks & best regards
Kejo
0
367
Solved
1 Replies

Reply

Bookmark

Sort
Cone8
 ( 23.69% )
- ago
#1
This version eliminates the code in Execute(), moving it all to PreExecute(). There some debug output that keeps track of open positions in the top 10 and shows when a new symbols is added -

CODE:
using System; using WealthLab.Core; using WealthLab.Backtest; using WealthLab.Indicators; using System.Drawing; using System.Collections.Generic; namespace WealthScript123 {    public class TacticalAssetRotation_20230402: UserStrategyBase    {       //declare private variables below       private TimeSeries avgROC;       private string seriesKey = "Average ROC";       //the list of symbols that we should buy each bar       private static List<BarHistory> buys = new List<BarHistory>();       //create the weight indicator and stash it into the BarHistory object for reference in PreExecute       public override void Initialize(BarHistory bars)       {          avgROC = (ROC.Series(bars.Close, 60) + ROC.Series(bars.Close, 120) + ROC.Series(bars.Close, 200)) / 3;          bars.Cache[seriesKey] = avgROC;       }       //this is called prior to the Execute loop, determine which symbols have the lowest average ROC       public override void PreExecute(DateTime dt, List<BarHistory> participants)       {          //store the symbols' AvgROC value in their BarHistory instances          foreach (BarHistory bh in participants)          {             TimeSeries symbolRoc = (TimeSeries)bh.Cache[seriesKey];             int idx = GetCurrentIndex(bh); //this returns the index of the BarHistory for the bar currently being processed             double rocVal = symbolRoc[idx];             //if the indicator isn't valid set a high value to avoid selection in rotation             if (idx < symbolRoc.FirstValidIndex)                rocVal = 1.0e6;             bh.UserData = rocVal; //save the current AvgROC value along with the BarHistory instance          }          // everything is the same in PreExecute up to the .Sort...                    //sort the participants by AvgROC value (lowest to highest)          participants.Sort((a, b) => a.UserDataAsDouble.CompareTo(b.UserDataAsDouble));          buys.Clear();          string msg = "";                    // if a symbol in the top 10 match an open symbol, add its BarHistory to buys          foreach (Position position in OpenPositionsAllSymbols)          {                         for (int n = 0; n < 10; n++)                {                if (n >= participants.Count)                   break;                                if (participants[n].Symbol == position.Symbol)                {                   buys.Add(participants[n]);                   msg += " " + position.Symbol + "\t";                }             }                          if (buys.Count >= 3)                break;          }                    // if a slot is left, add the highest priority if it's not already in buys (need to check the top 3)          for (int n = 0; n < 3; n++)          {             if (buys.Count >= 3)                break;                          if (n >= participants.Count)                break;             if (!buys.Contains(participants[n]))             {                buys.Add(participants[n]);                msg += "+" + participants[n].Symbol + "\t";             }          }                   WriteToDebugLog(dt.ToString("yyyy-MM-dd") + "\t" + msg, false);                              // sell any open symbol not in the buy list          foreach (Position pos in OpenPositionsAllSymbols)          {             if (!buys.Contains(pos.Bars))                ClosePosition(pos, OrderType.Market);          }          // buy symbols that aren't already open          foreach (BarHistory   bh in buys)          {             bool buyIt = true;             foreach (Position pos in OpenPositionsAllSymbols)             {                if (pos.Symbol == bh.Symbol)                {                   buyIt = false;                   break;                }             }                          if (buyIt)                PlaceTrade(bh, TransactionType.Buy, OrderType.Market);          }                                   }       //execute the strategy rules here, this is executed once for each bar in the backtest history       public override void Execute(BarHistory bars, int idx)       {       }    } }
0
Best Answer

Reply

Bookmark

Sort