GitHub で見る

Cidomo Strategy

Breakout system converted from the MetaTrader 5 expert advisor "Cidomo". The strategy waits for a new candle on the configured timeframe, measures the recent trading range, and places paired stop orders above and below that range. It manages risk with classic stop-loss/take-profit levels, an optional trailing stop, and two money-management modes (fixed volume or percentage risk).

How it works

  1. On every finished candle of CandleType, collect the last BarsCount highs and lows to define the short-term channel.
  2. Place a buy stop at highest + IndentPips and a sell stop at lowest - IndentPips (both values expressed in pips and converted to absolute prices).
  3. When a stop order is triggered, the opposite pending order is cancelled immediately.
  4. For an open position the strategy keeps track of:
    • Initial stop-loss (StopLossPips) and take-profit (TakeProfitPips).
    • A stepped trailing stop (TrailingStopPips / TrailingStepPips). The stop is moved only after price advances by at least TrailingStop + TrailingStep, mirroring the original EA.
    • Market exits are used to emulate MetaTrader's PositionModify calls when the stop or take-profit is touched.
  5. When UseTimeFilter is enabled, new orders are submitted only within ±30 seconds of StartHour:StartMinute (server time), replicating the tight trading window of the source script.

Money management

  • FixedVolume: always trades the exact TradeVolume specified by the user.
  • RiskPercent: calculates the order size so that a losing trade at the configured stop-loss distance reduces equity by RiskPercent. Volumes are rounded to the instrument's VolumeStep and clamped between MinVolume / MaxVolume.

Risk controls

  • Initial stop-loss and take-profit levels are stored locally and executed via market orders when price crosses the target during the next candle.
  • The trailing stop only moves in one direction and respects the step distance from the original EA, preventing constant small adjustments.
  • If no stop-loss is configured the risk-based position sizing automatically falls back to the fixed TradeVolume.

Parameters

Name Type Default Description
CandleType DataType H4 Timeframe used to build the breakout range.
BarsCount int 15 Number of completed candles considered when calculating the highest high and lowest low.
IndentPips decimal 3 Offset (in pips) added above/below the range before submitting stop orders.
StopLossPips decimal 50 Protective stop distance in pips. A value of 0 disables the stop.
TakeProfitPips decimal 50 Profit target distance in pips. A value of 0 disables the target.
TrailingStopPips decimal 35 Trailing stop distance in pips. Set to 0 to disable trailing.
TrailingStepPips decimal 5 Minimum extra profit required before tightening the trailing stop.
MoneyManagement CidomoMoneyManagementMode RiskPercent Chooses between fixed position size and risk-based sizing.
RiskPercent decimal 1 Percentage of equity risked per trade when MoneyManagement = RiskPercent.
TradeVolume decimal 0.1 Fixed order volume used in FixedVolume mode or when risk-based sizing cannot be computed.
UseTimeFilter bool false Enables the ±30 second time window filter.
StartHour int 9 Hour (0-23) of the trading window centre.
StartMinute int 58 Minute (0-59) of the trading window centre.

Notes

  • All pip-based parameters automatically adapt to 3- or 5-digit quotes by multiplying the instrument's PriceStep by 10, exactly like the MetaTrader implementation.
  • Because StockSharp manages stops client-side in this port, ensure the strategy remains connected so that market exits can be issued when protective levels are breached.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class CidomoStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public CidomoStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}