View on GitHub

Ten Pips EURUSD Strategy

Overview

The Ten Pips EURUSD Strategy is a breakout system that reproduces the logic of the original MetaTrader expert advisor. It watches the most recent completed candle and places stop orders above and below that range. Orders are sized in pips, adjusted for the current instrument tick size, and optionally managed by a trailing stop. The implementation uses StockSharp's high-level candle subscriptions together with order book updates to keep the behaviour close to the MQL version while remaining broker-neutral.

Strategy Logic

  1. Subscribe to the selected candle type and wait until a new bar becomes active.
  2. Capture the previous candle high and low when that bar finishes. Pending orders are cancelled at this moment because the original EA limits them to one bar.
  3. On the first tick of the new bar check that:
    • The current open lies inside the previous candle range (gap filtering).
    • The current price is at least three pip units away from both extremes (a proxy for the broker stop level).
  4. Calculate the current spread using the best bid/ask. If there is no level-one data the strategy falls back to the pip size.
  5. Place two pending stop orders:
    • Buy Stop: activation at previousHigh + 2 × spread with stop loss below the entry price by StopLossPips and, if trailing is disabled, take profit at previousHigh + 2 × spread + TakeProfitPips.
    • Sell Stop: activation at previousLow − spread with symmetric exit levels.
  6. As soon as the candle completes, or both orders are filled/cancelled, the process repeats for the next bar.

Position Management

  • While a position is open the strategy monitors the best bid/ask on each order book update.
  • If trailing is disabled, the position closes when price touches the fixed stop or take-profit level.
  • If trailing is enabled:
    • For long trades the trailing stop activates once price advances by TrailingStopPips. The stop is set to bid − TrailingStopPips and moves every time price improves by at least TrailingStepPips.
    • For short trades the logic mirrors the long side using the ask price.
  • Manual exits reset all protective levels and keep any outstanding opposite-side stop order alive until the candle ends, reproducing the straddle behaviour of the EA.

Parameters

Name Default Description
Volume 0.01 Order volume in lots (or contract units for non-FX symbols).
StopLossPips 50 Distance between entry and protective stop, expressed in pips.
TakeProfitPips 150 Take-profit distance in pips, used only when trailing is disabled.
UseTrailing false Enables the trailing stop logic.
TrailingStopPips 50 Initial distance for the trailing stop, measured in pips.
TrailingStepPips 25 Minimum gain (in pips) required to move an active trailing stop.
CandleType 15 minute timeframe Candle series used to detect the breakout levels.

Notes and Recommendations

  • The pip size is derived automatically from Security.PriceStep and emulates the MQL digits adjustment, so the strategy adapts to 3- and 5-digit FX symbols.
  • All distances are recalculated in price units before placing orders, which keeps the strategy compatible with non-FX assets as long as the tick size is defined.
  • The minimal stop level fallback (three pip units) mimics the behaviour of the original EA when the broker does not report a stop level.
  • Because pending orders expire at the end of each candle you should run the strategy on the desired timeframe without gaps in the incoming candle stream.
  • Risk management is crucial. Consider testing with realistic spreads and commission models before trading live capital.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Breakout strategy: enters long when price breaks above previous candle high,
/// enters short when price breaks below previous candle low.
/// Uses ATR-based stop loss and take profit.
/// </summary>
public class TenPipsEurusdStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stopLossMult;
	private readonly StrategyParam<decimal> _takeProfitMult;
	private readonly StrategyParam<decimal> _trailingMult;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takePrice;
	private bool _hasPrev;

	public decimal StopLossMult { get => _stopLossMult.Value; set => _stopLossMult.Value = value; }
	public decimal TakeProfitMult { get => _takeProfitMult.Value; set => _takeProfitMult.Value = value; }
	public decimal TrailingMult { get => _trailingMult.Value; set => _trailingMult.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TenPipsEurusdStrategy()
	{
		_stopLossMult = Param(nameof(StopLossMult), 1.0m)
			.SetGreaterThanZero()
			.SetDisplay("SL Mult", "Stop loss ATR multiplier", "Risk");

		_takeProfitMult = Param(nameof(TakeProfitMult), 2.0m)
			.SetGreaterThanZero()
			.SetDisplay("TP Mult", "Take profit ATR multiplier", "Risk");

		_trailingMult = Param(nameof(TrailingMult), 0.8m)
			.SetGreaterThanZero()
			.SetDisplay("Trail Mult", "Trailing stop ATR multiplier", "Risk");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR calculation length", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh = 0;
		_prevLow = 0;
		_entryPrice = 0;
		_stopPrice = null;
		_takePrice = null;
		_hasPrev = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal atr)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevHigh = candle.HighPrice;
			_prevLow = candle.LowPrice;
			_hasPrev = true;
			return;
		}

		var close = candle.ClosePrice;

		// Manage existing position
		if (Position > 0)
		{
			// Trail
			var trail = close - TrailingMult * atr;
			if (_stopPrice == null || trail > _stopPrice)
				_stopPrice = trail;

			if (close <= _stopPrice || (_takePrice != null && close >= _takePrice))
			{
				SellMarket(Math.Abs(Position));
				_stopPrice = null;
				_takePrice = null;
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			var trail = close + TrailingMult * atr;
			if (_stopPrice == null || trail < _stopPrice)
				_stopPrice = trail;

			if (close >= _stopPrice || (_takePrice != null && close <= _takePrice))
			{
				BuyMarket(Math.Abs(Position));
				_stopPrice = null;
				_takePrice = null;
				_entryPrice = 0;
			}
		}

		// Entry on breakout
		if (_hasPrev && Position == 0)
		{
			if (close > _prevHigh + atr * 0.5m)
			{
				BuyMarket();
				_entryPrice = close;
				_stopPrice = close - StopLossMult * atr;
				_takePrice = close + TakeProfitMult * atr;
			}
			else if (close < _prevLow - atr * 0.5m)
			{
				SellMarket();
				_entryPrice = close;
				_stopPrice = close + StopLossMult * atr;
				_takePrice = close - TakeProfitMult * atr;
			}
		}

		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_hasPrev = true;
	}
}