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>
/// Two smoothed moving average crossover strategy with level offsets.
/// </summary>
public class TwoMaFourLevelStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _mostTopLevel;
private readonly StrategyParam<int> _topLevel;
private readonly StrategyParam<int> _lowerLevel;
private readonly StrategyParam<int> _lowermostLevel;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<DataType> _candleType;
private SmoothedMovingAverage _fastMa = null!;
private SmoothedMovingAverage _slowMa = null!;
private decimal? _prevFast;
private decimal? _prevSlow;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int MostTopLevel { get => _mostTopLevel.Value; set => _mostTopLevel.Value = value; }
public int TopLevel { get => _topLevel.Value; set => _topLevel.Value = value; }
public int LowerLevel { get => _lowerLevel.Value; set => _lowerLevel.Value = value; }
public int LowermostLevel { get => _lowermostLevel.Value; set => _lowermostLevel.Value = value; }
public int TakeProfitPips { get => _takeProfitPips.Value; set => _takeProfitPips.Value = value; }
public int StopLossPips { get => _stopLossPips.Value; set => _stopLossPips.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TwoMaFourLevelStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Period of the fast smoothed MA", "Moving Averages")
.SetOptimize(20, 150, 5);
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Period of the slow smoothed MA", "Moving Averages")
.SetOptimize(60, 300, 5);
_mostTopLevel = Param(nameof(MostTopLevel), 2)
.SetGreaterThanZero()
.SetDisplay("Extreme Upper Level", "Highest positive offset in points", "Levels");
_topLevel = Param(nameof(TopLevel), 1)
.SetGreaterThanZero()
.SetDisplay("Upper Level", "Second positive offset in points", "Levels");
_lowerLevel = Param(nameof(LowerLevel), 1)
.SetGreaterThanZero()
.SetDisplay("Lower Level", "Second negative offset in points", "Levels");
_lowermostLevel = Param(nameof(LowermostLevel), 2)
.SetGreaterThanZero()
.SetDisplay("Extreme Lower Level", "Largest negative offset in points", "Levels");
_takeProfitPips = Param(nameof(TakeProfitPips), 500)
.SetGreaterThanZero()
.SetDisplay("Take Profit (pips)", "Distance to take profit", "Risk");
_stopLossPips = Param(nameof(StopLossPips), 1000)
.SetGreaterThanZero()
.SetDisplay("Stop Loss (pips)", "Distance to stop loss", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Time frame for analysis", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
if (Security != null)
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (FastPeriod >= SlowPeriod)
{
this.LogError("FastPeriod must be less than SlowPeriod.");
Stop();
return;
}
if (MostTopLevel <= TopLevel)
{
this.LogError("MostTopLevel must be greater than TopLevel.");
Stop();
return;
}
if (LowerLevel >= LowermostLevel)
{
this.LogError("LowerLevel must be less than LowermostLevel.");
Stop();
return;
}
_fastMa = new SmoothedMovingAverage { Length = FastPeriod };
_slowMa = new SmoothedMovingAverage { Length = SlowPeriod };
var pip = Security?.PriceStep ?? 1m;
StartProtection(
takeProfit: new Unit(TakeProfitPips * pip, UnitTypes.Absolute),
stopLoss: new Unit(StopLossPips * pip, UnitTypes.Absolute));
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_fastMa, _slowMa, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFast is null || _prevSlow is null)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var pip = Security?.PriceStep ?? 0.05m;
var signal = GetSignal(fast, slow, _prevFast.Value, _prevSlow.Value, pip);
if (signal > 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal < 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
private int GetSignal(decimal fast, decimal slow, decimal prevFast, decimal prevSlow, decimal pip)
{
if (IsCrossUp(prevFast, fast, prevSlow, slow, 0m) ||
IsCrossUp(prevFast, fast, prevSlow, slow, MostTopLevel * pip) ||
IsCrossUp(prevFast, fast, prevSlow, slow, TopLevel * pip) ||
IsCrossUp(prevFast, fast, prevSlow, slow, -LowermostLevel * pip) ||
IsCrossUp(prevFast, fast, prevSlow, slow, -LowerLevel * pip))
{
return 1;
}
if (IsCrossDown(prevFast, fast, prevSlow, slow, 0m) ||
IsCrossDown(prevFast, fast, prevSlow, slow, MostTopLevel * pip) ||
IsCrossDown(prevFast, fast, prevSlow, slow, TopLevel * pip) ||
IsCrossDown(prevFast, fast, prevSlow, slow, -LowermostLevel * pip) ||
IsCrossDown(prevFast, fast, prevSlow, slow, -LowerLevel * pip))
{
return -1;
}
return 0;
}
private static bool IsCrossUp(decimal prevFast, decimal fast, decimal prevSlow, decimal slow, decimal offset)
{
var prevSlowShifted = prevSlow + offset;
var slowShifted = slow + offset;
return prevFast <= prevSlowShifted && fast > slowShifted;
}
private static bool IsCrossDown(decimal prevFast, decimal fast, decimal prevSlow, decimal slow, decimal offset)
{
var prevSlowShifted = prevSlow + offset;
var slowShifted = slow + offset;
return prevFast >= prevSlowShifted && fast < slowShifted;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class two_ma_four_level_strategy(Strategy):
def __init__(self):
super(two_ma_four_level_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 10)
self._slow_period = self.Param("SlowPeriod", 30)
self._most_top = self.Param("MostTopLevel", 2)
self._top = self.Param("TopLevel", 1)
self._lower = self.Param("LowerLevel", 1)
self._lowermost = self.Param("LowermostLevel", 2)
self._tp = self.Param("TakeProfitPips", 500)
self._sl = self.Param("StopLossPips", 1000)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15)))
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(two_ma_four_level_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(two_ma_four_level_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast_ma = SmoothedMovingAverage()
fast_ma.Length = int(self._fast_period.Value)
slow_ma = SmoothedMovingAverage()
slow_ma.Length = int(self._slow_period.Value)
sec = self.Security
pip = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 1.0
tp_pips = int(self._tp.Value)
sl_pips = int(self._sl.Value)
self.StartProtection(
Unit(tp_pips * pip, UnitTypes.Absolute),
Unit(sl_pips * pip, UnitTypes.Absolute))
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ma, slow_ma, self.OnProcess).Start()
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
sec = self.Security
pip = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 0.05
pf = self._prev_fast
ps = self._prev_slow
most_top = int(self._most_top.Value) * pip
top = int(self._top.Value) * pip
lower = int(self._lower.Value) * pip
lowermost = int(self._lowermost.Value) * pip
buy_signal = (self._is_cross_up(pf, fv, ps, sv, 0) or
self._is_cross_up(pf, fv, ps, sv, most_top) or
self._is_cross_up(pf, fv, ps, sv, top) or
self._is_cross_up(pf, fv, ps, sv, -lowermost) or
self._is_cross_up(pf, fv, ps, sv, -lower))
sell_signal = (self._is_cross_down(pf, fv, ps, sv, 0) or
self._is_cross_down(pf, fv, ps, sv, most_top) or
self._is_cross_down(pf, fv, ps, sv, top) or
self._is_cross_down(pf, fv, ps, sv, -lowermost) or
self._is_cross_down(pf, fv, ps, sv, -lower))
if buy_signal:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif sell_signal:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def _is_cross_up(self, prev_fast, fast, prev_slow, slow, offset):
return prev_fast <= prev_slow + offset and fast > slow + offset
def _is_cross_down(self, prev_fast, fast, prev_slow, slow, offset):
return prev_fast >= prev_slow + offset and fast < slow + offset
def CreateClone(self):
return two_ma_four_level_strategy()