@Glitch - took me a while to nail this one down, but here's a minimal script with the failure (and working) scenario(s).
1. Run with All Data on TQQQ daily bars. The example works for TQQQ only.
2. There are 4 identical exit scenarios that Sell at MarketClose and Sell at Limit. The only difference between them is the assignment of OpenQuantity to Transaction.Quantity. The Limit order should always fill on the Open.
3. Run the Example, click though the Scenarios with the parameter 0 to 3. Scenario 1 is wrong.
1. Run with All Data on TQQQ daily bars. The example works for TQQQ only.
2. There are 4 identical exit scenarios that Sell at MarketClose and Sell at Limit. The only difference between them is the assignment of OpenQuantity to Transaction.Quantity. The Limit order should always fill on the Open.
3. Run the Example, click though the Scenarios with the parameter 0 to 3. Scenario 1 is wrong.
CODE:
using System; using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Data; namespace WealthScript10 { public class ErroneousMOC : UserStrategyBase { Parameter _scenario; public ErroneousMOC() { _scenario = AddParameter("Scenario", ParameterType.Int32, 0, 0, 1); } // **** EXAMPLE with limit order FOR TQQQ ONLY **** public override void Initialize(BarHistory bars) { PlotStopsAndLimits(3); } public override void Execute(BarHistory bars, int idx) { if (!HasOpenPosition(bars, PositionType.Long)) { if (bars.DateTimes[idx] == _dte) { _t = PlaceTrade(bars, TransactionType.Buy, OrderType.Market); } } else { Position p = LastPosition; if (bars.DateTimes[idx] == _dteX) { if (_scenario.AsInt == 0) { // works correctly _t = PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "MOC"); _t = PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, 4.10, "LMT"); // Limit triggers at the open } else if (_scenario.AsInt == 1) { // *** PROBLEM SCENARIO - misses the limit and sells at close *** _t = PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "MOC-t.Quantity"); _t.Quantity = OpenQuantity; _t = PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, 4.10, "LMT"); // Limit triggers at the open } else if (_scenario.AsInt == 2) { // works correctly _t = PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "MOC-t.Quantity"); _t.Quantity = OpenQuantity; _t = PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, 4.10, "LMT"); // Limit triggers at the open _t.Quantity = OpenQuantity; } else if (_scenario.AsInt == 3) { // works correctly _t = PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "MOC"); _t = PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, 4.10, "LMT"); // Limit triggers at the open _t.Quantity = OpenQuantity; } } } } Transaction _t; DateTime _dte = new DateTime(2015, 1, 16); DateTime _dteX = new DateTime(2015, 1, 22); } }
Rename
... and there are more scenarios, apparently.
After converting a strategy with Market, Limit, Stop, and MarketClose to assign the OpenQuantity to the Sell Transaction.Quantity (without modifying anything else) , the result changed drastically.
After converting a strategy with Market, Limit, Stop, and MarketClose to assign the OpenQuantity to the Sell Transaction.Quantity (without modifying anything else) , the result changed drastically.
The Backtester processes orders that have Quantity assigned in a separate step. Since you're only assigning the Quantity to one of the Transactions, it's processed separately from the other Transaction, resulting in this side effect. I'll add this to the bug list, but for now the workaround is to assign the Quantity to the other, second, Transaction as well.
I made a change for Build 140 that resolves the issue.
That case works, thanks.
The problem is that I have a far more complex strategy that assigns the manual quantity for all the exits, but it still changes the result drastically. I'll try to isolate it - it's likely I messed up something else while doing that modification.
The problem is that I have a far more complex strategy that assigns the manual quantity for all the exits, but it still changes the result drastically. I'll try to isolate it - it's likely I messed up something else while doing that modification.
It wasn't me after all. This one is insidious!
1. Use the parameter to run both identical scenarios on TQQQ.
2. Load Year 2025 and watch the trade at the start of the chart.
3. See the debug - when assigning the Quantity to the Limit order, HasOpenPosition() returns false on the next bar!
1. Use the parameter to run both identical scenarios on TQQQ.
2. Load Year 2025 and watch the trade at the start of the chart.
3. See the debug - when assigning the Quantity to the Limit order, HasOpenPosition() returns false on the next bar!
CODE:
using System; using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Data; namespace WealthScript15 { public class ErroneousMOC2 : UserStrategyBase { Parameter _scenario; public ErroneousMOC2() { _scenario = AddParameter("Scenario", ParameterType.Int32, 0, 0, 1); } // **** EXAMPLE with limit order FOR TQQQ ONLY **** public override void Initialize(BarHistory bars) { PlotStopsAndLimits(3); } public override void Execute(BarHistory bars, int idx) { if (!HasOpenPosition(bars, PositionType.Long)) { WriteToDebugLog($"{idx} Long Position NOT Open"); if (bars.DateTimes[idx] == _dte) { _t = PlaceTrade(bars, TransactionType.Buy, OrderType.Market); } } else { WriteToDebugLog($"{idx} Long Position Open"); Position p = LastPosition; if (_scenario.AsInt == 0) { _t = PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, p.EntryPrice * 1.05); if (bars.DateTimes[idx] == _dteX) { _t = PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "MOC"); } } else if (_scenario.AsInt == 1) { // *** PROBLEM SCENARIO - HasOpenPosition FAILS *** _t = PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, p.EntryPrice * 1.05); _t.Quantity = this.OpenQuantity; // just comment this out and it works if (bars.DateTimes[idx] == _dteX) { _t = PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "MOC"); _t.Quantity = OpenQuantity; } } } } Transaction _t; DateTime _dte = new DateTime(2025, 1, 3); DateTime _dteX = new DateTime(2025, 1, 7); } }
I have this case fixed for Build 140 as well.
Your Response
Post
Edit Post
Login is required