TugbaGold is a grid-based averaging expert advisor that originates from MetaTrader 5. The converted strategy recreates its martingale position sizing and basket management logic using StockSharp's high-level API. The system places new orders whenever the previous candle closes with directional momentum and progressively builds a grid of positions spaced by a configurable distance. Averaging exits are executed either by locking in profits on the extreme positions or by partially closing the basket depending on the selected mode.
How it works
The strategy evaluates completed candles from the CandleType parameter. Signals use the previous candle, matching the original MT5 logic.
A bullish candle enables the placement of a new buy order. A bearish candle enables a new sell order.
Orders are added only if the distance from the best existing price in that direction exceeds PointOrderStepPips.
The first order uses StartVolume. Subsequent entries double the volume of the most favorable position while respecting MaxVolume and broker limits.
Once at least two positions exist, the strategy computes target prices that include the MinimalProfitPips buffer. The computation differs per exit mode:
Average – weighted average of the extreme positions plus the profit buffer.
Partial – combination of the worst and best tickets where the worst ticket uses StartVolume and the best uses its actual size.
When targets are reached the strategy closes the corresponding orders:
Average mode – closes both extreme entries entirely.
Partial mode – closes the worst entry completely and reduces the better entry by StartVolume.
Single standalone positions use TakeProfitPips to exit once price reaches the configured distance.
Parameters
Parameter
Description
TakeProfitPips
Take-profit distance applied when only one position is open. Set to 0 to disable.
StartVolume
Initial volume for the first order in a grid sequence.
MaxVolume
Maximum order volume. 0 keeps the doubling sequence unbounded.
CloseMode
Exit mode: Average (close both extremes) or Partial (partial + full close).
PointOrderStepPips
Minimum distance in pips before a new averaging order can be added.
MinimalProfitPips
Additional profit buffer added to averaging targets.
CandleType
Candle series used for signal evaluation.
Position management
Price steps are derived from Security.PriceStep. If it is not available a default of 0.0001 is used.
Volumes are automatically normalized to the broker's minimum, maximum and step constraints.
The strategy tracks filled positions internally and issues market orders (BuyMarket / SellMarket) when closing parts of the basket.
Protection is enabled automatically through StartProtection() once the strategy starts.
Notes and limitations
The implementation assumes immediate fills for market orders, similar to the MT5 environment.
Averaging signals rely on current best bid/ask quotes; ensure Level1 data is available for accurate execution.
Because exits are driven by strategy logic, stop-loss levels from the original expert are not recreated.
Use cautious risk management: martingale sizing can lead to large exposure if trends persist.
Conversion details
The averaging formulas and basket adjustments mirror the original source code.
Position selection (best/worst tickets) is reproduced by tracking the highest and lowest open prices within each direction.
All logic is executed inside the candle subscription using StockSharp's high-level API without resorting to low-level data access.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// TugbaGold strategy: candle direction + EMA trend filter with martingale-style averaging.
/// Buys on bullish candle above EMA, sells on bearish candle below EMA.
/// </summary>
public class TugbaGoldStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private bool _wasBullishSignal;
private bool _hasPrevSignal;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public TugbaGoldStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_wasBullishSignal = false;
_hasPrevSignal = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_wasBullishSignal = false;
_hasPrevSignal = false;
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;
var bullish = candle.ClosePrice > candle.OpenPrice;
var bearish = candle.ClosePrice < candle.OpenPrice;
var bullishSignal = bullish && candle.ClosePrice > emaValue;
var bearishSignal = bearish && candle.ClosePrice < emaValue;
var crossedUp = bullishSignal && (!_hasPrevSignal || !_wasBullishSignal);
var crossedDown = bearishSignal && (!_hasPrevSignal || _wasBullishSignal);
if (crossedUp && Position <= 0)
BuyMarket();
else if (crossedDown && Position >= 0)
SellMarket();
if (bullishSignal || bearishSignal)
{
_wasBullishSignal = bullishSignal;
_hasPrevSignal = 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 tugba_gold_strategy(Strategy):
def __init__(self):
super(tugba_gold_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._ema_period = self.Param("EmaPeriod", 50)
self._was_bullish_signal = False
self._has_prev_signal = False
@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
def OnReseted(self):
super(tugba_gold_strategy, self).OnReseted()
self._was_bullish_signal = False
self._has_prev_signal = False
def OnStarted2(self, time):
super(tugba_gold_strategy, self).OnStarted2(time)
self._was_bullish_signal = False
self._has_prev_signal = False
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
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
ema_val = float(ema_value)
bullish = close > open_price
bearish = close < open_price
bullish_signal = bullish and close > ema_val
bearish_signal = bearish and close < ema_val
crossed_up = bullish_signal and (not self._has_prev_signal or not self._was_bullish_signal)
crossed_down = bearish_signal and (not self._has_prev_signal or self._was_bullish_signal)
if crossed_up and self.Position <= 0:
self.BuyMarket()
elif crossed_down and self.Position >= 0:
self.SellMarket()
if bullish_signal or bearish_signal:
self._was_bullish_signal = bullish_signal
self._has_prev_signal = True
def CreateClone(self):
return tugba_gold_strategy()