Стратегия CoeffofLine True
Эта стратегия переносит эксперт MQL5 Exp_CoeffofLine_true.mq5 на платформу StockSharp. Она отслеживает наклон линейной регрессии от медианных цен и реагирует на пересечения нулевой линии.
Длинная позиция открывается, когда наклон становится положительным после отрицательного значения. Короткая позиция открывается, когда наклон из положительного становится отрицательным. При противоположных сигналах существующие позиции закрываются. Обрабатываются только завершённые свечи.
Параметры
- Candle Type – таймфрейм используемых свечей.
- Slope Period – длина линейной регрессии для расчёта наклона.
- Signal Bar – индекс исторической свечи, по которой оценивается сигнал.
- Buy Open / Sell Open – разрешение на открытие длинных или коротких позиций.
- Buy Close / Sell Close – разрешение на закрытие длинных или коротких позиций.
Стратегия подписывается на свечи через высокоуровневый API и связывает индикатор без ручного запроса его значений.
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 zero crossing of a smoothed slope proxy.
/// </summary>
public class CoeffofLineTrueStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _slopePeriod;
private readonly StrategyParam<int> _signalBar;
private readonly StrategyParam<bool> _buyOpen;
private readonly StrategyParam<bool> _sellOpen;
private readonly StrategyParam<bool> _buyClose;
private readonly StrategyParam<bool> _sellClose;
private readonly StrategyParam<int> _cooldownBars;
private readonly List<decimal> _slopes = [];
private ExponentialMovingAverage _slopeProxy = null!;
private decimal? _prevValue;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int SlopePeriod { get => _slopePeriod.Value; set => _slopePeriod.Value = value; }
public int SignalBar { get => _signalBar.Value; set => _signalBar.Value = value; }
public bool BuyPosOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
public bool SellPosOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
public bool BuyPosClose { get => _buyClose.Value; set => _buyClose.Value = value; }
public bool SellPosClose { get => _sellClose.Value; set => _sellClose.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public CoeffofLineTrueStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
_slopePeriod = Param(nameof(SlopePeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Slope Period", "Slope proxy length", "Parameters");
_signalBar = Param(nameof(SignalBar), 1)
.SetNotNegative()
.SetDisplay("Signal Bar", "Historical bar index for signal", "Parameters");
_buyOpen = Param(nameof(BuyPosOpen), true)
.SetDisplay("Buy Open", "Allow opening long positions", "Trading");
_sellOpen = Param(nameof(SellPosOpen), true)
.SetDisplay("Sell Open", "Allow opening short positions", "Trading");
_buyClose = Param(nameof(BuyPosClose), true)
.SetDisplay("Buy Close", "Allow closing long positions", "Trading");
_sellClose = Param(nameof(SellPosClose), true)
.SetDisplay("Sell Close", "Allow closing short positions", "Trading");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_slopes.Clear();
_slopeProxy = null!;
_prevValue = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_slopeProxy = new ExponentialMovingAverage { Length = SlopePeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_slopeProxy, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal proxyValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
if (_prevValue is not decimal prevValue)
{
_prevValue = proxyValue;
return;
}
var slope = proxyValue - prevValue;
_prevValue = proxyValue;
_slopes.Add(slope);
if (_slopes.Count > SignalBar + 2)
_slopes.RemoveAt(0);
if (_slopes.Count <= SignalBar + 1)
return;
var prev = _slopes[^ (SignalBar + 1)];
var prev2 = _slopes[^ (SignalBar + 2)];
var buyOpen = BuyPosOpen && prev2 <= 0m && prev > 0m;
var sellOpen = SellPosOpen && prev2 >= 0m && prev < 0m;
var buyClose = BuyPosClose && prev2 >= 0m && prev < 0m;
var sellClose = SellPosClose && prev2 <= 0m && prev > 0m;
if (buyClose && Position > 0)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
if (sellClose && Position < 0)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
if (_cooldownRemaining == 0 && buyOpen)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownBars;
}
if (_cooldownRemaining == 0 && sellOpen)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
}
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 coeffof_line_true_strategy(Strategy):
def __init__(self):
super(coeffof_line_true_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._slope_period = self.Param("SlopePeriod", 5) \
.SetDisplay("Slope Period", "Slope proxy length", "Parameters")
self._signal_bar = self.Param("SignalBar", 1) \
.SetDisplay("Signal Bar", "Historical bar index for signal", "Parameters")
self._buy_open = self.Param("BuyPosOpen", True) \
.SetDisplay("Buy Open", "Allow opening long positions", "Trading")
self._sell_open = self.Param("SellPosOpen", True) \
.SetDisplay("Sell Open", "Allow opening short positions", "Trading")
self._buy_close = self.Param("BuyPosClose", True) \
.SetDisplay("Buy Close", "Allow closing long positions", "Trading")
self._sell_close = self.Param("SellPosClose", True) \
.SetDisplay("Sell Close", "Allow closing short positions", "Trading")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._slopes = []
self._prev_value = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def slope_period(self):
return self._slope_period.Value
@property
def signal_bar(self):
return self._signal_bar.Value
@property
def buy_open(self):
return self._buy_open.Value
@property
def sell_open(self):
return self._sell_open.Value
@property
def buy_close(self):
return self._buy_close.Value
@property
def sell_close(self):
return self._sell_close.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(coeffof_line_true_strategy, self).OnReseted()
self._slopes = []
self._prev_value = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(coeffof_line_true_strategy, self).OnStarted2(time)
slope_proxy = ExponentialMovingAverage()
slope_proxy.Length = self.slope_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(slope_proxy, self.process_candle).Start()
def process_candle(self, candle, proxy_value):
if candle.State != CandleStates.Finished:
return
proxy_value = float(proxy_value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if self._prev_value is None:
self._prev_value = proxy_value
return
slope = proxy_value - self._prev_value
self._prev_value = proxy_value
self._slopes.append(slope)
sb = self.signal_bar
if len(self._slopes) > sb + 2:
self._slopes.pop(0)
if len(self._slopes) <= sb + 1:
return
prev = self._slopes[-(sb + 1)]
prev2 = self._slopes[-(sb + 2)]
buy_open_sig = self.buy_open and prev2 <= 0 and prev > 0
sell_open_sig = self.sell_open and prev2 >= 0 and prev < 0
buy_close_sig = self.buy_close and prev2 >= 0 and prev < 0
sell_close_sig = self.sell_close and prev2 <= 0 and prev > 0
if buy_close_sig and self.Position > 0:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
if sell_close_sig and self.Position < 0:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
if self._cooldown_remaining == 0 and buy_open_sig:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
if self._cooldown_remaining == 0 and sell_open_sig:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
def CreateClone(self):
return coeffof_line_true_strategy()