using System;
using NinjaTrader.Cbi;
using NinjaTrader.NinjaScript;
namespace NinjaTrader.NinjaScript.Strategies
{
public class EMA21FirstTouch : Strategy
{
private double emaValue;
private double lowestPriceSinceMoveAway;
private bool hasMovedAway = false;
private bool tradeActive = false;
public int EmaPeriod { get; set; } = 21;
public int MinDistanceFromEMA { get; set; } = 40;
public int StopLoss { get; set; } = 40;
public double RiskRewardRatio { get; set; } = 2;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Executes trades on the first touch of the 21-day EMA after price moves away by a specified distance.";
Name = "EMA21FirstTouch";
Calculate = Calculate.OnEachTick;
IsInstantiatedOnEachOptimizationIteration = false;
BarsRequiredToTrade = 21;
}
else if (State == State.DataLoaded)
{
lowestPriceSinceMoveAway = double.MaxValue;
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < BarsRequiredToTrade)
return;
emaValue = EMA(EmaPeriod)[0];
// Ensure trading only occurs during the specified time window
if (ToTime(Time[0]) < 084500 || ToTime(Time[0]) > 143000)
{
Print($"Outside trading hours: {Time[0]}");
return;
}
// Reset conditions if trade is closed
if (tradeActive && Position.MarketPosition == MarketPosition.Flat)
{
Print($"Trade closed at {Time[0]}, resetting strategy.");
ResetStrategy();
}
// Track lowest price since moving away
lowestPriceSinceMoveAway = Math.Min(lowestPriceSinceMoveAway, Low[0]);
// Check if price has moved away by the specified distance
if (!hasMovedAway && Math.Abs(Close[0] - emaValue) >= MinDistanceFromEMA)
{
hasMovedAway = true;
Print($"Price moved {MinDistanceFromEMA}+ points away from EMA at {Time[0]}. EMA: {emaValue}, Close: {Close[0]}");
}
// Execute trade on first touch of EMA
if (hasMovedAway && !tradeActive && High[0] >= emaValue && Low[0] <= emaValue)
{
Print($"Price touched EMA at {Time[0]}. EMA: {emaValue}, High: {High[0]}, Low: {Low[0]}");
if (Close[0] > emaValue)
{
// Price is above EMA, execute a long trade
Print($"Executing Long trade at {Time[0]}. EMA: {emaValue}, Close: {Close[0]}");
EnterLong();
SetStopLoss(CalculationMode.Ticks, StopLoss);
SetProfitTarget(CalculationMode.Ticks, StopLoss * RiskRewardRatio);
}
else if (Close[0] < emaValue)
{
// Price is below EMA, execute a short trade
Print($"Executing Short trade at {Time[0]}. EMA: {emaValue}, Close: {Close[0]}");
EnterShort();
SetStopLoss(CalculationMode.Ticks, StopLoss);
SetProfitTarget(CalculationMode.Ticks, StopLoss * RiskRewardRatio);
}
tradeActive = true;
hasMovedAway = false;
}
}
private void ResetStrategy()
{
lowestPriceSinceMoveAway = double.MaxValue;
hasMovedAway = false;
tradeActive = false;
Print($"Strategy reset at {Time[0]}.");
}
}
}
