#region Using declarations using System; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Xml.Serialization; using System.Collections.Generic; using NinjaTrader.Cbi; using NinjaTrader.Data; using NinjaTrader.Indicator; using NinjaTrader.Gui.Chart; using NinjaTrader.Strategy; #endregion // This namespace holds all strategies and is required. Do not change it. namespace NinjaTrader.Strategy { /// /// Enter the description of your strategy here /// [Description("Forex D1 SMA5/3 Strategy as presented on ForexFactory.com - http://www.forexfactory.com/showthread.php?t=600112")] public class FXD1SMA5_3 : Strategy { #region Variables // Default settings private int defaultQuantity = 10000; private int currentQuantity = 10000; private bool doubleAfterLoss = true; private bool exitOnReverseSignals = true; private bool autoTakeProfit = true; private int takeProfitTarget = 50; private int reverseSignals = 3; private int trailingStopPoints = 5; private int trailingStopBarsAgo = 1; private int maxQuantity = 1000000; private Trade currentTrade = null; private Trade lastTrade = null; private int smaPeriod = 5; private int smaDisplacement = 1; private int bbDeviation = 2; private int bbPeriod = 20; private int conseqWinCount = 0; private int conseqLossCount = 0; private int tradesCounter = 0; private double takeProfitPrice = 0.0f, stopLossPrice = 0.0f; private string shortEntryName = "E Short"; private string shortExitName = "X Short"; private string longEntryName = "E Long"; private string longExitName = "X Long"; #endregion /// /// This method is used to configure the strategy and is called once before any strategy method is called. /// protected override void Initialize() { SMA(smaPeriod).Plots[0].Pen.Color = Color.Firebrick; SMA(smaPeriod).Plots[0].Pen.Width = 1; SMA(smaPeriod).Displacement = smaDisplacement; Add(SMA(smaPeriod)); Add(Bollinger(bbDeviation, bbPeriod)); EntriesPerDirection = 1; ExitOnClose = false; CalculateOnBarClose = true; ClearOutputWindow(); } /// /// Called on each bar update event (incoming tick) /// protected override void OnBarUpdate() { if (Position.MarketPosition == MarketPosition.Flat) { bool bCouldGoShort = false, bCouldGoLong = false; GetMarketPositionOpinion(ref bCouldGoShort, ref bCouldGoLong); if (bCouldGoShort || bCouldGoLong) OpenMarketPosition(bCouldGoShort, bCouldGoLong); } else { if (autoTakeProfit) AdjustProfitTarget(); if (!autoTakeProfit && takeProfitTarget == 0) AdjustTrailingStop(); if (exitOnReverseSignals) HandleExitConditions(); } } /// /// Draw trade results on chart /// protected override void OnPositionUpdate(IPosition position) { if (position.MarketPosition == MarketPosition.Long) { double y = Low[0], y2 = Bollinger(bbDeviation, bbPeriod).High[0]; DrawText("Stop", "Stop at " + y.ToString("#.####0") + "----", 0, y, Color.Red); DrawText("Target", "Target at " + y2.ToString("#.####0") + "----", 0, y, Color.Lime); } else if (position.MarketPosition == MarketPosition.Short) { double y = High[0], y2 = Bollinger(bbDeviation, bbPeriod).Low[0]; DrawText("Stop", "Stop at " + y.ToString("#.####0") + "----", 0, y, Color.Red); DrawText("Target", "Target at " + y2.ToString("#.####0") + "----", 0, y, Color.Lime); } else // MarketPosition.Flat { RemoveDrawObject("Stop"); RemoveDrawObject("Target"); int lastTradePosition = 1; lastTrade = Performance.AllTrades[Performance.AllTrades.Count - lastTradePosition]; // Last trade was a winner? if (lastTrade.ProfitCurrency > 0) { currentQuantity = defaultQuantity; conseqLossCount = 0; conseqWinCount++; DrawRectangle("WinnerArea" + CurrentBar, false, BarsSinceEntry(), lastTrade.Entry.Price, -1, lastTrade.Exit.Price, Color.Chartreuse, Color.Chartreuse, 5); Print("Last Trade was a winner!"); } // Last trade was a loser? else if (lastTrade.ProfitCurrency < 0) { conseqWinCount = 0; conseqLossCount++; DrawRectangle("LoserArea" + CurrentBar, false, BarsSinceEntry(), lastTrade.Entry.Price, -1, lastTrade.Exit.Price, Color.Red, Color.Red, 5); if (doubleAfterLoss) { currentQuantity = currentQuantity * 2; if (currentQuantity > maxQuantity) currentQuantity = defaultQuantity; } Print("Last Trade was a loser. New quantity " + currentQuantity); } } } /// /// Opens a market position, depending on what is available /// protected void OpenMarketPosition(bool bCouldGoShort, bool bCouldGoLong) { if (bCouldGoShort && !bCouldGoLong) { OpenPositionShort(); } else if (bCouldGoLong && !bCouldGoShort) { OpenPositionLong(); } else if (bCouldGoLong && bCouldGoShort) { Print("OMG SO UNDECIDED!"); } } /// /// Opens a LONG position /// protected void OpenPositionShort() { double price = Close[0]; stopLossPrice = High[trailingStopBarsAgo]; if (autoTakeProfit) takeProfitPrice = Bollinger(bbDeviation, bbPeriod).Lower[0]; else if (takeProfitTarget > 0) takeProfitPrice = Close[0] - takeProfitTarget * TickSize; else takeProfitPrice = 0.0f; tradesCounter++; DrawPositionChartInfo(MarketPosition.Short, stopLossPrice, takeProfitPrice); EnterShort(currentQuantity, shortEntryName); SetStopLoss(shortEntryName, CalculationMode.Price, stopLossPrice, false); if (takeProfitPrice > 0.0f) SetProfitTarget(shortEntryName, CalculationMode.Price, takeProfitPrice); Print("[" + tradesCounter + "] SHORT entry on " + Instrument.FullName + " @ " + price.ToString("#.####0") + " / SL: " + stopLossPrice.ToString("#.####0") + " / TP: " + takeProfitPrice.ToString("#.####0")); } /// /// Opens a SHORT position /// protected void OpenPositionLong() { double price = Close[0]; stopLossPrice = Low[trailingStopBarsAgo]; if (autoTakeProfit) takeProfitPrice = Bollinger(bbDeviation, bbPeriod).Upper[0]; else if (takeProfitTarget > 0) takeProfitPrice = Close[0] + takeProfitTarget * TickSize; else takeProfitPrice = 0.0f; tradesCounter++; DrawPositionChartInfo(MarketPosition.Long, stopLossPrice, takeProfitPrice); EnterLong(currentQuantity, longEntryName); SetStopLoss(longEntryName, CalculationMode.Price, stopLossPrice, false); if (takeProfitPrice > 0.0f) SetProfitTarget(longEntryName, CalculationMode.Price, takeProfitPrice); Print("[" + tradesCounter + "] LONG entry on " + Instrument.FullName + " @ " + price.ToString("#.####0") + " / SL: " + stopLossPrice.ToString("#.####0") + " / TP: " + takeProfitPrice.ToString("#.####0")); } /// /// Gets an evaluation of the current market /// protected void GetMarketPositionOpinion(ref bool bCouldGoShort, ref bool bCouldGoLong) { bCouldGoShort = GetMarketPositionOpinionShort(); bCouldGoLong = GetMarketPositionOpinionLong(); } /// /// Checks if the market allows a SHORT position /// protected bool GetMarketPositionOpinionShort() { bool bCouldGoShort = false; double price = GetCurrentBid(), sma0 = SMA(smaPeriod)[0 + smaDisplacement], sma1 = SMA(smaPeriod)[1 + smaDisplacement], sma2 = SMA(smaPeriod)[2 + smaDisplacement]; if (// Current price must be below SMA price < sma0 // Last bar must have been opened above and closed below SMA && Open[0] > sma0 && Close[0] <= sma0 // 2nd and 3rd last bars must have been closed above SMA && Close[1] > sma1 && Close[2] > sma2 ) { bCouldGoShort = true; } return(bCouldGoShort); } /// /// Checks if the market allows a LONG position /// protected bool GetMarketPositionOpinionLong() { bool bCouldGoLong = false; double price = GetCurrentBid(), sma0 = SMA(smaPeriod)[0 + smaDisplacement], sma1 = SMA(smaPeriod)[1 + smaDisplacement], sma2 = SMA(smaPeriod)[2 + smaDisplacement]; if (// Current price must be above SMA price > sma0 // Last bar must have been opened below and closed above SMA && Open[0] < sma0 && Close[0] >= sma0 // 2nd and 3rd last bars must have been closed below SMA && Close[1] < sma1 && Close[2] < sma2 ) { bCouldGoLong = true; } return(bCouldGoLong); } /// /// Adjust profit target to the last upper / lower Bollinger bands /// protected void AdjustProfitTarget() { if (Position.MarketPosition == MarketPosition.Long) { AdjustProfitTargetLong(); } else if (Position.MarketPosition == MarketPosition.Short) { AdjustProfitTargetShort(); } } /// /// Adjust LONG profit target if market moves in our favor /// protected void AdjustProfitTargetLong() { double pnl = Position.GetProfitLoss(Close[0], PerformanceUnit.Points); double tp = trailingStopPoints * TickSize; if (tp == 0 || pnl < tp) return; takeProfitPrice = Bollinger(bbDeviation, bbPeriod).Upper[0] - 5 * TickSize; SetProfitTarget(longEntryName, CalculationMode.Price, takeProfitPrice); DrawPositionChartInfo(MarketPosition.Long, stopLossPrice, takeProfitPrice); } /// /// Adjust SHORT profit target if market moves in our favor /// protected void AdjustProfitTargetShort() { double pnl = Position.GetProfitLoss(Close[0], PerformanceUnit.Points); double tp = trailingStopPoints * TickSize; if (tp == 0 || pnl < tp) return; takeProfitPrice = Bollinger(bbDeviation, bbPeriod).Lower[0] + 5 * TickSize; SetProfitTarget(shortEntryName, CalculationMode.Price, takeProfitPrice); DrawPositionChartInfo(MarketPosition.Short, stopLossPrice, takeProfitPrice); } /// /// Adjust trailing stop /// protected void AdjustTrailingStop() { if (Position.MarketPosition == MarketPosition.Long) { AdjustTrailingStopLong(); } else if (Position.MarketPosition == MarketPosition.Short) { AdjustTrailingStopShort(); } } /// /// Adjust trailing stop for LONG positions /// protected void AdjustTrailingStopLong() { double pnl = Position.GetProfitLoss(Close[0], PerformanceUnit.Points); double tp = trailingStopPoints * TickSize; if (tp == 0 || pnl < tp) return; stopLossPrice = Low[trailingStopBarsAgo]; SetStopLoss(longEntryName, CalculationMode.Price, stopLossPrice, false); DrawPositionChartInfo(MarketPosition.Long, stopLossPrice, takeProfitPrice); } /// /// Adjust trailing stop for SHORT positions /// protected void AdjustTrailingStopShort() { double pnl = Position.GetProfitLoss(Close[0], PerformanceUnit.Points); double tp = trailingStopPoints * TickSize; if (tp == 0 || pnl < tp) return; stopLossPrice = High[trailingStopBarsAgo]; SetStopLoss(shortEntryName, CalculationMode.Price, stopLossPrice, false); DrawPositionChartInfo(MarketPosition.Short, stopLossPrice, takeProfitPrice); } /// /// Draw SL and TP position info on chart /// protected void DrawPositionChartInfo(MarketPosition position, double sl, double tp) { if (position == MarketPosition.Short) { RemoveDrawObject("SLLineShort_" + tradesCounter); RemoveDrawObject("SLTextShort_" + tradesCounter); RemoveDrawObject("TPLineShort_" + tradesCounter); RemoveDrawObject("TPTextShort_" + tradesCounter); DrawLine("SLLineShort_" + tradesCounter, 2, sl, 0, sl, Color.Red); DrawText("SLTextShort_" + tradesCounter, "SL " + tradesCounter, 3, sl, Color.Black); if (tp > 0.0f) { DrawLine("TPLineShort_" + tradesCounter, 2, takeProfitPrice, 0, takeProfitPrice, Color.Gold); DrawText("TPTextShort_" + tradesCounter, "TP " + tradesCounter, 3, takeProfitPrice, Color.Black); } } else if (position == MarketPosition.Long) { RemoveDrawObject("SLLineLong_" + tradesCounter); RemoveDrawObject("SLTextLong_" + tradesCounter); RemoveDrawObject("TPLineLong_" + tradesCounter); RemoveDrawObject("TPTextLong_" + tradesCounter); DrawLine("SLLineLong_" + tradesCounter, 2, sl, 0, sl, Color.Red); DrawText("SLTextLong_" + tradesCounter, "SL " + tradesCounter, 3, sl, Color.Black); if (tp > 0.0f) { DrawLine("TPLineLong_" + tradesCounter, 2, takeProfitPrice, 0, takeProfitPrice, Color.Gold); DrawText("TPTextLong_" + tradesCounter, "TP " + tradesCounter, 3, takeProfitPrice, Color.Black); } } } /// /// Checks for reversal signals and exits a position if required. /// protected void HandleExitConditions() { bool exitTrade = false; // Check if N last candles bodies were 45% smaller than their body // and their Highs / Lows peaked through the SMA int signalsCounter = 0; for (int i = 0; i < reverseSignals; i++) { double ch = High[i] - Low[i], bh = Math.Abs(Open[i] - Close[i]), sma = SMA(smaPeriod)[i + smaDisplacement]; if ((bh <= ch * 0.45) && ((Position.MarketPosition == MarketPosition.Long && Low[i] < sma) || (Position.MarketPosition == MarketPosition.Short && High[i] > sma) ) ) signalsCounter++; else signalsCounter = 0; } if (signalsCounter >= reverseSignals) { exitTrade = true; } if (// If we are SHORT and last bar opened above SMA, exit. (Position.MarketPosition == MarketPosition.Short && Open[0] > SMA(smaPeriod)[0 + smaDisplacement] && Open[1] < SMA(smaPeriod)[1 + smaDisplacement]) // If we are LONG and last bar opened below SMA, exit. || (Position.MarketPosition == MarketPosition.Long && Open[0] < SMA(smaPeriod)[0 + smaDisplacement] && Open[1] > SMA(smaPeriod)[1 + smaDisplacement]) ) { exitTrade = true; } if (exitTrade) { switch (Position.MarketPosition) { case MarketPosition.Long: Print("Exiting LONG Trade #" + tradesCounter + ": Reverse signals encountered"); ExitLong(); break; case MarketPosition.Short: Print("Exiting SHORT Trade #" + tradesCounter + ": Reverse signals encountered"); ExitShort(); break; } } } #region Properties [Description("Set TP to Bollinger upper / lower band. Could be counter productive on breakouts or small channels.")] [GridCategory("Parameters")] public bool AutoTakeProfit { get { return autoTakeProfit; } set { autoTakeProfit = value; } } [Description("Take Profit target if not using AutoTakeProfit. (0 = disabled)")] [GridCategory("Parameters")] public int TakeProfitTarget { get { return takeProfitTarget; } set { takeProfitTarget = Math.Max(0, value); } } [Description("SMA Period")] [GridCategory("Parameters")] public int SMAPeriod { get { return smaPeriod; } set { smaPeriod = Math.Max(1, value); } } [Description("SMA Shift")] [GridCategory("Parameters")] public int SMAShift { get { return smaDisplacement; } set { smaDisplacement = Math.Max(1, value); } } [Description("Quantity")] [GridCategory("Parameters")] public int Quantity { get { return defaultQuantity; } set { defaultQuantity = Math.Max(1, value); } } [Description("Maximum Quantity")] [GridCategory("Parameters")] public int MaxQuantity { get { return maxQuantity; } set { maxQuantity = Math.Max(1, value); } } [Description("Double Quantity after Loss")] [GridCategory("Parameters")] public bool DoubleQuantityAfterLoss { get { return doubleAfterLoss; } set { doubleAfterLoss = value; } } [Description("Exits a trade on reversal signals")] [GridCategory("Parameters")] public bool ExitOnReverseSignals { get { return exitOnReverseSignals; } set { exitOnReverseSignals = value; } } [Description("Number of consecutive reverse signals before exit")] [GridCategory("Parameters")] public int ReverseSignals { get { return reverseSignals; } set { reverseSignals = Math.Max(1, value); } } [Description("Activate Trailing Stop after N points profit (0 = disabled)")] [GridCategory("Parameters")] public int TrailingStop { get { return trailingStopPoints; } set { trailingStopPoints = Math.Max(0, value); } } [Description("Put Stop Loss at Low / High of Nth last bar.")] [GridCategory("Parameters")] public int TrailingStopBarsAgo { get { return trailingStopBarsAgo; } set { trailingStopBarsAgo = Math.Max(0, value); } } #endregion } }