using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on difference of two moving averages with standard deviation filter.
/// Buys when xdin change exceeds K1*StdDev, sells when below -K1*StdDev.
/// </summary>
public class ColorXdinMAStDevStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _mainLength;
private readonly StrategyParam<int> _plusLength;
private readonly StrategyParam<int> _stdPeriod;
private readonly StrategyParam<decimal> _k1;
private StandardDeviation _stdDev;
private decimal? _prevXdin;
public ColorXdinMAStDevStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle type", "Type of candles", "General");
_mainLength = Param(nameof(MainLength), 10)
.SetDisplay("Main MA Length", "Length of primary moving average", "Parameters");
_plusLength = Param(nameof(PlusLength), 20)
.SetDisplay("Plus MA Length", "Length of secondary moving average", "Parameters");
_stdPeriod = Param(nameof(StdPeriod), 9)
.SetDisplay("StdDev Period", "Period for standard deviation of MA changes", "Parameters");
_k1 = Param(nameof(K1), 0.5m)
.SetDisplay("Filter K1", "Multiplier for standard deviation filter", "Parameters");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MainLength
{
get => _mainLength.Value;
set => _mainLength.Value = value;
}
public int PlusLength
{
get => _plusLength.Value;
set => _plusLength.Value = value;
}
public int StdPeriod
{
get => _stdPeriod.Value;
set => _stdPeriod.Value = value;
}
public decimal K1
{
get => _k1.Value;
set => _k1.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevXdin = null;
_stdDev = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevXdin = null;
_stdDev = new StandardDeviation { Length = StdPeriod };
Indicators.Add(_stdDev);
var mainMa = new ExponentialMovingAverage { Length = MainLength };
var plusMa = new ExponentialMovingAverage { Length = PlusLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(mainMa, plusMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, mainMa);
DrawIndicator(area, plusMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal mainValue, decimal plusValue)
{
if (candle.State != CandleStates.Finished)
return;
// xdin = extrapolated price using MA difference
var xdin = mainValue * 2m - plusValue;
if (_prevXdin is null)
{
_prevXdin = xdin;
return;
}
var change = xdin - _prevXdin.Value;
_prevXdin = xdin;
var stdResult = _stdDev.Process(new DecimalIndicatorValue(_stdDev, change, candle.ServerTime) { IsFinal = true });
if (!_stdDev.IsFormed)
return;
var stDev = stdResult.ToDecimal();
if (stDev == 0)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var filter = K1 * stDev;
if (change > filter && Position <= 0)
BuyMarket();
else if (change < -filter && Position >= 0)
SellMarket();
}
}
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, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class color_xdin_ma_st_dev_strategy(Strategy):
def __init__(self):
super(color_xdin_ma_st_dev_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle type", "Type of candles", "General")
self._main_length = self.Param("MainLength", 10) \
.SetDisplay("Main MA Length", "Length of primary moving average", "Parameters")
self._plus_length = self.Param("PlusLength", 20) \
.SetDisplay("Plus MA Length", "Length of secondary moving average", "Parameters")
self._std_period = self.Param("StdPeriod", 9) \
.SetDisplay("StdDev Period", "Period for standard deviation of MA changes", "Parameters")
self._k1 = self.Param("K1", 0.5) \
.SetDisplay("Filter K1", "Multiplier for standard deviation filter", "Parameters")
self._std_dev = None
self._prev_xdin = None
@property
def candle_type(self):
return self._candle_type.Value
@property
def main_length(self):
return self._main_length.Value
@property
def plus_length(self):
return self._plus_length.Value
@property
def std_period(self):
return self._std_period.Value
@property
def k1(self):
return self._k1.Value
def OnReseted(self):
super(color_xdin_ma_st_dev_strategy, self).OnReseted()
self._prev_xdin = None
self._std_dev = None
def OnStarted2(self, time):
super(color_xdin_ma_st_dev_strategy, self).OnStarted2(time)
self._prev_xdin = None
self._std_dev = StandardDeviation()
self._std_dev.Length = self.std_period
self.Indicators.Add(self._std_dev)
main_ma = ExponentialMovingAverage()
main_ma.Length = self.main_length
plus_ma = ExponentialMovingAverage()
plus_ma.Length = self.plus_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(main_ma, plus_ma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, main_ma)
self.DrawIndicator(area, plus_ma)
self.DrawOwnTrades(area)
def process_candle(self, candle, main_value, plus_value):
if candle.State != CandleStates.Finished:
return
main_value = float(main_value)
plus_value = float(plus_value)
xdin = main_value * 2.0 - plus_value
if self._prev_xdin is None:
self._prev_xdin = xdin
return
change = xdin - self._prev_xdin
self._prev_xdin = xdin
std_result = process_float(self._std_dev, change, candle.ServerTime, True)
if not self._std_dev.IsFormed:
return
st_dev = float(std_result)
if st_dev == 0:
return
filter_val = float(self.k1) * st_dev
if change > filter_val and self.Position <= 0:
self.BuyMarket()
elif change < -filter_val and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return color_xdin_ma_st_dev_strategy()