Эта стратегия является портом эксперта MetaTrader 4 «CrossMA» на платформу StockSharp. Торговля ведётся по пересечению двух простых скользящих средних, а каждая сделка защищается стоп-лоссом, рассчитанным по индикатору Average True Range (ATR). Вместо отправки e-mail уведомлений стратегия записывает подробные сообщения в журнал.
Логика торговли
Стратегия подписывается на выбранную серию свечей и рассчитывает быструю и медленную SMA, а также ATR.
Когда быстрая SMA пересекает медленную снизу вверх, закрываются короткие позиции и открывается длинная позиция. Стоп-лосс устанавливается на расстоянии одного ATR ниже цены входа.
Когда быстрая SMA пересекает медленную сверху вниз, закрываются длинные позиции и открывается короткая позиция. Стоп-лосс устанавливается на расстоянии одного ATR выше цены входа.
На каждой завершённой свече проверяется достижение стоп-цены. Если цена касается стопа, позиция немедленно закрывается по рынку.
Управление рисками
Размер позиции вычисляется из стоимости портфеля и параметра Maximum Risk. Если данные о капитале недоступны, используется значение Base Volume.
После двух и более подряд убыточных сделок объём уменьшается пропорционально параметру Decrease Factor, что повторяет поведение оригинального советника.
Объёмы нормализуются по шагу объёма инструмента, чтобы заявки имели допустимый размер.
Уведомления
Вместо писем стратегия записывает в журнал информативные сообщения о каждом открытии и закрытии позиции по сигналу или стопу. При необходимости логирование можно отключить параметром Enable Notifications.
Параметры
Candle Type – тип свечей для расчёта индикаторов.
Fast SMA Period – период быстрой скользящей средней (по умолчанию 4).
Slow SMA Period – период медленной скользящей средней (по умолчанию 12).
ATR Period – количество свечей для расчёта ATR (по умолчанию 6).
Base Volume – минимальный объём сделки при отсутствии данных о риске (по умолчанию 0.1).
Maximum Risk – доля капитала, выделяемая на одну сделку (по умолчанию 0.02).
Decrease Factor – уменьшение объёма после серии убыточных сделок (по умолчанию 3).
Enable Notifications – включает журналирование торговых событий.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// CrossMA strategy - fast/slow SMA crossover with ATR volatility filter.
/// Buys when fast SMA crosses above slow SMA.
/// Sells when fast SMA crosses below slow SMA.
/// </summary>
public class CrossMaAtrNotificationStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public CrossMaAtrNotificationStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 40)
.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new SimpleMovingAverage { Length = FastPeriod };
var slow = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class cross_ma_atr_notification_strategy(Strategy):
def __init__(self):
super(cross_ma_atr_notification_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 10).SetDisplay("Fast SMA", "Fast SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 40).SetDisplay("Slow SMA", "Slow SMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
@property
def fast_period(self): return self._fast_period.Value
@property
def slow_period(self): return self._slow_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(cross_ma_atr_notification_strategy, self).OnReseted()
self._prev_fast = 0.0; self._prev_slow = 0.0; self._has_prev = False
def OnStarted2(self, time):
super(cross_ma_atr_notification_strategy, self).OnStarted2(time)
self._has_prev = False
fast = SimpleMovingAverage(); fast.Length = self.fast_period
slow = SimpleMovingAverage(); slow.Length = self.slow_period
sub = self.SubscribeCandles(self.candle_type)
sub.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished: return
f = float(fast); s = float(slow)
if not self._has_prev: self._prev_fast = f; self._prev_slow = s; self._has_prev = True; return
if self._prev_fast <= self._prev_slow and f > s and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and f < s and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_fast = f; self._prev_slow = s
def CreateClone(self): return cross_ma_atr_notification_strategy()