Ver en GitHub

Back Kick Strategy

The Back Kick strategy is a hedged breakout system converted from the MetaTrader 5 expert advisor Back kick.mq5. It continuously maintains a two-sided exposure by opening both a long and a short market position at the close of every bar. Each leg is protected with symmetrical stop-loss and take-profit distances expressed in pips. The StockSharp port keeps the paired positions independent by tracking their state manually instead of relying on the aggregated net position.

Trading Logic

  1. Subscribe to the configured time-frame candles. When a candle closes and there are no active hedged legs, request a new pair of entries.
  2. Immediately submit a market buy and a market sell order using the same volume. Each leg keeps its own stop-loss and take-profit offsets converted from pip distances.
  3. Monitor best bid/ask prices from Level 1 data. If a leg reaches its protective price it is closed with a market order, while the opposite leg remains active until its own exit is triggered.
  4. After both legs are flat, the strategy waits for the next completed candle before recreating the hedge.

This behaviour mirrors the original expert, which constantly re-enters in both directions to capture abrupt price "kicks".

Parameters

Name Description Default Notes
OrderVolume Volume used for each hedge leg. 0.1 Normalised to the instrument VolumeStep, must respect MinVolume/MaxVolume.
StopLossPips Stop-loss distance in pips. 50 Set to 0 to disable the protective stop for both legs.
TakeProfitPips Take-profit distance in pips. 140 Set to 0 to disable the protective take-profit.
CandleType Time-frame that triggers new hedged pairs. 15m Accepts any TimeFrame supported by the selected security.
LogDiagnostics Enables verbose logging about entries and exits. false Useful for debugging fill sequences.

Implementation Notes

  • Pip conversion – The original EA adjusts pip size for 3/5 digit symbols. The StockSharp port replicates this by multiplying the price step with 10 when necessary.
  • Manual hedging model – StockSharp uses net positions, so the strategy keeps per-leg state (PositionState) and dispatches explicit market orders for exits. This allows the behaviour to resemble the MT5 hedged account mode.
  • Risk management – Stop-loss and take-profit levels are optional. If either is disabled, that leg will only close when the opposite protective level is hit or via external management.
  • Protection serviceStartProtection() is still invoked to let the framework monitor unexpected disconnects even though custom exit logic is implemented.

Usage

  1. Attach the strategy to a security with reliable Level 1 data (bid/ask) and the desired time-frame candles.
  2. Configure the pip distances and trade volume according to your risk profile.
  3. Start the strategy; it will wait for the next candle close before submitting the hedged pair.
  4. Monitor the logs or chart to observe how each leg exits independently.

Differences from the MT5 Version

  • Money-management based on risk percentage is not transferred; use OrderVolume to control trade size.
  • Because StockSharp aggregates portfolio positions, the strategy emulates hedging through internal bookkeeping. This ensures behaviour close to the original expert while remaining compatible with brokers that net positions.
  • Broker-specific freeze/stop level checks are omitted. Instead, the volume normalisation routine throws descriptive exceptions if exchange limits are violated.

Files

  • CS/BackKickStrategy.cs – Strategy implementation using the high-level StockSharp API.
  • README.md – English documentation (this file).
  • README_ru.md – Russian documentation.
  • README_zh.md – Chinese documentation.
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 BackKickStrategy : 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 BackKickStrategy()
	{
		_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;
	}
}