#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
}
}