DreamBot is a StockSharp port of the MetaTrader 4 expert advisor "DreamBot". The strategy monitors the Force Index oscillator on hourly candles and waits for the momentum to cross bullish or bearish thresholds. When the Force Index crosses above the bullish level after being below it on the previous bar, the strategy opens a long position. When the Force Index crosses below the bearish level after being above it, the strategy opens a short position. Trading occurs only when there is no existing position, mirroring the single-position logic of the original robot.
Trading logic
Subscribe to H1 candles and compute a smoothed Force Index (length 13 by default).
Track the last two completed Force Index values. Signals are generated using the previous bar values, exactly like the MT4 implementation (iForce with shift 1 and 2).
Enter long when the Force Index on the previous candle is above BullsThreshold and the value two candles back was below the threshold, provided no position is open.
Enter short when the Force Index on the previous candle is below BearsThreshold and the value two candles back was above the threshold, provided no position is open.
Optional trailing stop replicates the original EA: once profit exceeds TrailingStepPoints, a stop level is pulled to TrailingStartPoints away from price and follows further advances.
Risk management
StartProtection attaches classic stop-loss and take-profit orders using the MetaTrader "points" distance converted through the instrument price step.
Trailing protection is market-based: when the computed trailing level is breached, the strategy sends a market order to close the position immediately.
Position tracking captures the volume-weighted entry price so the trailing logic aligns with partial fills and reversals.
Parameters
Parameter
Description
ForcePeriod
Force Index smoothing period (default 13).
TakeProfitPoints
Take-profit distance in MetaTrader points.
StopLossPoints
Stop-loss distance in MetaTrader points.
BullsThreshold
Bullish Force Index threshold that enables long entries.
BearsThreshold
Bearish Force Index threshold that enables short entries.
EnableTrailing
Enables the trailing stop logic.
TrailingStartPoints
Distance (in points) maintained between price and trailing stop once activated.
TrailingStepPoints
Profit (in points) required before the trailing stop activates.
CandleType
Timeframe used for Force Index calculations (defaults to H1 candles).
Notes
The parameter validation keeps the trailing trigger (TrailingStepPoints) from exceeding the trailing distance (TrailingStartPoints), matching the MetaTrader safety check.
Stop-level enforcement from the original EA (broker MODE_STOPLEVEL) is approximated through StockSharp's price-step conversions. Depending on broker constraints, additional validation may be required.
All code comments and logs are provided in English as requested by the conversion guidelines.
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()