GitHub で見る

Forex Profit System Strategy

The strategy reproduces the classic MetaTrader expert advisor "Forex Profit System" inside the StockSharp high-level API. It uses three exponential moving averages (EMA 10, 25 and 50) on the candle median price combined with a Parabolic SAR filter. The combination detects short-lived momentum bursts that appear after the fast average crosses the slow trend line while the Parabolic SAR already flipped to the same side as price.

Trading Logic

  1. Indicator stack
    • Median price derived from the finished candle drives all indicators so that results match the original MetaTrader "PRICE_MEDIAN" input.
    • Fast EMA (length 10) reacts quickly to short-term momentum shifts.
    • Medium EMA (length 25) and slow EMA (length 50) define the directional bias.
    • Parabolic SAR with step 0.02 and maximum 0.2 confirms that price already broke to the new side of the trend.
  2. Long entry
    • EMA(10) is greater than both EMA(25) and EMA(50).
    • EMA(10) was below EMA(50) on the previous closed candle (cross-up confirmation).
    • Parabolic SAR value is below the candle close, meaning the dots switched to bullish mode.
    • No open position exists and the strategy is allowed to trade (online + permissions).
  3. Short entry
    • EMA(10) is lower than both EMA(25) and EMA(50).
    • EMA(10) was above EMA(50) on the previous closed candle (cross-down confirmation).
    • Parabolic SAR is above the candle close.
  4. Exit management
    • Hard stop-loss and take-profit are applied immediately after entry with asymmetric settings for long and short trades.
    • A trailing stop is armed once price moves far enough in favor of the position. The stop is pulled to current price -/+ trailing distance depending on the direction.
    • Early exit occurs when EMA(10) reverses direction (drops below its previous value for longs or rises above for shorts) and the open profit exceeds a minimum trigger distance.

Default Parameter Values

Parameter Default Description
CandleType 15 minute time frame Candle series processed by the strategy.
FastEmaLength 10 Period of the fast EMA.
MediumEmaLength 25 Period of the medium EMA.
SlowEmaLength 50 Period of the slow EMA.
SarStep 0.02 Initial acceleration for Parabolic SAR.
SarMax 0.2 Maximum acceleration for Parabolic SAR.
Volume 0.1 Trading volume in lots/contracts.
LongTakeProfitPoints 50 Take-profit distance for long trades measured in price points.
ShortTakeProfitPoints 50 Take-profit distance for short trades measured in price points.
LongStopLossPoints 30 Stop-loss distance for long trades measured in price points.
ShortStopLossPoints 30 Stop-loss distance for short trades measured in price points.
LongTrailingStopPoints 10 Trailing stop trigger distance for long trades.
ShortTrailingStopPoints 10 Trailing stop trigger distance for short trades.
LongProfitTriggerPoints 10 Minimum open profit (points) required before a long trade can be closed on EMA reversal.
ShortProfitTriggerPoints 5 Minimum open profit (points) required before a short trade can be closed on EMA reversal.

Implementation Notes

  • The strategy uses candle subscriptions and indicator binding in the high-level API while keeping all risk control inside the strategy class. No low-level order book access is required.
  • All trade management distances are converted from points into actual price offsets using the instrument PriceStep. If PriceStep is not available the raw point value is used so the algorithm still functions on synthetic instruments.
  • Protective stops (SetStopLoss, SetTakeProfit) are set using the resulting position after the market order is sent to stay in sync with potential partial fills.
  • Internal state keeps track of the last entry price per direction so that trailing and EMA-based exits can evaluate the realized progress precisely.
  • Because all logic runs on finished candles, there is no repainting risk and signals mirror the original MetaTrader behavior that calculated everything on start() close prices.

Suggested Usage

  • The method is suited for liquid FX pairs on intraday charts (15-minute default). Higher time frames can be used by adjusting the EMA periods and trade management distances accordingly.
  • For assets with different tick sizes or volatility levels adjust the point-based parameters (StopLoss, TakeProfit, TrailingStop, ProfitTrigger) so that distances match the instrument profile.
  • Combine with spread or session filters if the venue has wide spreads during certain hours; the strategy expects reasonable execution to realize the short-term momentum bursts.
using System;



using StockSharp.Algo.Indicators;

using StockSharp.Algo.Strategies;

using StockSharp.BusinessEntities;

using StockSharp.Messages;



namespace StockSharp.Samples.Strategies;



public class ForexProfitSystemStrategy : Strategy

{

	private readonly StrategyParam<int> _fastPeriod;

	private readonly StrategyParam<int> _slowPeriod;

	private readonly StrategyParam<DataType> _candleType;



	private decimal _prevFast; private decimal _prevSlow; private bool _hasPrev;

	private int _cooldown;



	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }

	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }



	public ForexProfitSystemStrategy()

	{

		_fastPeriod = Param(nameof(FastPeriod), 10).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 25).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");

	}



	/// <inheritdoc />

	protected override void OnReseted()

	{

		base.OnReseted();

		_prevFast = default;

		_prevSlow = default;

		_hasPrev = default;

		_cooldown = default;

	}



	/// <inheritdoc />

	protected override void OnStarted2(DateTime time)

	{

		base.OnStarted2(time);

		_hasPrev = false;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };

		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription.Bind(fast, slow, ProcessCandle).Start();

	}



	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)

	{

		if (candle.State != CandleStates.Finished) return;

		if (!IsFormedAndOnlineAndAllowTrading()) return;

		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldown > 0)

		{

			_cooldown--;

			_prevFast = fast; _prevSlow = slow;

			return;

		}



		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)

		{

			var volume = Volume + Math.Abs(Position);

			BuyMarket(volume);

			_cooldown = 2;

		}

		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)

		{

			var volume = Volume + Math.Abs(Position);

			SellMarket(volume);

			_cooldown = 2;

		}

		_prevFast = fast; _prevSlow = slow;

	}

}