Главная
/
Примеры стратегий
Открыть на GitHub
Стратегия Hans123Trader (StockSharp)
Общее описание
Стратегия Hans123Trader повторяет логику советника MetaTrader "Hans123Trader v1" на высокоуровневом API StockSharp. Алгоритм дважды в день выставляет стоп-заявки на пробой диапазона, сформированного последними 80 пятиминутными свечами. Решение ориентировано на валютные инструменты, где шаг цены соответствует дробной величине пункта. В начале каждого нового торгового дня все отложенные ордера обновляются, а открытые позиции принудительно закрываются.
Последовательность работы
Отслеживание диапазона. Индикаторы Highest и Lowest ведут скользящее окно из 80 свечей таймфрейма 5 минут. Максимум и минимум определяют уровни потенциального пробоя.
Планирование сессий. Торговые окна задаются параметрами EndSession1 и EndSession2. Как только наступает соответствующий час (при минуте 00), стратегия пересчитывает и выставляет новые стоп-заявки.
Постановка ордеров. Бай-стоп размещается на 5 пунктов выше диапазона, селл-стоп — на 5 пунктов ниже. При смене календарного дня заявки снимаются, имитируя истечение срока действия в MetaTrader в 23:59.
Ведение позиции. После входа применяются стартовый стоп-лосс, опциональный тейк-профит и трейлинг-стоп. Все защитные уровни задаются в пунктах и переводятся в цену через PriceStep инструмента.
Ежедневная очистка. При переходе на новый день открытая позиция закрывается рыночной сделкой. Отложенные ордера прошлого дня отменяются прежде, чем будут подготовлены новые.
Правила торговли
Условия входа
Две попытки входа в сутки: первая в час EndSession1, вторая — в час EndSession2 (по времени сервера брокера).
Цена бай-стопа = High диапазона + 5 пунктов. Цена селл-стопа = Low диапазона − 5 пунктов.
Объём ордеров берётся из параметра Volume (по умолчанию 1).
Если объём ≤ 0, заявки не выставляются.
Условия выхода
Начальный стоп-лосс = цена входа ± InitialStopLoss пунктов (ниже для покупок, выше для продаж).
Тейк-профит = цена входа ± TakeProfit пунктов (выше для покупок, ниже для продаж).
Трейлинг-стоп подтягивает защитный уровень, когда цена закрытия уходит в прибыль минимум на TrailingStop пунктов.
Позиция, оставшаяся до следующего дня, закрывается немедленно рыночным ордером.
Обслуживание ордеров
Отложенные стоп-заявки отменяются в начале каждого календарного дня.
Как только заявка исполняется, отменяется или завершается с ошибкой, ссылка на неё очищается.
Параметры
Параметр
Назначение
BeginSession1 / BeginSession2
Сохранены для совместимости (подсказка начала окна); текущая реализация использует конечные часы.
EndSession1 / EndSession2
Часы (0–23), в которые выставляются новые стоп-заявки. Минуты должны равняться нулю.
TrailingStop
Дистанция трейлинг-стопа в пунктах. 0 отключает трейлинг.
TakeProfit
Дистанция тейк-профита в пунктах. 0 отключает тейк-профит.
InitialStopLoss
Начальный стоп-лосс в пунктах. 0 оставляет сделку без стопа (пока не сработает трейлинг).
CandleType
Серия свечей для расчёта диапазона (по умолчанию 5 минут).
Volume
Базовый объём стратегии.
Особенности конверсии
Функция MetaTrader OrderSendExtended и блокировка через глобальные переменные не потребовались: управление очередями заявок берет на себя StockSharp.
Вместо magic-номеров используются явные поля _session*, которые очищаются при завершении жизненного цикла ордера.
Истечение отложенных заявок в 23:59 заменено отменой при наступлении нового дня.
Для трейлинга используются цены закрытия свечей как приближение котировок Bid/Ask из MetaTrader.
Параметры в пунктах переводятся в абсолютную цену путём умножения на Security.PriceStep. Если шаг цены не задан, значения трактуются как абсолютные.
Рекомендации по применению
Назначайте инструменты с корректно настроенными PriceStep, StepPrice и VolumeStep, иначе пересчёт пунктов и объёмов будет некорректен.
Убедитесь, что доступна история пятиминутных свечей: пробойные уровни строятся по последним 80 барам.
Настраивайте EndSession1/EndSession2 под нужные торговые сессии (например, перед началом Лондона и Нью-Йорка).
Перед запуском протестируйте и оптимизируйте InitialStopLoss, TakeProfit и TrailingStop в Designer или Runner для конкретного инструмента.
Дополнительно подключайте риск-менеджмент StockSharp, если несколько стратегий работают на одном портфеле.
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;
/// <summary>
/// Hans123 breakout strategy. Builds a range from recent highest/lowest prices
/// and enters on breakout above range high or below range low.
/// </summary>
public class Hans123TraderRangeBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _rangeLength;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _prevHighest;
private decimal _prevLowest;
public Hans123TraderRangeBreakoutStrategy()
{
_rangeLength = Param(nameof(RangeLength), 20)
.SetDisplay("Range Length", "Number of candles used to compute the breakout range.", "Breakout");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle series for range detection.", "General");
}
public int RangeLength
{
get => _rangeLength.Value;
set => _rangeLength.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevHighest = 0;
_prevLowest = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_prevHighest = 0;
_prevLowest = 0;
var highest = new Highest { Length = RangeLength };
var lowest = new Lowest { Length = RangeLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highestValue, decimal lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
if (highestValue <= 0 || lowestValue <= 0)
{
_prevHighest = highestValue;
_prevLowest = lowestValue;
return;
}
// Entry on breakout using previous levels
if (Position == 0 && _prevHighest > 0 && _prevLowest > 0)
{
if (candle.ClosePrice > _prevHighest)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (candle.ClosePrice < _prevLowest)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
_prevHighest = highestValue;
_prevLowest = lowestValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import Highest, Lowest
class hans123_trader_range_breakout_strategy(Strategy):
def __init__(self):
super(hans123_trader_range_breakout_strategy, self).__init__()
self._range_length = self.Param("RangeLength", 20) \
.SetDisplay("Range Length", "Number of candles used to compute the breakout range", "Breakout")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle series for range detection", "General")
self._entry_price = 0.0
self._stop_price = 0.0
self._prev_highest = 0.0
self._prev_lowest = 0.0
@property
def RangeLength(self):
return self._range_length.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(hans123_trader_range_breakout_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._stop_price = 0.0
self._prev_highest = 0.0
self._prev_lowest = 0.0
self._highest = Highest()
self._highest.Length = self.RangeLength
self._lowest = Lowest()
self._lowest.Length = self.RangeLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._highest, self._lowest, self.ProcessCandle).Start()
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(1, UnitTypes.Percent)
)
def ProcessCandle(self, candle, highest_value, lowest_value):
if candle.State != CandleStates.Finished:
return
hv = float(highest_value)
lv = float(lowest_value)
if hv <= 0 or lv <= 0:
self._prev_highest = hv
self._prev_lowest = lv
return
# Entry on breakout using previous levels
if self.Position == 0 and self._prev_highest > 0 and self._prev_lowest > 0:
close = float(candle.ClosePrice)
if close > self._prev_highest:
self.BuyMarket()
self._entry_price = close
elif close < self._prev_lowest:
self.SellMarket()
self._entry_price = close
self._prev_highest = hv
self._prev_lowest = lv
def OnReseted(self):
super(hans123_trader_range_breakout_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = 0.0
self._prev_highest = 0.0
self._prev_lowest = 0.0
def CreateClone(self):
return hans123_trader_range_breakout_strategy()