Стратегия Eugene переносит оригинального советника MetaTrader 4 «Eugene» на высокоуровневый API StockSharp. Алгоритм по умолчанию анализирует часовые свечи и ищет пробои внутренних баров, подтверждённые откатом к одной трети предыдущей свечи. После подтверждения пробоя стратегия входит по направлению пробоя и при появлении обратного сигнала переворачивает позицию.
Логика торговли
Поиск внутренней свечи – предыдущая свеча должна полностью находиться внутри диапазона свечи двумя барами ранее. Направление её закрытия определяет, является ли свеча «чёрной» (медвежьей) или «белой» (бычьей).
Фильтры «птиц» – если за внутренней свечой следует свеча того же цвета, такая комбинация отмечается как «птица». Чёрные птицы блокируют покупки, белые – продажи. Это повторяет защитный фильтр оригинального советника.
Уровни подтверждения – рассчитываются две цены подтверждения на уровне одной трети тела или тени предыдущей свечи:
Длинный уровень располагается на одной трети ниже предыдущего закрытия (используется тело бычьей свечи или тень медвежьей).
Короткий уровень располагается на одной трети выше предыдущего закрытия (тело медвежьей свечи или тень бычьей).
Сессионный фильтр – если текущая свеча открылась в 08:00 или позже, подтверждение считается выполненным даже без отката.
Условие пробоя – сигнал на покупку требует обновления максимумов текущей свечой при условии более высокого минимума и перекрытия диапазона свечи два бара назад. Для продажи используются зеркальные условия с обновлением минимумов и понижением максимумов.
Управление позицией – перед открытием новой сделки стратегия закрывает противоположную позицию. На каждой свече допускается не более одной покупки и одной продажи, что повторяет ограничения Counter_buy и Counter_sell из исходного кода.
Параметры
Имя
Описание
Значение по умолчанию
Trade Volume
Объём рыночных заявок.
0.1
Candle Type
Таймфрейм анализируемых свечей.
1 час
Отображение на графике
При наличии окна графика стратегия отображает анализируемые свечи и выполненные сделки, что помогает оценить характер пробоев.
Примечания
В реализации для StockSharp сохранён часовой фильтр торговой сессии оригинального советника. При работе на других рынках или таймзонах подберите подходящий таймфрейм.
В исходном MQL-файле отсутствует управление стоп-лоссом и тейк-профитом, поэтому портированная версия оставляет управление рисками на усмотрение пользователя.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class EugeneStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevSma;
private bool _hasPrev;
private int _cooldownRemaining;
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public EugeneStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 50).SetDisplay("SMA Period", "SMA lookback", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetDisplay("RSI Period", "RSI lookback", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevSma = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevSma = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sma, decimal rsi)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevSma = sma; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevSma = sma;
return;
}
if (_prevClose <= _prevSma && close > sma && rsi < 70 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevSma && close < sma && rsi > 30 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevSma = sma;
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class eugene_strategy(Strategy):
def __init__(self):
super(eugene_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 50) \
.SetDisplay("SMA Period", "SMA lookback", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def sma_period(self):
return self._sma_period.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(eugene_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(eugene_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._cooldown_remaining = 0
sma = SimpleMovingAverage()
sma.Length = self.sma_period
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, rsi, self.process_candle).Start()
def process_candle(self, candle, sma, rsi):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma_val = float(sma)
rsi_val = float(rsi)
if not self._has_prev:
self._prev_close = close
self._prev_sma = sma_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_sma = sma_val
return
if self._prev_close <= self._prev_sma and close > sma_val and rsi_val < 70 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_sma and close < sma_val and rsi_val > 30 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_sma = sma_val
def CreateClone(self):
return eugene_strategy()