Ten Pips EURUSD — это стратегия пробоя диапазона предыдущей свечи. Логика полностью повторяет одноимённого советника для MetaTrader: после закрытия свечи выставляются отложенные stop-заявки выше и ниже диапазона в расчёте на движение на 10 и более пунктов. Реализация использует высокоуровневые подписки StockSharp на свечи и стакан, поэтому алгоритм легко переносится на любые торговые площадки.
Алгоритм работы
Подписаться на выбранный тип свечей и дождаться появления новой активной свечи.
При закрытии свечи сохранить её максимум и минимум. В этот же момент отменяются все отложенные заявки — советник допускает их жизнь только в рамках одного бара.
На первом тике новой свечи выполнить проверки:
Открытие новой свечи находится внутри диапазона предыдущей (фильтр ценовых разрывов).
Текущая цена отстоит от экстремумов как минимум на три пункта — приблизительная оценка минимально допустимого стопа у брокера.
Рассчитать актуальный спред по лучшим Bid/Ask. Если поток котировок недоступен, использовать величину пункта.
Выставить две отложенные заявки:
Buy Stop на уровне high прошлого бара + 2 × спред со стопом StopLossPips и, если трейлинг отключён, тейк-профитом TakeProfitPips выше цены активации.
Sell Stop на уровне low прошлого бара − спред с зеркальными параметрами.
После завершения свечи повторить процедуру для следующего бара.
Управление позицией
Пока позиция открыта, стратегия отслеживает лучшие цены покупки/продажи и корректирует защитные уровни.
При отключенном трейлинг-стопе позиция закрывается при достижении фиксированного стоп-лосса или тейк-профита.
При включённом трейлинге:
Для лонга трейлинг активируется, когда цена проходит TrailingStopPips в прибыль. Стоп переносится на bid − TrailingStopPips и двигается при каждом дополнительном движении минимум на TrailingStepPips.
Для шорта используется зеркальная логика относительно цены ask.
Если позиция закрыта вручную или по рынку, защитные уровни сбрасываются. Оставшаяся противоположная stop-заявка остаётся активной до завершения свечи, что имитирует поведение оригинального советника.
Параметры
Имя
Значение по умолчанию
Описание
Volume
0.01
Объём ордеров в лотах или контрактах.
StopLossPips
50
Дистанция от входа до стоп-лосса в пунктах.
TakeProfitPips
150
Дистанция до тейк-профита (работает только без трейлинга).
UseTrailing
false
Включение трейлинг-стопа.
TrailingStopPips
50
Первоначальная дистанция трейлинга в пунктах.
TrailingStepPips
25
Шаг, на который должна продвинуться цена, чтобы подтянуть стоп.
CandleType
15 минут
Тип свечей, по которым рассчитывается диапазон.
Дополнительные замечания
Размер пункта определяется автоматически на основе Security.PriceStep, что позволяет корректно работать с инструментами в 3 и 5 знаков.
Все расстояния переводятся в денежные единицы инструмента перед размещением заявок, поэтому стратегию можно применять не только на валютном рынке при наличии заданного шага цены.
Минимальный уровень стопа по умолчанию равен трём пунктам, что повторяет поведение MT5-советника при отсутствии данных от брокера.
Поскольку заявки действуют только в течение одной свечи, важно обеспечивать непрерывный поток данных по выбранному таймфрейму.
Перед использованием на реальном счёте рекомендуется провести тесты с реальными спредами, комиссиями и проскальзыванием.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy: enters long when price breaks above previous candle high,
/// enters short when price breaks below previous candle low.
/// Uses ATR-based stop loss and take profit.
/// </summary>
public class TenPipsEurusdStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossMult;
private readonly StrategyParam<decimal> _takeProfitMult;
private readonly StrategyParam<decimal> _trailingMult;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private bool _hasPrev;
public decimal StopLossMult { get => _stopLossMult.Value; set => _stopLossMult.Value = value; }
public decimal TakeProfitMult { get => _takeProfitMult.Value; set => _takeProfitMult.Value = value; }
public decimal TrailingMult { get => _trailingMult.Value; set => _trailingMult.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TenPipsEurusdStrategy()
{
_stopLossMult = Param(nameof(StopLossMult), 1.0m)
.SetGreaterThanZero()
.SetDisplay("SL Mult", "Stop loss ATR multiplier", "Risk");
_takeProfitMult = Param(nameof(TakeProfitMult), 2.0m)
.SetGreaterThanZero()
.SetDisplay("TP Mult", "Take profit ATR multiplier", "Risk");
_trailingMult = Param(nameof(TrailingMult), 0.8m)
.SetGreaterThanZero()
.SetDisplay("Trail Mult", "Trailing stop ATR multiplier", "Risk");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation length", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_entryPrice = 0;
_stopPrice = null;
_takePrice = null;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_hasPrev = true;
return;
}
var close = candle.ClosePrice;
// Manage existing position
if (Position > 0)
{
// Trail
var trail = close - TrailingMult * atr;
if (_stopPrice == null || trail > _stopPrice)
_stopPrice = trail;
if (close <= _stopPrice || (_takePrice != null && close >= _takePrice))
{
SellMarket(Math.Abs(Position));
_stopPrice = null;
_takePrice = null;
_entryPrice = 0;
}
}
else if (Position < 0)
{
var trail = close + TrailingMult * atr;
if (_stopPrice == null || trail < _stopPrice)
_stopPrice = trail;
if (close >= _stopPrice || (_takePrice != null && close <= _takePrice))
{
BuyMarket(Math.Abs(Position));
_stopPrice = null;
_takePrice = null;
_entryPrice = 0;
}
}
// Entry on breakout
if (_hasPrev && Position == 0)
{
if (close > _prevHigh + atr * 0.5m)
{
BuyMarket();
_entryPrice = close;
_stopPrice = close - StopLossMult * atr;
_takePrice = close + TakeProfitMult * atr;
}
else if (close < _prevLow - atr * 0.5m)
{
SellMarket();
_entryPrice = close;
_stopPrice = close + StopLossMult * atr;
_takePrice = close - TakeProfitMult * atr;
}
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_hasPrev = true;
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class ten_pips_eurusd_strategy(Strategy):
def __init__(self):
super(ten_pips_eurusd_strategy, self).__init__()
self._sl_mult = self.Param("StopLossMult", 1.0)
self._tp_mult = self.Param("TakeProfitMult", 2.0)
self._trail_mult = self.Param("TrailingMult", 0.8)
self._atr_period = self.Param("AtrPeriod", 14)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(ten_pips_eurusd_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
def OnStarted2(self, time):
super(ten_pips_eurusd_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(atr, self.OnProcess).Start()
def OnProcess(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
return
atr_v = float(atr_val)
close = float(candle.ClosePrice)
pos = float(self.Position)
if pos > 0:
trail = close - float(self._trail_mult.Value) * atr_v
if self._stop_price is None or trail > self._stop_price:
self._stop_price = trail
if close <= self._stop_price or (self._take_price is not None and close >= self._take_price):
self.SellMarket(abs(pos))
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
elif pos < 0:
trail = close + float(self._trail_mult.Value) * atr_v
if self._stop_price is None or trail < self._stop_price:
self._stop_price = trail
if close >= self._stop_price or (self._take_price is not None and close <= self._take_price):
self.BuyMarket(abs(pos))
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
pos = float(self.Position)
if self._has_prev and pos == 0:
if close > self._prev_high + atr_v * 0.5:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - float(self._sl_mult.Value) * atr_v
self._take_price = close + float(self._tp_mult.Value) * atr_v
elif close < self._prev_low - atr_v * 0.5:
self.SellMarket()
self._entry_price = close
self._stop_price = close + float(self._sl_mult.Value) * atr_v
self._take_price = close - float(self._tp_mult.Value) * atr_v
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
def CreateClone(self):
return ten_pips_eurusd_strategy()