View on GitHub

ZigZag Climber Strategy

Overview

The ZigZag Climber expert advisor generated by fxDreema contains only three blocks: a No trade filter followed by Buy now and Sell now actions. Once the terminal detects that no positions are open, it immediately fires a market buy order with predefined stop-loss and take-profit levels and, without any further checks, places a symmetric market sell order. Both trades inherit the same risk parameters and are meant to coexist as a hedged pair.

This C# port reproduces that behaviour in StockSharp by waiting for the first finished candle of the selected timeframe and then submitting the buy and sell legs back-to-back with identical protective distances. No additional signal generation, trailing logic or position management is present—exactly like in the source MQL project.

Trading Logic

  1. Wait until the first candle of the configured timeframe is completely formed.
  2. If the strategy is allowed to trade and no orders have been placed yet, submit a market buy using the fixed volume.
  3. Attach stop-loss and take-profit orders to the long trade using MetaTrader-style pip distances (converted through the instrument PriceStep).
  4. Immediately submit a market sell with the same volume and attach mirrored protective levels.
  5. Do not open any further orders for the rest of the run.

Important: MetaTrader 4 works in hedging mode, so both sides can stay open simultaneously. StockSharp uses the broker's execution model; on netting accounts the second order will offset the first one and the strategy will finish flat. Use a hedging-capable connector (e.g., MetaTrader gateway configured for hedge accounts) if you want to keep both legs alive.

Parameters

Name Default Description
Candle Type 1 minute Timeframe that triggers the one-off entry sequence.
Trade Volume 0.01 Fixed volume applied to both market orders.
Stop-Loss (pips) 99.9 Distance of the protective stop in MetaTrader pips (handles 4/5-digit symbols automatically).
Take-Profit (pips) 100 Target distance in MetaTrader pips.

All distances are converted to price points via the instrument PriceStep and decimal precision before being passed to SetStopLoss/SetTakeProfit.

Risk Management

The strategy relies on the built-in StartProtection() service and the helper methods SetStopLoss/SetTakeProfit to place protective orders right after each market order. There is no trailing or break-even logic.

Usage Notes

  • Assign the desired security and portfolio before starting the strategy. Ensure the symbol exposes PriceStep and Decimals so pip conversion works correctly.
  • Because the entry logic runs only once, restarting the strategy is the only way to create a new hedge cycle.
  • When testing on a netting simulator the realised behaviour will differ from MetaTrader: the sell order will neutralise the buy order almost immediately.
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// ZigZag Climber strategy: Highest/Lowest channel breakout.
/// Buys when close >= highest, sells when close <= lowest.
/// </summary>
public class ZigZagClimberStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _channelPeriod;

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

	public int ChannelPeriod
	{
		get => _channelPeriod.Value;
		set => _channelPeriod.Value = value;
	}

	public ZigZagClimberStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_channelPeriod = Param(nameof(ChannelPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators");
	}

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

		var high = new Highest { Length = ChannelPeriod };
		var low = new Lowest { Length = ChannelPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (candle.ClosePrice >= high && Position <= 0)
		{
			BuyMarket();
		}
		else if (candle.ClosePrice <= low && Position >= 0)
		{
			SellMarket();
		}
	}
}