- ago
I recently found some strategies that trade a few tickers in a rotational way with complex conditions. For example https://www.reddit.com/r/LETFs/comments/1bn8o5t/backtesting_tqqq_for_the_long_term_tqqq_ftlt/. Rules for this strategy are:

Could this kind of setup easily done in WL? Major differences for this kind of strategy to what I usually use WL are:
1. Many if/else statements for rotations.
2. Testing tickers are fixed in the strategy instead of as something configurable outside of the strategy itself.

0
309
5 Replies

Reply

Bookmark

Sort
- ago
#1
This rather falls into the realm of pairs trading. It can effectively be coded in C#. You can check out an example called "Pairs Trading" in the Sample Strategies folder.

On second thought, it should be possible to come up in Blocks too with the help of Conditions such as "Symbol Filter" (that limits a Block to a list of symbols) and indicators like SymbolInd or SymbolData (which refer to external symbol's OHLC or Indicator).
0
- ago
#2
@Eugene, thanks for pointing me to the pairs trading code. With that I came up with the following implementation. But somehow it opened the first position but it never get closed, though there were many condition changes. Anything wrong here in the implementation? Thanks.

CODE:
using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Indicators; namespace WealthScript11 {    public class LeveragedETFStrategy : UserStrategyBase    {       BarHistory TQQQ, SPXL, UVXY, TECL, UPRO, SQQQ, TLT;       int RSIWindow = 10;       int MAPeriod = 200;       TimeSeries TQQQ_RSI, SPXL_RSI, SPY_RSI, SPY_MA, TQQQ_MA, TQQQ_Price, SPY_Price;       string previousCondition = "";       public override void Initialize(BarHistory bars)       {          TQQQ = GetHistory(bars, "TQQQ");          SPXL = GetHistory(bars, "SPXL");          UVXY = GetHistory(bars, "UVXY");          TECL = GetHistory(bars, "TECL");          UPRO = GetHistory(bars, "UPRO");          SQQQ = GetHistory(bars, "SQQQ");          TLT = GetHistory(bars, "TLT");          TQQQ_RSI = RSI.Series(TQQQ.Close, RSIWindow);          SPXL_RSI = RSI.Series(SPXL.Close, RSIWindow);          SPY_RSI = RSI.Series(bars.Close, RSIWindow);          SPY_MA = SMA.Series(bars.Close, MAPeriod);          TQQQ_MA = SMA.Series(TQQQ.Close, 20);          TQQQ_Price = TQQQ.Close;          SPY_Price = bars.Close;          PlotTimeSeries(TQQQ_RSI, "TQQQ RSI", "RatioPane", WLColor.AliceBlue);          PlotTimeSeries(SPXL_RSI, "SPXL RSI", "RatioPane", WLColor.Aqua);          PlotTimeSeries(SPY_RSI, "SPY RSI", "RatioPane", WLColor.Coral);          PlotTimeSeries(SPY_MA, "SPY 200MA", "Price", WLColor.AliceBlue);          StartIndex = MAPeriod;       }       public override void Execute(BarHistory bars, int idx)       {          // Determine the current condition          string currentCondition = DetermineCondition(idx);          WriteToDebugLog($"current conditon {currentCondition}, pre conditon {previousCondition}");          // If the condition has changed since the previous day, close all positions          if (currentCondition != previousCondition)          {             WriteToDebugLog($"prepare to close position for switch.");             CloseAllPositions();             OpenPositionsBasedOnCondition(currentCondition, idx);             previousCondition = currentCondition;          } else {             WriteToDebugLog($"No conditon change, no position change.");          }       }       private string DetermineCondition(int idx)       {          // Check if SPY is above its 200-day moving average          bool isSPYAbove200MA = SPY_Price[idx] > SPY_MA[idx];          if (isSPYAbove200MA)          {             if (TQQQ_RSI[idx] > 79) return "Buy UVXY";             if (SPXL_RSI[idx] > 80) return "Buy UVXY";             return "Buy TQQQ";          }          else          {             if (TQQQ_RSI[idx] < 31) return "Buy TECL";             if (SPY_RSI[idx] < 30) return "Buy UPRO";             if (TQQQ_Price[idx] < TQQQ_MA[idx])             {                if (TQQQ_RSI[idx] < 31) return "Buy SQQQ";                return "Buy TLT";             }             return "Buy TQQQ";          }       }       private void CloseAllPositions()       {          foreach (var position in GetPositions())          {             ClosePosition(position, OrderType.Market, 0, "Close " + position.Symbol);             WriteToDebugLog($"Closing position {position.Symbol}");          }       }       private void OpenPositionsBasedOnCondition(string condition, int idx)       {          // Open new positions based on the current condition          switch (condition)          {             case "Buy UVXY":                PlaceTrade(UVXY, TransactionType.Buy, OrderType.Market, 0, "Buy UVXY");                break;             case "Buy TQQQ":                PlaceTrade(TQQQ, TransactionType.Buy, OrderType.Market, 0, "Buy TQQQ");                break;             case "Buy TECL":                PlaceTrade(TECL, TransactionType.Buy, OrderType.Market, 0, "Buy TECL");                break;             case "Buy UPRO":                PlaceTrade(UPRO, TransactionType.Buy, OrderType.Market, 0, "Buy UPRO");                break;             case "Buy SQQQ":                PlaceTrade(SQQQ, TransactionType.Buy, OrderType.Market, 0, "Buy SQQQ");                break;             case "Buy TLT":                PlaceTrade(TLT, TransactionType.Buy, OrderType.Market, 0, "Buy TLT");                break;          }       }    } }
0
- ago
#3
Okey I figured it out, I should use GetPositionAllSymbols
0
- ago
#4
QUOTE:
I should use GetPositionAllSymbols

I think you just want the Open positions, so use OpenPositionsAllSymbols.

CODE:
foreach (Position position in OpenPositionsAllSymbols)
You can fetch all positions, but processing all the Closed positions will just unnecessarily slow WL down.
0
- ago
#5
@superticker: yes you are right. I used OpenPositionAllSymbols and did a filter to get all open positions but it's easier to just use the method you mentioned.
1

Reply

Bookmark

Sort