DreamBot — это порт советника MetaTrader 4 «DreamBot» на платформу StockSharp. Стратегия анализирует осциллятор Force Index на часовых свечах и отслеживает моменты, когда импульс пересекает заданные бычий и медвежий уровни. Если Force Index пересекает бычий порог снизу вверх на предыдущей свече, открывается длинная позиция. Если индикатор пересекает медвежий порог сверху вниз, открывается короткая позиция. Сделки выполняются только при отсутствии открытых позиций, что повторяет оригинальную логику одного ордера.
Торговая логика
Подписка на свечи H1 и расчёт сглаженного Force Index (период по умолчанию 13).
Хранение двух последних завершённых значений индикатора. Сигналы формируются по данным предыдущих свечей, аналогично вызовам iForce со сдвигами 1 и 2 в MT4.
Открытие лонга, когда Force Index на предыдущей свече выше BullsThreshold, а значение двумя свечами ранее было ниже порога, и при этом позиция отсутствует.
Открытие шорта, когда Force Index на предыдущей свече ниже BearsThreshold, а значение двумя свечами ранее было выше порога, и позиция отсутствует.
Дополнительный трейлинг-стоп воспроизводит оригинал: после прибыли больше TrailingStepPoints стоп подтягивается на расстояние TrailingStartPoints от цены и следует за рынком.
Управление рисками
StartProtection добавляет классические стоп-лосс и тейк-профит, переводя значения в «пунктах» MetaTrader через шаг цены инструмента.
Трейлинг реализован через рыночное закрытие: при пробое рассчитанного уровня стратегия немедленно отправляет рыночный ордер на выход.
Учёт позиции ведётся по средневзвешенной цене входа, чтобы корректно работать с частичными исполнениями и реверсами.
Параметры
Параметр
Описание
ForcePeriod
Период сглаживания Force Index (по умолчанию 13).
TakeProfitPoints
Дистанция тейк-профита в пунктах MetaTrader.
StopLossPoints
Дистанция стоп-лосса в пунктах MetaTrader.
BullsThreshold
Порог Force Index, сигнализирующий о бычьем импульсе.
BearsThreshold
Порог Force Index, сигнализирующий о медвежьем импульсе.
EnableTrailing
Включает логику трейлинг-стопа.
TrailingStartPoints
Расстояние (в пунктах) между ценой и трейлинг-стопом после активации.
TrailingStepPoints
Прибыль (в пунктах), требуемая для активации трейлинга.
CandleType
Таймфрейм для расчёта Force Index (по умолчанию часовые свечи).
Примечания
Проверка параметров блокирует ситуацию, когда TrailingStepPoints больше TrailingStartPoints, как и в оригинальном советнике.
Ограничения брокера по минимальному стопу (MODE_STOPLEVEL) приближённо учитываются через шаг цены. При необходимости добавьте дополнительные проверки.
Комментарии и сообщения журнала оставлены на английском языке в соответствии с требованиями инструкций по конверсии.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// DreamBot strategy: Force Index momentum with EMA trend filter.
/// Buys when Force Index positive and close above EMA, sells when negative and below EMA.
/// </summary>
public class DreamBotStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevClose;
private bool _hasPrevClose;
private bool _wasBullish;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public DreamBotStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0m;
_hasPrevClose = false;
_wasBullish = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrevClose = false;
_candlesSinceTrade = SignalCooldownCandles;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
var close = candle.ClosePrice;
var volume = candle.TotalVolume;
if (_hasPrevClose && volume > 0)
{
// Simple force index: (close - prevClose) * volume
var forceIndex = (close - _prevClose) * volume;
var isBullish = forceIndex > 0 && close > emaValue;
if (isBullish && !_wasBullish && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (!isBullish && forceIndex < 0 && close < emaValue && _wasBullish && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
_wasBullish = isBullish;
}
_prevClose = close;
_hasPrevClose = true;
}
}
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 dream_bot_strategy(Strategy):
def __init__(self):
super(dream_bot_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._ema_period = self.Param("EmaPeriod", 100)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._prev_close = 0.0
self._has_prev_close = False
self._was_bullish = False
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(dream_bot_strategy, self).OnReseted()
self._prev_close = 0.0
self._has_prev_close = False
self._was_bullish = False
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(dream_bot_strategy, self).OnStarted2(time)
self._has_prev_close = False
self._candles_since_trade = self.SignalCooldownCandles
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self._process_candle).Start()
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
close = float(candle.ClosePrice)
volume = float(candle.TotalVolume)
ema_val = float(ema_value)
if self._has_prev_close and volume > 0:
force_index = (close - self._prev_close) * volume
is_bullish = force_index > 0 and close > ema_val
if is_bullish and not self._was_bullish and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif not is_bullish and force_index < 0 and close < ema_val and self._was_bullish and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._was_bullish = is_bullish
self._prev_close = close
self._has_prev_close = True
def CreateClone(self):
return dream_bot_strategy()