Стратегия Plan X воспроизводит советник MetaTrader «plan x» Питера Ингрэма. Она ориентирована на европейскую сессию и наблюдает за 15-минутными свечами. После фиксирования контрольной свечи стратегия ожидает пробоя на заданное количество пунктов и открывает одну чистую позицию. Управление риском выполняется через стоп-лоссы и трейлинг-стопы, выраженные в пунктах.
Логика торговли
Опорная свеча
Анализируются 15-минутные свечи.
В момент начала торгового окна (по умолчанию 11:00) сохраняется цена закрытия свечи. Она служит опорной ценой до конца окна.
Сигналы рассматриваются только после закрытия следующей свечи и до окончания окна (по умолчанию 15:00).
Условия входа
Покупка: последняя завершённая свеча закрылась выше опорной цены более чем на LongTargetPips (по умолчанию 25 пунктов), позиция отсутствует.
Продажа: последняя завершённая свеча закрылась ниже опорной цены более чем на ShortTargetPips (по умолчанию 20 пунктов), позиция отсутствует.
Сравнения выполняются в пунктах, рассчитываемых из шага цены инструмента.
Сопровождение позиций
Сразу после входа устанавливается фиксированный стоп-лосс на расстоянии InitialStopPips (по умолчанию 25 пунктов).
Когда прибыль достигает TrailTriggerPips (по умолчанию 10 пунктов), стоп превращается в трейлинг.
Каждый раз, когда цена проходит ещё TrailTriggerPips, стоп сдвигается на TrailStepPips (по умолчанию 5 пунктов) в прибыльную сторону.
При срабатывании стопа позиция закрывается рыночной заявкой.
Объём
Входы и выходы используют параметр TradeVolume (по умолчанию 0.1 лота). Подберите значение под контракт инструмента.
Параметры
Имя
Описание
Значение по умолчанию
TradeVolume
Объём рыночных заявок.
0.1
LongTargetPips
Минимальный пробой вверх от опорной цены.
25
ShortTargetPips
Минимальный пробой вниз от опорной цены.
20
InitialStopPips
Расстояние стоп-лосса от цены входа.
25
TrailTriggerPips
Прибыль, после которой активируется/обновляется трейлинг.
10
TrailStepPips
Шаг сдвига трейлинг-стопа.
5
SessionStartHour
Час начала окна (десятичный формат, например 11.5 = 11:30).
11.0
SessionEndHour
Час завершения окна. Должен быть больше SessionStartHour.
15.0
CandleType
Тип свечей для расчётов (по умолчанию 15 минут).
15 минут
Примечания
Размер пункта определяется автоматически по PriceStep и количеству десятичных знаков инструмента (для 3 и 5 знаков множитель 10).
Опорная цена пересчитывается каждый торговый день; на инструментах с гэпами учитывайте возможные разрывы.
В реализации StockSharp используется чистая позиция, поэтому стратегия открывает только одно направление одновременно, что соответствует оригинальному советнику без хеджирования.
Файлы
CS/PlanXBreakoutStrategy.cs – реализация логики Plan X на C# для StockSharp.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Plan X Breakout strategy using highest high / lowest low channel breakout.
/// Buy when price breaks above the highest high of the lookback period.
/// Sell when price breaks below the lowest low of the lookback period.
/// </summary>
public class PlanXBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private bool _hasPrev;
public int Lookback { get => _lookback.Value; set => _lookback.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public PlanXBreakoutStrategy()
{
_lookback = Param(nameof(Lookback), 20)
.SetDisplay("Lookback", "Channel lookback period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0m;
_prevLow = 0m;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var highestHigh = new Highest { Length = Lookback };
var lowestLow = new Lowest { Length = Lookback };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highestHigh, lowestLow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevHigh = highest;
_prevLow = lowest;
_hasPrev = true;
return;
}
// Breakout above previous highest high
if (Position <= 0 && candle.ClosePrice > _prevHigh)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Breakout below previous lowest low
else if (Position >= 0 && candle.ClosePrice < _prevLow)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevHigh = highest;
_prevLow = lowest;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class plan_x_breakout_strategy(Strategy):
"""Plan X Breakout strategy using highest high / lowest low channel breakout.
Buy when price breaks above the highest high of the lookback period.
Sell when price breaks below the lowest low of the lookback period."""
def __init__(self):
super(plan_x_breakout_strategy, self).__init__()
self._lookback = self.Param("Lookback", 20) \
.SetDisplay("Lookback", "Channel lookback period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Lookback(self):
return self._lookback.Value
def OnReseted(self):
super(plan_x_breakout_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(plan_x_breakout_strategy, self).OnStarted2(time)
self._has_prev = False
highest = Highest()
highest.Length = self.Lookback
lowest = Lowest()
lowest.Length = self.Lookback
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._process_candle).Start()
def _process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished:
return
high_val = float(highest)
low_val = float(lowest)
close = float(candle.ClosePrice)
if not self._has_prev:
self._prev_high = high_val
self._prev_low = low_val
self._has_prev = True
return
# Breakout above previous highest high
if self.Position <= 0 and close > self._prev_high:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Breakout below previous lowest low
elif self.Position >= 0 and close < self._prev_low:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = high_val
self._prev_low = low_val
def CreateClone(self):
return plan_x_breakout_strategy()