- ago
Hey!

I am trying to write a script that trades several substrategies on futures. Can somebody guide me to the easiest possible way to have WealthLab automatically place positions in the "correct" future month (almost always the nearest one) and roll over current positions to the nearest future month in case the current contract nears expiry?
0
261
Solved
2 Replies

Reply

Bookmark

Sort
Cone8
 ( 3.60% )
- ago
#1
I was about to tell you there is no "easy way", but I just thought of something that we didn't create for this purpose, but it could work well - You can make custom dynamic DataSets in WealthLab.

You can make any DataSet "dynamic" on the last wizard page of the DataSet configuration. You specify the dates you want each symbol to be included in backtests run on the DataSet (live trading is a backtest too). When the end date is reached, any position in that symbol is exited automatically.

Here's an example for a DataSet containing ES contracts. I set the start date of each contract to be the Friday before expiration of the previous contract.



Then I ran a Buy and Hold strategy on that DataSet -



As you can see, the dates for entry and exits are actually 1 day (1 bar) after the date specified in the dynamic DataSet configuration. Consequently, that date is the "signal bar".

So exiting positions, is "easy" because it's automatic. The problem is to program your strategy to pick up a new position when that happens.
1
Best Answer
Cone8
 ( 3.60% )
- ago
#2
Here's a helper script to automatically create that list of ranges.

Contraints
Dynamic DataSets work with a "non-Intraday" Date. Consequently, for an open position, an exit signal would occur on the last bar of the

Requirements
1. A DataSet of contracts for a single instrument.
2. Contract symbols must have 2-digit years, e.g., ESU24, ESZ24, ESH25
3. Assumes 3rd Friday expiration. For other expirations, you need to change the GetExpiration() method in the FuturesData class.

