Ver no GitHub

Parallax Sell Strategy

Overview

Parallax Sell is a short-only martingale strategy converted from the MetaTrader expert advisor parallax_sell. The original robot traded JPY crosses (CAD/JPY and CHF/JPY) and relies on a confluence of Williams %R, MACD and stochastic oscillator filters to initiate shorts into overbought rallies. Position exits depend on momentum fading signs provided by Williams %R or a slow stochastic, while a martingale-like position sizing scheme increases exposure after losing sequences.

Entry Logic

  • Work on the configurable timeframe (default: 1-hour candles).
  • Wait for a fresh candle close.
  • Require Williams %R (entry lookback 350) to be above the overbought threshold (default -10).
  • Require the MACD main line (12/120/9 settings) to stay above a bullish threshold (default 0.178) to confirm upward momentum before fading it.
  • Detect a downward cross of the fast stochastic %K (length 10, slowing 3) below the entry trigger level (default 90). Only this cross event can produce a new short.
  • Every qualified signal sends an additional market sell order. Multiple short orders can stack, following the martingale volume logic.

Exit Logic

  • Track the floating profit of all open shorts in pips using the instrument pip size.
  • If only one short is open and the average profit exceeds the single-trade target (default 10 pips) and Williams %R drops below the exit threshold (default -80), close the position.
  • If more than one short is open and the average basket profit exceeds the basket target (default 15 pips) and the slow stochastic %K (length 90, slowing 1) falls below the oversold trigger (default 12), close the entire basket.
  • An additional safety take-profit closes the basket when the average gain reaches the configured take-profit distance (default 100 pips).

Position Sizing

  • Start with the base volume (default 0.01 lots).
  • After a profitable cycle (realized PnL increase), reset the next order volume to the base volume.
  • After a losing cycle (realized PnL decrease), multiply the next order volume by the martingale multiplier (default 1.6). Volumes are automatically aligned to the instrument volume step.

Risk Management

  • The strategy registers a protective take-profit order using the configured pip distance. No fixed stop loss is used; exits are driven by indicator filters.
  • Start protection is engaged once, as required by the StockSharp conversion guidelines.

Parameters

Name Description Default
CandleType Timeframe used for calculations. 1H candles
EntryWilliamsLength Williams %R lookback for entries. 350
ExitWilliamsLength Williams %R lookback for exits. 350
EntryStochasticLength / Signal / Slowing Fast stochastic settings for the entry cross. 10 / 1 / 3
ExitStochasticLength / Signal / Slowing Slow stochastic settings for exit confirmation. 90 / 7 / 1
MacdFastLength / MacdSlowLength / MacdSignalLength MACD parameters. 12 / 120 / 9
EntryWilliamsThreshold Minimum Williams %R value required before shorting. -10
ExitWilliamsThreshold Williams %R level that confirms exit for a single trade. -80
EntryStochasticTrigger Level the fast stochastic must cross downward to trigger entries. 90
ExitStochasticTrigger Level the slow stochastic must drop below to close baskets. 12
MacdThreshold Minimum MACD main-line value. 0.178
SingleTradeTargetPips Profit target (pips) when only one short is active. 10
MultiTradeTargetPips Profit target (pips) when multiple shorts are active. 15
TakeProfitPips Hard take-profit distance (pips). 100
InitialVolume Base order size. 0.01
MartingaleMultiplier Multiplier applied after a loss when martingale is enabled. 1.6
UseMartingale Enable or disable martingale escalation. true

Notes

  • The strategy only trades short positions and assumes Forex-like pip conventions when measuring profits.
  • The average profit calculations treat each entry equally, mirroring the MetaTrader block that averaged pips per trade.
  • Adjust thresholds or disable martingale (UseMartingale = false) to reduce risk on highly volatile pairs.
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 ParallaxSellStrategy : 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 ParallaxSellStrategy()
	{
		_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;
	}
}