This is the guidance throughout this forum. Combine multiple systems into one strategy if you want to do this (or use multiple accounts, but I'm trying the combined script approach right now.)
I ran a simple test. (Strategy code below.)
* System 1 enters a Long 1 position at 9am.
* System 2 enters a Long 1 position at 10am.
* System 2 exits its Long 1 position at 11am.
* System 1 exits its Long 1 position at 12am.
The two system trades purposely overlap. System 2 enters and exits in the middle of system 1.
The chart ends up drawing the trades like this:
I realize that this is how the strategy position, or the account position, sees these trades.
Because the System 2 position closes before the System 1 position, it's charted as though the System 2 exit is the exit of the System 1 trade.
Is there a way to get the chart to display the trades the way the two systems see their trades?
In this case, our Sys2 trade would be drawn showing it open after Sys1 and close before Sys1.
using NinjaTrader.Cbi; using System; namespace NinjaTrader.NinjaScript.Strategies.Sims { /// <summary> /// This strategy tests using unmanaged market orders to enter two completely /// different trades which overlap in time. We want to see how OCO links entries /// and exits and how these trades draw on the chart. /// </summary> public class Sims2SimpleUnmanagedStrategy : Strategy { Order _entryOrder1; Order _entryOrder2; protected int Quantity => 1; protected string System1Name => "Sys1"; protected string System2Name => "Sys2"; protected override void OnBarUpdate() { if (CurrentBar < BarsRequiredToTrade) { return; } OnBarUpdate_System1(); OnBarUpdate_System2(); } protected void OnBarUpdate_System1() { if (CurrentBar < BarsRequiredToTrade) { return; } if ($"{Time[0]:HHmm}" == "0900") { SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, Quantity, 0, 0, Guid.NewGuid().ToString(), System1Name); } else if ($"{Time[0]:HHmm}" == "1200") { if (_entryOrder1 != null) { string oco = _entryOrder1.Oco; SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, Quantity, 0, 0, oco, System1Name); } } } protected void OnBarUpdate_System2() { if (CurrentBar < BarsRequiredToTrade) { return; } if ($"{Time[0]:HHmm}" == "1000") { SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, Quantity, 0, 0, Guid.NewGuid().ToString(), System2Name); } else if ($"{Time[0]:HHmm}" == "1100") { if (_entryOrder2 != null) { string oco = _entryOrder2.Oco; SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, Quantity, 0, 0, oco, System2Name); } } } protected override void OnExecutionUpdate(Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time) { Order o = execution.Order; bool isExit = execution.IsExit; if (isExit && _entryOrder1 != null && _entryOrder1 == o) { // Because we restrict Q=1 we know that any filled exit is a full // exit. We don't have to track the total quantity. if (o.Name == System1Name && o.Filled > 0) { _entryOrder1 = null; } } else if (isExit && _entryOrder2 != null && _entryOrder2 == o) { // Because we restrict Q=1 we know that any filled exit is a full // exit. We don't have to track the total quantity. if (o.Name == System2Name && o.Filled > 0) { _entryOrder2 = null; } } } protected override void OnOrderUpdate(Order order, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, OrderState orderState, DateTime time, ErrorCode error, string comment) { if (order.Name == System1Name) { _entryOrder1 = order; // Reset the entryOrder object to null if order was cancelled without any fill if (order.OrderState == OrderState.Cancelled && order.Filled == 0) { _entryOrder1 = null; } } else if (order.Name == System2Name) { _entryOrder2 = order; // Reset the entryOrder object to null if order was cancelled without any fill if (order.OrderState == OrderState.Cancelled && order.Filled == 0) { _entryOrder2 = null; } } } protected override void OnStateChange() { if (State == State.SetDefaults) { Name = "Sims-2-SimpleUnmanaged"; IsInstantiatedOnEachOptimizationIteration = false; EntriesPerDirection = 2; IsExitOnSessionCloseStrategy = false; IsUnmanaged = true; _entryOrder1 = null; _entryOrder2 = null; } else if (State == State.Configure) { _entryOrder1 = null; _entryOrder2 = null; } else if (State == State.DataLoaded) { var bp = BarsArray[0].BarsPeriod; if (bp.BarsPeriodType != Data.BarsPeriodType.Minute || bp.Value != 30) { throw new Exception("The primary data series for this test strategy must use 30 minute bars"); } if (BarsArray[0].TradingHours.Name != "CME US Index Futures ETH") { throw new Exception("This test strategy requires that the primary data series is using the 'CME US Index Futures ETH' trading hours template."); } } } } }
Comment