region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Gui.Tools;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.Indicators;
using NinjaTrader.NinjaScript.DrawingTools;
#endregion
//This namespace holds Strategies in this folder and is required. Do not change it.
namespace NinjaTrader.NinjaScript.Strategies
{
public class TensThroughVWAP : Strategy
{
private OrderFlowVWAP vwap;
private Order shortLimitOrder;
private Order longLimitOrder;
private bool vwapTouchedToday;
private bool tradeCompletedToday;
private bool isFirstTickProcessed = false; // Flag to control execution on the first tick of the bar
private double upperTensPlace; // Class level variable
private double lowerTensPlace; // Class level variable
private bool ordersCanceledPost340 = false; // Flag to control cancellation post 3:40 PM
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = @"Cross of the first 10s place above or below the VWAP";
Name = "TensThroughVWAP";
Calculate = Calculate.OnEachTick;
EntriesPerDirection = 1;
EntryHandling = EntryHandling.AllEntries;
IsExitOnSessionCloseStrategy = true;
ExitOnSessionCloseSeconds = 30;
IsFillLimitOnTouch = false;
MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
OrderFillResolution = OrderFillResolution.Standard;
Slippage = 0;
StartBehavior = StartBehavior.WaitUntilFlat;
TimeInForce = TimeInForce.Gtc;
TraceOrders = false;
RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose;
StopTargetHandling = StopTargetHandling.PerEntryExecution;
BarsRequiredToTrade = 20;
// Disable this property for performance gains in Strategy Analyzer optimizations
// See the Help Guide for additional information
IsInstantiatedOnEachOptimizationIteration = true;
Print("Strategy is being set to default settings.");
}
else if (State == State.Configure)
{
// Adding the tick series for "MES 06-24" with Tick Replay enabled
AddDataSeries("MES 06-24", BarsPeriodType.Tick, 1, MarketDataType.Last);
vwap = OrderFlowVWAP(VWAPResolution.Standard, TradingHours.Get("CME US Index Futures ETH"), VWAPStandardDeviations.One, 0, 0, 0);
AddChartIndicator(vwap);
Print("VWAP indicator has been configured.");
}
else if (State == State.Historical)
{
vwapTouchedToday = false;
tradeCompletedToday = false;
isFirstTickProcessed = false;
Print("Resetting flags for the historical data processing.");
}
}
protected override void OnBarUpdate()
{
// Reset flags at the start of each trading session
if (Bars.IsFirstBarOfSession)
{
isFirstTickProcessed = false;
ordersCanceledPost340 = false;
vwapTouchedToday = false;
tradeCompletedToday = false;
Print($"{Time[0]} - Session start: Reset all flags.");
}
if (BarsInProgress == 0 && IsFirstTickOfBar)
{
isFirstTickProcessed = true;
double vwapValue = vwap.VWAP[0];
upperTensPlace = Math.Ceiling(vwapValue / 10) * 10;
lowerTensPlace = Math.Floor(vwapValue / 10) * 10;
Print($"{Time[0]} - New 5-min bar: Open={Open[0]}, VWAP={vwapValue}, UpperTP={upperTensPlace}, LowerTP={lowerTensPlace}, Orders: Short={shortLimitOrder}, Long={longLimitOrder}");
if (!IsTradingHour() || (tradeCompletedToday && vwapTouchedToday))
{
Print($"{Time[0]} - Trading halted: Outside trading hours or conditions met.");
return;
}
}
if (BarsInProgress == 1)
{
ExecuteTradesOnTick();
}
}
private void ExecuteTradesOnTick()
{
if (!IsTradingHour() || (tradeCompletedToday && vwapTouchedToday))
return;
// Check conditions for the new 5-minute bar, not for every tick unless it's the first tick of a new bar
if (isFirstTickProcessed)
{
if (Open[0] > upperTensPlace && (shortLimitOrder == null || shortLimitOrder.LimitPrice != upperTensPlace))
{
CancelAndPlaceOrder(ref shortLimitOrder, upperTensPlace, "ShortVWAPEntry", MarketPosition.Short);
}
else if (Open[0] < lowerTensPlace && (longLimitOrder == null || longLimitOrder.LimitPrice != lowerTensPlace))
{
CancelAndPlaceOrder(ref longLimitOrder, lowerTensPlace, "LongVWAPEntry", MarketPosition.Long);
}
}
}
private void CancelAndPlaceOrder(ref Order order, double price, string signalName, MarketPosition position)
{
try
{
if (order != null && order.OrderState == OrderState.Working)
{
CancelOrder(order);
}
order = position == MarketPosition.Short ?
EnterShortLimit(1, true, 1, price, signalName) :
EnterLongLimit(1, true, 1, price, signalName);
SetProfitTarget(signalName, CalculationMode.Ticks, ProfitTicks);
SetStopLoss(signalName, CalculationMode.Ticks, StopLossTicks, false);
Print($"Order placed at {price} for {signalName}.");
}
catch (Exception ex)
{
Print("Error in CancelAndPlaceOrder: " + ex.Message);
}
}
private bool IsTradingHour()
{
int currentTime = ToTime(Time[0]);
return currentTime >= 93000 && currentTime < 160000; // 9:30 AM to 3:40 PM
}
private void CancelAllOrders()
{
if (shortLimitOrder != null && shortLimitOrder.OrderState == OrderState.Working)
{
CancelOrder(shortLimitOrder);
Print("Canceled short limit order.");
}
if (longLimitOrder != null && longLimitOrder.OrderState == OrderState.Working)
{
CancelOrder(longLimitOrder);
Print("Canceled long limit order.");
}
}
protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity, Cbi.MarketPosition marketPosition, string orderId, DateTime time)
{
if (execution.Order != null && execution.Order.OrderState == OrderState.Filled)
{
Print($"Order filled. Price: {price}, Quantity: {quantity}, Position: {marketPosition}");
if (Math.Abs(execution.Price - vwap.VWAP[0]) < TickSize)
{
vwapTouchedToday = true;
Print("VWAP touched by execution.");
}
tradeCompletedToday = true;
Print("Trade completed today, setting flag.");
}
}
region
[Range(1, 100)]
[NinjaScriptProperty]
public int ProfitTicks { get; set; } = 40;
[Range(1, 100)]
[NinjaScriptProperty]
public int StopLossTicks { get; set; } = 40;
#endregion
}
}
It is properly calculating the open price, vwap, and upper and lower 10s places but it will place limit orders when the price doesn't open above/below one of the 10s places. It also takes more than one trade a day even if my touchedvwap and tradecompletedtoday flags have been set to true. Sometimes it doesn't set the limite orders when a 5 minute candle has opened above/below the 10s places. Any help is appreciated, thanks.
Comment