Стратегия Market Capture повторяет логику оригинального советника для MetaTrader 5. Алгоритм строит динамическую сетку вокруг перемещающегося ценового центра и открывает «зеркальные» позиции, когда цена колеблется около него. Сделки распределяются выше и ниже центра с фиксированными целями по прибыли, а контрольные точки по величине капитала определяют, когда необходимо закрыть наиболее убыточные позиции.
Правила торговли
Центральный уровень – внутренняя переменная задаётся по закрытию первой свечи. Если цена уходит дальше настроенного шага сетки, центр последовательно сдвигается за ценой.
Стартовый шорт – для соответствия MQL-версии можно сразу после запуска открыть начальную короткую позицию.
Входы в лонг – разрешены, когда текущий закрытие выше центра, а предыдущая свеча опускалась ниже него. Дополнительная проверка гарантирует отсутствие другой длинной позиции рядом с тем же уровнем.
Входы в шорт – разрешены, когда закрытие ниже центра, а предыдущая свеча поднималась выше него. Аналогичный фильтр не даёт открывать повторные короткие сделки.
Фиксация прибыли – для каждой сделки сохраняется целевой уровень, отстоящий от цены входа на заданное число шагов цены. Достижение цели (high для лонга или low для шорта) приводит к закрытию рыночным ордером.
Управление капиталом – отслеживается стоимость портфеля. При росте капитала на заданный процент закрывается несколько наиболее убыточных позиций, что фиксирует прибыль. При падении на другой процент выполняется аналогичное сокращение риска. После каждого срабатывания границы база для расчёта обновляется.
Параметры
Enable Long / Enable Short – включение сделок в каждом направлении.
Grid Steps – шаг сетки в единицах ценового шага инструмента.
Take Profit Steps – расстояние до цели в шагах цены.
Open Initial Short – открыть стартовую короткую позицию после запуска.
Use Equity Target – правило закрытия убыточных сделок после прироста капитала.
Track Drawdown – правило закрытия убыточных сделок при просадке.
Equity Gain % / Equity Loss % – процент изменения капитала для срабатывания каждого правила.
Loss Trades Up / Loss Trades Down – максимально закрываемое число убыточных позиций при срабатывании правил.
Candle Type – таймфрейм или тип свечей, на которых выполняется расчёт.
Volume (свойство стратегии) – объём каждой рыночной заявки.
Примечания
Стратегия ведёт собственный учёт открытых сделок, чтобы приблизить поведение к хеджевому подходу оригинального советника в условиях неттинговой модели StockSharp.
Параметры дистанций умножаются на ценовой шаг инструмента; убедитесь, что для инструмента задано корректное значение PriceStep.
Логика работает только по завершённым свечам. Выберите тип свечей, соответствующий требуемому горизонту торговли: от внутридневных до более широких сеток.
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>
/// Market capture strategy using EMA crossover to capture market direction changes.
/// </summary>
public class MarketCaptureStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public MarketCaptureStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast;
_prevSlow = slow;
if (!prevAbove && currAbove)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (prevAbove && !currAbove)
{
if (Position > 0)
SellMarket();
if (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
from StockSharp.Algo.Strategies import Strategy
class market_capture_strategy(Strategy):
def __init__(self):
super(market_capture_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 8) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(market_capture_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(market_capture_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif prev_above and not curr_above:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return market_capture_strategy()