Стратегия прорыва по корректированной средней
Стратегия торгует относительно индикатора Corrected Average. Индикатор использует скользящую среднюю и корректирует её с учётом стандартного отклонения цены, что уменьшает влияние резких движений.
Если цена закрывается выше корректированной средней на заданное число пунктов и затем возвращается к уровню прорыва, стратегия открывает длинную позицию. Обратная логика применяется для коротких позиций. Стоп-лосс и тейк-профит задаются в абсолютных пунктах.
Параметры
Candle Type – таймфрейм используемых свечей.
Length – период скользящей средней и стандартного отклонения.
MA Type – тип скользящей средней (SMA, EMA, SMMA, LWMA).
Level Points – расстояние прорыва от корректированной средней в шагах цены.
Stop Loss Points – расстояние стоп-лосса от цены входа.
Take Profit Points – расстояние тейк-профита от цены входа.
Enable Long – разрешить открытие длинных позиций.
Enable Short – разрешить открытие коротких позиций.
Логика торговли
- Рассчитать скользящую среднюю и стандартное отклонение.
- Построить корректированную среднюю, используя предыдущие значения и соотношение дисперсий.
- Зафиксировать прорыв, когда предыдущая свеча закрывается выше/ниже корректированной средней на заданный уровень.
- После прорыва дождаться возврата цены к уровню прорыва и войти в сделку по направлению прорыва.
- Закрывать противоположные позиции при появлении нового сигнала.
- Применять защиту стоп-лоссом и тейк-профитом.
Примечание
Стратегия является конверсией MQL-скрипта Exp_CorrectedAverage.mq5 и предназначена для образовательных целей. Перед использованием на реальных рынках требуется дополнительное тестирование.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the Corrected Average breakout.
/// Monitors price relative to a corrected moving average and trades on breakouts.
/// </summary>
public class CorrectedAverageBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _length;
private readonly StrategyParam<int> _levelPoints;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private decimal _prevCorrected;
private decimal _prevPrevCorrected;
private decimal _prevClose;
private decimal _prevPrevClose;
private bool _isInitialized;
private decimal _level;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Length { get => _length.Value; set => _length.Value = value; }
public int LevelPoints { get => _levelPoints.Value; set => _levelPoints.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public CorrectedAverageBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
_length = Param(nameof(Length), 12)
.SetDisplay("Length", "Period of moving average", "Indicator");
_levelPoints = Param(nameof(LevelPoints), 300)
.SetDisplay("Level Points", "Breakout distance in price steps", "Trading");
_stopLossPoints = Param(nameof(StopLossPoints), 1000)
.SetDisplay("Stop Loss Points", "Stop loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
.SetDisplay("Take Profit Points", "Take profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCorrected = default;
_prevPrevCorrected = default;
_prevClose = default;
_prevPrevClose = default;
_isInitialized = default;
_level = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var step = Security.PriceStep ?? 1m;
_level = LevelPoints * step;
var ma = new ExponentialMovingAverage { Length = Length };
var std = new StandardDeviation { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ma, std, ProcessCandle).Start();
StartProtection(
new Unit(StopLossPoints * step, UnitTypes.Absolute),
new Unit(TakeProfitPoints * step, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, decimal maValue, decimal stdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
decimal corrected;
if (!_isInitialized)
{
corrected = maValue;
_isInitialized = true;
}
else
{
var v1 = stdValue * stdValue;
var v2 = (_prevCorrected - maValue) * (_prevCorrected - maValue);
var k = (v2 < v1 || v2 == 0m) ? 0m : 1m - (v1 / v2);
corrected = _prevCorrected + k * (maValue - _prevCorrected);
}
var buySignal = _prevPrevClose > _prevPrevCorrected + _level && _prevClose <= _prevCorrected + _level;
var sellSignal = _prevPrevClose < _prevPrevCorrected - _level && _prevClose >= _prevCorrected - _level;
if (buySignal && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (sellSignal && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevPrevCorrected = _prevCorrected;
_prevPrevClose = _prevClose;
_prevCorrected = corrected;
_prevClose = candle.ClosePrice;
}
}
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 ExponentialMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class corrected_average_breakout_strategy(Strategy):
"""
Strategy based on the Corrected Average breakout.
Monitors price relative to a corrected moving average and trades on breakouts.
"""
def __init__(self):
super(corrected_average_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._length = self.Param("Length", 12) \
.SetDisplay("Length", "Period of moving average", "Indicator")
self._level_points = self.Param("LevelPoints", 300) \
.SetDisplay("Level Points", "Breakout distance in price steps", "Trading")
self._stop_loss_points = self.Param("StopLossPoints", 1000) \
.SetDisplay("Stop Loss Points", "Stop loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 2000) \
.SetDisplay("Take Profit Points", "Take profit in price steps", "Risk")
self._prev_corrected = 0.0
self._prev_prev_corrected = 0.0
self._prev_close = 0.0
self._prev_prev_close = 0.0
self._is_initialized = False
self._level = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(corrected_average_breakout_strategy, self).OnReseted()
self._prev_corrected = 0.0
self._prev_prev_corrected = 0.0
self._prev_close = 0.0
self._prev_prev_close = 0.0
self._is_initialized = False
self._level = 0.0
def OnStarted2(self, time):
super(corrected_average_breakout_strategy, self).OnStarted2(time)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
self._level = self._level_points.Value * step
ma = ExponentialMovingAverage()
ma.Length = self._length.Value
std = StandardDeviation()
std.Length = self._length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, std, self.on_process).Start()
sl_dist = self._stop_loss_points.Value * step
tp_dist = self._take_profit_points.Value * step
self.StartProtection(
Unit(float(tp_dist), UnitTypes.Absolute),
Unit(float(sl_dist), UnitTypes.Absolute)
)
def on_process(self, candle, ma_val, std_val):
if candle.State != CandleStates.Finished:
return
ma_val = float(ma_val)
std_val = float(std_val)
if not self._is_initialized:
corrected = ma_val
self._is_initialized = True
else:
v1 = std_val * std_val
diff = self._prev_corrected - ma_val
v2 = diff * diff
if v2 < v1 or v2 == 0:
k = 0.0
else:
k = 1.0 - (v1 / v2)
corrected = self._prev_corrected + k * (ma_val - self._prev_corrected)
buy_signal = (self._prev_prev_close > self._prev_prev_corrected + self._level and
self._prev_close <= self._prev_corrected + self._level)
sell_signal = (self._prev_prev_close < self._prev_prev_corrected - self._level and
self._prev_close >= self._prev_corrected - self._level)
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev_corrected = self._prev_corrected
self._prev_prev_close = self._prev_close
self._prev_corrected = corrected
self._prev_close = float(candle.ClosePrice)
def CreateClone(self):
return corrected_average_breakout_strategy()