Главная
/
Примеры стратегий
Открыть на GitHub
Стратегия BandOsMa
Обзор
BandOsMa — перенос эксперта MetaTrader 5 "BandOsMA" на платформу StockSharp. Стратегия анализирует гистограмму MACD (OsMA), строит на ней полосы Боллинджера и отслеживает пробои за пределы канала. Дополнительное скользящее среднее по OsMA помогает определить, когда сигнал перестаёт быть актуальным.
Стратегия работает с одним инструментом и таймфреймом, выбранными пользователем. Все расчёты выполняются на закрытых свечах через высокоуровневую подписку StockSharp.
Логика торговли
Индикаторы
MovingAverageConvergenceDivergenceSignal используется для получения гистограммы MACD (OsMA).
BollingerBands применяется непосредственно к значениям OsMA для выявления экстремальных отклонений.
Настраиваемое скользящее среднее сглаживает OsMA и служит фильтром выхода.
Вход в позицию
Покупка : текущая OsMA закрывается ниже нижней полосы, а предыдущая свеча находилась выше неё.
Продажа : текущая OsMA закрывается выше верхней полосы, а предыдущая свеча была ниже неё.
Выход
Сигнал сбрасывается при обратном пересечении гистограммы и скользящей средней.
Если открытая позиция не соответствует активному сигналу, она закрывается немедленно.
На каждую сделку устанавливается стоп-лосс в пунктах, который одновременно используется как трейлинг-стоп с шагом StopLossPoints / 50 (по аналогии с MQL-реализацией).
Управление позицией
Стоп-лосс и трейлинг : расстояние задаётся в пунктах MetaTrader и переводится в цену с помощью PriceStep. Тот же диапазон применён к трейлингу; стоп переносится вперёд при улучшении цены закрытия на величину шага трейлинга.
Единственная позиция : стратегия держит только один нетто-объём. При появлении противоположного сигнала текущая позиция закрывается, после чего рассматривается новый вход.
Параметры
Группа
Параметр
Описание
Значение по умолчанию
General
CandleType
Таймфрейм для расчётов.
H1
Risk
LotSize
Торговый объём в лотах.
0.01
Risk
StopLossPoints
Стоп-лосс в пунктах MetaTrader (также задаёт трейлинг).
1000
Indicators
MacdFastPeriod
Быстрая EMA в MACD.
12
Indicators
MacdSlowPeriod
Медленная EMA в MACD.
26
Indicators
MacdSignalPeriod
EMA сигнальной линии MACD.
9
Indicators
PriceType
Тип цены для MACD (Close, Open, High, Low, Median, Typical, Weighted).
Typical
Indicators
BollingerPeriod
Период полос Боллинджера по OsMA.
26
Indicators
BollingerShift
Сдвиг буферов полос Боллинджера (неотрицательный).
0
Indicators
BollingerDeviation
Множитель стандартного отклонения.
2
Indicators
MovingAveragePeriod
Период сглаживающего скользящего среднего OsMA.
10
Indicators
MovingAverageShift
Сдвиг буфера скользящего среднего (неотрицательный).
0
Indicators
MovingAverageMethod
Тип скользящей средней (Simple, Exponential, Smoothed, LinearWeighted).
Simple
Особенности реализации
Используется WhenCandlesFinished, поэтому расчёты выполняются только на завершённых свечах.
Значения индикаторов сохраняются в списках, что позволяет имитировать сдвиги буферов MetaTrader. Отрицательные значения shift не поддерживаются.
Трейлинг-стоп обновляется по ценам закрытия свечей. Для более точного тикового трейлинга можно увеличить расстояние в пунктах.
Как использовать
Выберите инструмент и таймфрейм в StockSharp.
Настройте ключевые параметры (CandleType, LotSize, периоды индикаторов).
Запустите стратегию — она подпишется на свечи, рассчитает индикаторы и будет исполнять сделки в соответствии с описанной логикой.
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>
/// Strategy combining the MACD histogram (OsMA) with Bollinger Bands to trade reversals.
/// When OsMA crosses below lower Bollinger band, buy signal is generated.
/// When OsMA crosses above upper Bollinger band, sell signal is generated.
/// </summary>
public class BandOsMaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _macdFastPeriod;
private readonly StrategyParam<int> _macdSlowPeriod;
private readonly StrategyParam<int> _macdSignalPeriod;
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private decimal _prevOsma;
private decimal _prevUpper;
private decimal _prevLower;
private bool _hasPrev;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MacdFastPeriod
{
get => _macdFastPeriod.Value;
set => _macdFastPeriod.Value = value;
}
public int MacdSlowPeriod
{
get => _macdSlowPeriod.Value;
set => _macdSlowPeriod.Value = value;
}
public int MacdSignalPeriod
{
get => _macdSignalPeriod.Value;
set => _macdSignalPeriod.Value = value;
}
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
public BandOsMaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
_macdFastPeriod = Param(nameof(MacdFastPeriod), 20)
.SetDisplay("MACD Fast", "Fast EMA length", "Indicators");
_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 50)
.SetDisplay("MACD Slow", "Slow EMA length", "Indicators");
_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 12)
.SetDisplay("MACD Signal", "Signal EMA length", "Indicators");
_bollingerPeriod = Param(nameof(BollingerPeriod), 14)
.SetDisplay("Bollinger Period", "OsMA Bollinger Bands period", "Indicators");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
.SetDisplay("Bollinger Deviation", "Bollinger Bands deviation", "Indicators");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFastPeriod },
LongMa = { Length = MacdSlowPeriod }
},
SignalMa = { Length = MacdSignalPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
private BollingerBands _bollinger;
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
var val = (IMovingAverageConvergenceDivergenceSignalValue)macdValue;
if (val.Macd is not decimal macdLine || val.Signal is not decimal signalLine)
return;
var osma = macdLine - signalLine;
_bollinger ??= new BollingerBands
{
Length = BollingerPeriod,
Width = BollingerDeviation
};
var bbResult = (BollingerBandsValue)_bollinger.Process(new DecimalIndicatorValue(_bollinger, osma, candle.CloseTime));
if (bbResult.UpBand is not decimal upper || bbResult.LowBand is not decimal lower)
{
return;
}
if (_hasPrev)
{
// Buy: OsMA crosses below lower band (reversal up expected)
if (_prevOsma > _prevLower && osma <= lower && Position <= 0)
{
BuyMarket(Position < 0 ? Math.Abs(Position) + 1 : 1);
}
// Sell: OsMA crosses above upper band (reversal down expected)
else if (_prevOsma < _prevUpper && osma >= upper && Position >= 0)
{
SellMarket(Position > 0 ? Math.Abs(Position) + 1 : 1);
}
}
_prevOsma = osma;
_prevUpper = upper;
_prevLower = lower;
_hasPrev = true;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevOsma = 0;
_prevUpper = 0;
_prevLower = 0;
_hasPrev = false;
_bollinger = null;
base.OnReseted();
}
}
import clr
import math
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 MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
class band_os_ma_strategy(Strategy):
def __init__(self):
super(band_os_ma_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._macd_fast_period = self.Param("MacdFastPeriod", 20)
self._macd_slow_period = self.Param("MacdSlowPeriod", 50)
self._macd_signal_period = self.Param("MacdSignalPeriod", 12)
self._bollinger_period = self.Param("BollingerPeriod", 14)
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0)
self._macd_history = []
self._osma_history = []
self._prev_osma = None
self._prev_upper = None
self._prev_lower = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MacdFastPeriod(self):
return self._macd_fast_period.Value
@MacdFastPeriod.setter
def MacdFastPeriod(self, value):
self._macd_fast_period.Value = value
@property
def MacdSlowPeriod(self):
return self._macd_slow_period.Value
@MacdSlowPeriod.setter
def MacdSlowPeriod(self, value):
self._macd_slow_period.Value = value
@property
def MacdSignalPeriod(self):
return self._macd_signal_period.Value
@MacdSignalPeriod.setter
def MacdSignalPeriod(self, value):
self._macd_signal_period.Value = value
@property
def BollingerPeriod(self):
return self._bollinger_period.Value
@BollingerPeriod.setter
def BollingerPeriod(self, value):
self._bollinger_period.Value = value
@property
def BollingerDeviation(self):
return self._bollinger_deviation.Value
@BollingerDeviation.setter
def BollingerDeviation(self, value):
self._bollinger_deviation.Value = value
def OnReseted(self):
super(band_os_ma_strategy, self).OnReseted()
self._macd_history = []
self._osma_history = []
self._prev_osma = None
self._prev_upper = None
self._prev_lower = None
def OnStarted2(self, time):
super(band_os_ma_strategy, self).OnStarted2(time)
self._macd_history = []
self._osma_history = []
self._prev_osma = None
self._prev_upper = None
self._prev_lower = None
macd = MovingAverageConvergenceDivergence()
macd.ShortMa.Length = self.MacdFastPeriod
macd.LongMa.Length = self.MacdSlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(macd, self._process_candle).Start()
def _process_candle(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
macd_val = float(macd_value)
signal_period = self.MacdSignalPeriod
bb_period = self.BollingerPeriod
bb_dev = float(self.BollingerDeviation)
# Compute signal line manually
self._macd_history.append(macd_val)
while len(self._macd_history) > signal_period:
self._macd_history.pop(0)
if len(self._macd_history) < signal_period:
return
signal = sum(self._macd_history) / signal_period
osma = macd_val - signal
# Compute Bollinger Bands on OsMA
self._osma_history.append(osma)
while len(self._osma_history) > bb_period:
self._osma_history.pop(0)
if len(self._osma_history) < bb_period:
return
mean = sum(self._osma_history) / len(self._osma_history)
variance = sum((x - mean) ** 2 for x in self._osma_history) / len(self._osma_history)
std_dev = math.sqrt(variance)
upper = mean + bb_dev * std_dev
lower = mean - bb_dev * std_dev
if self._prev_osma is not None and self._prev_upper is not None and self._prev_lower is not None:
# Buy: OsMA crosses below lower band
if self._prev_osma > self._prev_lower and osma <= lower and self.Position <= 0:
self.BuyMarket()
# Sell: OsMA crosses above upper band
elif self._prev_osma < self._prev_upper and osma >= upper and self.Position >= 0:
self.SellMarket()
self._prev_osma = osma
self._prev_upper = upper
self._prev_lower = lower
def CreateClone(self):
return band_os_ma_strategy()