Procedure
1. Change DataSetName string on line 12 to the name containing the contract symbols.
2. Run the script on any one symbol.
3. Copy the result from the debug window.
4. Right click your DataSet, Configure..., click to the last wizard page, check "Make this DataSet Dynamic.
5. Paste the result from step 3 into Define Data Ranges.
6. Click Finished

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using System.Collections.Generic; using System.Linq; namespace WealthScript5 { public class RolloverHelper : UserStrategyBase {       // change DataSetName to the one with the contract symbols       const string DataSetName = "iq_contracts_ES";          Parameter _daysbefore;              public RolloverHelper()       {          _daysbefore = AddParameter("", ParameterType.Double, 8, 1, 14, 1);                } public override void Initialize(BarHistory bars) {          DataSet dset = WLHost.Instance.FindDataSet(DataSetName);          var sortedList = dset.Symbols.OrderBy(item =>          {             // Extract the last two digits, if there are any, otherwise use 0             if (int.TryParse(item.Substring(Math.Max(0, item.Length - 2)), out int lastTwo))                return lastTwo;             else                return 0;          })          .ThenBy(item => item[item.Length - 1]); // Sort by the last character          string sym1 = "";          foreach (var symbol in sortedList)          {             if (bars.SecurityType != SecurityType.Future)                throw new Exception($"{symbol} is not set up as a futures Symbol.");             FutureData data = new FutureData(symbol, _daysbefore.AsInt, sym1);             WriteToDebugLog(data.ToString());             sym1 = symbol;          } }     public override void Execute(BarHistory bars, int idx) { }        }        public class FutureData    {       public static List<string> MonthCodesList = new List<string> { "F", "G", "H", "J", "K", "M", "N", "Q", "U", "V", "X", "Z" };       string Symbol { get; set; }       DateTime StartDate { get; set; }       DateTime EndDate { get; set; }              public FutureData(string symbol, int daysBeforeExpiration, string previousSymbol = "")       {          // assumes 2-digit year, e.g., ESZ24, ESH25          Symbol = symbol;          DateTime exp = GetExpiration(symbol);          int deltaMonths = 1;          if (previousSymbol == "")             deltaMonths = 3;          else          {             DateTime exp1 = GetExpiration(previousSymbol);             TimeSpan mnths = (exp - exp1);             deltaMonths = (int)(mnths.TotalDays / 30);          }          EndDate = exp.AddDays(-8);          StartDate = DateTimeExtensions.ThirdFriday(EndDate.AddMonths(-deltaMonths)).AddDays(-daysBeforeExpiration);             }              public DateTime GetExpiration(string symbol)       {          string rootSymbol = symbol.Substring(0, symbol.Length - 3);          string sMonth = symbol.Substring(symbol.Length - 3, 1);          string syr = "20" + symbol.Substring(symbol.Length - 2);          int iyr = int.Parse(syr);          int imnth = MonthCodesList.IndexOf(sMonth) + 1;          return DateTimeExtensions.ThirdFriday(new DateTime(iyr, imnth, 1));       } public override string ToString() {          return $"{Symbol},{StartDate:M/d/yyyy},{EndDate:M/d/yyyy}"; } } }


Sample output -
CODE:
@ESH08,12/13/2007,3/13/2008 @ESM08,3/13/2008,6/12/2008 @ESU08,6/12/2008,9/11/2008 @ESZ08,9/11/2008,12/11/2008 @ESH09,12/11/2008,3/12/2009 @ESM09,3/12/2009,6/11/2009 @ESU09,6/11/2009,9/10/2009 @ESZ09,9/10/2009,12/10/2009 @ESH10,12/10/2009,3/11/2010 @ESM10,3/11/2010,6/10/2010 @ESU10,6/10/2010,9/9/2010 @ESZ10,9/9/2010,12/9/2010 @ESH11,12/9/2010,3/10/2011 @ESM11,3/10/2011,6/9/2011 @ESU11,6/9/2011,9/8/2011 @ESZ11,9/8/2011,12/8/2011 @ESH12,12/8/2011,3/8/2012 @ESM12,3/8/2012,6/7/2012 @ESU12,6/7/2012,9/13/2012 @ESZ12,9/13/2012,12/13/2012 @ESH13,1/10/2013,3/7/2013 @ESM13,3/7/2013,6/13/2013 @ESU13,6/13/2013,9/12/2013 @ESZ13,9/12/2013,12/12/2013 @ESH14,12/12/2013,3/13/2014 @ESM14,3/13/2014,6/12/2014 @ESU14,6/12/2014,9/11/2014 @ESZ14,9/11/2014,12/11/2014 @ESH15,12/11/2014,3/12/2015 @ESM15,3/12/2015,6/11/2015 @ESU15,6/11/2015,9/10/2015 @ESZ15,9/10/2015,12/10/2015 @ESH16,12/10/2015,3/10/2016 @ESM16,3/10/2016,6/9/2016 @ESU16,6/9/2016,9/8/2016 @ESZ16,9/8/2016,12/8/2016 @ESH17,12/8/2016,3/9/2017 @ESM17,3/9/2017,6/8/2017 @ESU17,6/8/2017,9/7/2017 @ESZ17,9/7/2017,12/7/2017 @ESH18,12/7/2017,3/8/2018 @ESM18,3/8/2018,6/7/2018 @ESU18,6/7/2018,9/13/2018 @ESZ18,9/13/2018,12/13/2018 @ESH19,1/10/2019,3/7/2019 @ESM19,3/7/2019,6/13/2019 @ESU19,6/13/2019,9/12/2019 @ESZ19,9/12/2019,12/12/2019 @ESH20,12/12/2019,3/12/2020 @ESM20,3/12/2020,6/11/2020 @ESU20,6/11/2020,9/10/2020 @ESZ20,9/10/2020,12/10/2020 @ESH21,12/10/2020,3/11/2021 @ESM21,3/11/2021,6/10/2021 @ESU21,6/10/2021,9/9/2021 @ESZ21,9/9/2021,12/9/2021 @ESH22,12/9/2021,3/10/2022 @ESM22,3/10/2022,6/9/2022 @ESU22,6/9/2022,9/8/2022 @ESZ22,9/8/2022,12/8/2022 @ESH23,12/8/2022,3/9/2023 @ESM23,3/9/2023,6/8/2023 @ESU23,6/8/2023,9/7/2023 @ESZ23,9/7/2023,12/7/2023 @ESH24,12/7/2023,3/7/2024 @ESM24,3/7/2024,6/13/2024 @ESU24,6/13/2024,9/12/2024 @ESZ24,9/12/2024,12/12/2024 @ESH25,12/12/2024,3/13/2025 @ESM25,3/13/2025,6/12/2025 @ESU25,6/12/2025,9/11/2025 @ESZ25,9/11/2025,12/11/2025
1

Reply

Bookmark

Sort