Simple FX Strategy
Overview
This strategy uses two exponential moving averages to detect trend changes. A long position is opened when the short EMA crosses above the long EMA, while a short position is opened when the short EMA crosses below the long EMA.
Parameters
- Long MA Period – period of the long EMA.
- Short MA Period – period of the short EMA.
- Stop Loss (points) – protective stop in price steps.
- Take Profit (points) – profit target in price steps.
- Candle Type – timeframe for candles.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simple trend following strategy based on two exponential moving averages.
/// Opens a long position when the short EMA crosses above the long EMA and a short position when it crosses below.
/// Optional stop loss and take profit are specified in price steps.
/// </summary>
public class SimpleFxStrategy : Strategy
{
private readonly StrategyParam<int> _longMaPeriod;
private readonly StrategyParam<int> _shortMaPeriod;
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private int _lastTrend; // 1 for bull, -1 for bear, 0 for none
/// <summary>
/// Period of the long EMA.
/// </summary>
public int LongMaPeriod
{
get => _longMaPeriod.Value;
set => _longMaPeriod.Value = value;
}
/// <summary>
/// Period of the short EMA.
/// </summary>
public int ShortMaPeriod
{
get => _shortMaPeriod.Value;
set => _shortMaPeriod.Value = value;
}
/// <summary>
/// Stop loss in price steps.
/// </summary>
public int StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in price steps.
/// </summary>
public int TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Type of candles to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes parameters.
/// </summary>
public SimpleFxStrategy()
{
_longMaPeriod = Param(nameof(LongMaPeriod), 200)
.SetGreaterThanZero()
.SetDisplay("Long MA Period", "Period of the long EMA", "Parameters")
.SetOptimize(100, 300, 50);
_shortMaPeriod = Param(nameof(ShortMaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Short MA Period", "Period of the short EMA", "Parameters")
.SetOptimize(10, 100, 10);
_stopLoss = Param(nameof(StopLoss), 30)
.SetDisplay("Stop Loss", "Stop loss in price steps", "Risk");
_takeProfit = Param(nameof(TakeProfit), 50)
.SetDisplay("Take Profit", "Take profit in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_lastTrend = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var longMa = new ExponentialMovingAverage { Length = LongMaPeriod };
var shortMa = new ExponentialMovingAverage { Length = ShortMaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(longMa, shortMa, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, longMa);
DrawIndicator(area, shortMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal longValue, decimal shortValue)
{
// Only process finished candles
if (candle.State != CandleStates.Finished)
return;
var trend = 0;
if (shortValue > longValue)
trend = 1;
else if (shortValue < longValue)
trend = -1;
if (trend == 0)
return;
if (trend != _lastTrend)
{
if (trend == 1)
{
// Bullish trend - open long
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else
{
// Bearish trend - open short
SellMarket();
_entryPrice = candle.ClosePrice;
}
_lastTrend = trend;
return;
}
if (Position == 0 || _entryPrice == 0m)
return;
var step = Security?.PriceStep ?? 1m;
var stopDelta = StopLoss * step;
var takeDelta = TakeProfit * step;
if (Position > 0)
{
// Manage long position
if (StopLoss > 0 && candle.ClosePrice <= _entryPrice - stopDelta)
{
SellMarket();
_lastTrend = 0;
_entryPrice = 0m;
return;
}
if (TakeProfit > 0 && candle.ClosePrice >= _entryPrice + takeDelta)
{
SellMarket();
_lastTrend = 0;
_entryPrice = 0m;
}
}
else if (Position < 0)
{
// Manage short position
if (StopLoss > 0 && candle.ClosePrice >= _entryPrice + stopDelta)
{
BuyMarket();
_lastTrend = 0;
_entryPrice = 0m;
return;
}
if (TakeProfit > 0 && candle.ClosePrice <= _entryPrice - takeDelta)
{
BuyMarket();
_lastTrend = 0;
_entryPrice = 0m;
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class simple_fx_strategy(Strategy):
def __init__(self):
super(simple_fx_strategy, self).__init__()
self._long_ma = self.Param("LongMaPeriod", 200).SetGreaterThanZero().SetDisplay("Long MA Period", "Period of long EMA", "Parameters")
self._short_ma = self.Param("ShortMaPeriod", 50).SetGreaterThanZero().SetDisplay("Short MA Period", "Period of short EMA", "Parameters")
self._sl = self.Param("StopLoss", 30).SetDisplay("Stop Loss", "SL in price steps", "Risk")
self._tp = self.Param("TakeProfit", 50).SetDisplay("Take Profit", "TP in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(simple_fx_strategy, self).OnReseted()
self._entry_price = 0
self._last_trend = 0
def OnStarted2(self, time):
super(simple_fx_strategy, self).OnStarted2(time)
self._entry_price = 0
self._last_trend = 0
self._step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
self._step = float(self.Security.PriceStep)
long_ema = ExponentialMovingAverage()
long_ema.Length = self._long_ma.Value
short_ema = ExponentialMovingAverage()
short_ema.Length = self._short_ma.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(long_ema, short_ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, long_ema)
self.DrawIndicator(area, short_ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, long_val, short_val):
if candle.State != CandleStates.Finished:
return
trend = 0
if short_val > long_val:
trend = 1
elif short_val < long_val:
trend = -1
if trend == 0:
return
close = float(candle.ClosePrice)
if trend != self._last_trend:
if trend == 1:
self.BuyMarket()
self._entry_price = close
else:
self.SellMarket()
self._entry_price = close
self._last_trend = trend
return
if self.Position == 0 or self._entry_price == 0:
return
step = self._step
sl_delta = self._sl.Value * step
tp_delta = self._tp.Value * step
if self.Position > 0:
if self._sl.Value > 0 and close <= self._entry_price - sl_delta:
self.SellMarket()
self._last_trend = 0
self._entry_price = 0
return
if self._tp.Value > 0 and close >= self._entry_price + tp_delta:
self.SellMarket()
self._last_trend = 0
self._entry_price = 0
elif self.Position < 0:
if self._sl.Value > 0 and close >= self._entry_price + sl_delta:
self.BuyMarket()
self._last_trend = 0
self._entry_price = 0
return
if self._tp.Value > 0 and close <= self._entry_price - tp_delta:
self.BuyMarket()
self._last_trend = 0
self._entry_price = 0
def CreateClone(self):
return simple_fx_strategy()