Стратегия 20/200 Expert
Стратегия открывает сделки на основе разницы между ценами открытия двух прошлых баров. Лонг открывается, когда открытие на смещении shift2 минус открытие на смещении shift1 превышает порог, шорт — при обратном условии. Сделки открываются только в указанный час и закрываются по тейк-профиту, стоп-лоссу или по истечении максимального времени удержания.
Детали
- Критерии входа:
- Лонг: open[Shift2] - open[Shift1] > DeltaLong пунктов.
- Шорт: open[Shift1] - open[Shift2] > DeltaShort пунктов.
- Длинные/короткие: обе стороны.
- Критерии выхода: тейк-профит, стоп-лосс или максимальное время удержания.
- Стопы: фиксированные стоп-лосс и тейк-профит в пунктах.
- Значения по умолчанию:
- Shift1 = 6
- Shift2 = 2
- DeltaLong = 6 пунктов
- DeltaShort = 21 пункт
- TakeProfitLong = 390 пунктов
- StopLossLong = 1470 пунктов
- TakeProfitShort = 320 пунктов
- StopLossShort = 2670 пунктов
- TradeHour = 14
- MaxOpenTime = 504 часа
- Volume = 0.1
- Таймфрейм свечей = 1 час
- Фильтры:
- Категория: Моментум
- Направление: Лонг и Шорт
- Индикаторы: Нет
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Часовой
- Сезонность: Временной фильтр
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Strategy that compares the opening prices of two past bars.
/// Goes long if the second shifted open exceeds the first by a threshold
/// and short if the opposite condition is met. Trades only at a specified hour
/// and exits via fixed take profit, stop loss or timeout.
/// </summary>
public class Twenty200ExpertStrategy : Strategy
{
private readonly StrategyParam<int> _shift1;
private readonly StrategyParam<int> _shift2;
private readonly StrategyParam<int> _deltaLong;
private readonly StrategyParam<int> _deltaShort;
private readonly StrategyParam<int> _takeProfitLong;
private readonly StrategyParam<int> _stopLossLong;
private readonly StrategyParam<int> _takeProfitShort;
private readonly StrategyParam<int> _stopLossShort;
private readonly StrategyParam<int> _tradeHour;
private readonly StrategyParam<int> _maxOpenTime;
private readonly StrategyParam<decimal> _volume;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _opens = new();
private decimal _entryPrice;
private DateTimeOffset _entryTime;
/// <summary>
/// First bar shift.
/// </summary>
public int Shift1 { get => _shift1.Value; set => _shift1.Value = value; }
/// <summary>
/// Second bar shift.
/// </summary>
public int Shift2 { get => _shift2.Value; set => _shift2.Value = value; }
/// <summary>
/// Difference for long entry in points.
/// </summary>
public int DeltaLong { get => _deltaLong.Value; set => _deltaLong.Value = value; }
/// <summary>
/// Difference for short entry in points.
/// </summary>
public int DeltaShort { get => _deltaShort.Value; set => _deltaShort.Value = value; }
/// <summary>
/// Take profit for long positions in points.
/// </summary>
public int TakeProfitLong { get => _takeProfitLong.Value; set => _takeProfitLong.Value = value; }
/// <summary>
/// Stop loss for long positions in points.
/// </summary>
public int StopLossLong { get => _stopLossLong.Value; set => _stopLossLong.Value = value; }
/// <summary>
/// Take profit for short positions in points.
/// </summary>
public int TakeProfitShort { get => _takeProfitShort.Value; set => _takeProfitShort.Value = value; }
/// <summary>
/// Stop loss for short positions in points.
/// </summary>
public int StopLossShort { get => _stopLossShort.Value; set => _stopLossShort.Value = value; }
/// <summary>
/// Hour when new positions can be opened.
/// </summary>
public int TradeHour { get => _tradeHour.Value; set => _tradeHour.Value = value; }
/// <summary>
/// Maximum time to hold a position in hours.
/// </summary>
public int MaxOpenTime { get => _maxOpenTime.Value; set => _maxOpenTime.Value = value; }
/// <summary>
/// Order volume.
/// </summary>
public decimal OrderVolume { get => _volume.Value; set => _volume.Value = value; }
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initializes a new instance of the <see cref="Twenty200ExpertStrategy"/>.
/// </summary>
public Twenty200ExpertStrategy()
{
_shift1 = Param(nameof(Shift1), 6)
.SetGreaterThanZero()
.SetDisplay("Shift 1", "Shift of first bar", "Signals")
;
_shift2 = Param(nameof(Shift2), 2)
.SetGreaterThanZero()
.SetDisplay("Shift 2", "Shift of second bar", "Signals")
;
_deltaLong = Param(nameof(DeltaLong), 20)
.SetGreaterThanZero()
.SetDisplay("Delta Long", "Price difference for long in points", "Signals")
;
_deltaShort = Param(nameof(DeltaShort), 40)
.SetGreaterThanZero()
.SetDisplay("Delta Short", "Price difference for short in points", "Signals")
;
_takeProfitLong = Param(nameof(TakeProfitLong), 390)
.SetDisplay("Take Profit Long", "Take profit for long in points", "Risk")
;
_stopLossLong = Param(nameof(StopLossLong), 1470)
.SetDisplay("Stop Loss Long", "Stop loss for long in points", "Risk")
;
_takeProfitShort = Param(nameof(TakeProfitShort), 320)
.SetDisplay("Take Profit Short", "Take profit for short in points", "Risk")
;
_stopLossShort = Param(nameof(StopLossShort), 2670)
.SetDisplay("Stop Loss Short", "Stop loss for short in points", "Risk")
;
_tradeHour = Param(nameof(TradeHour), 12)
.SetRange(0, 23)
.SetDisplay("Trade Hour", "Hour to open positions", "Signals")
;
_maxOpenTime = Param(nameof(MaxOpenTime), 504)
.SetGreaterThanZero()
.SetDisplay("Max Open Time", "Maximum position time in hours", "Risk")
;
_volume = Param(nameof(OrderVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Volume", "Order volume", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_opens.Clear();
_entryPrice = 0m;
_entryTime = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_opens.Enqueue(candle.OpenPrice);
var maxShift = Math.Max(Shift1, Shift2) + 1;
if (_opens.Count > maxShift)
_opens.Dequeue();
var priceStep = Security.PriceStep ?? 1m;
if (Position > 0)
{
var tp = _entryPrice + TakeProfitLong * priceStep;
var sl = _entryPrice - StopLossLong * priceStep;
var timedOut = MaxOpenTime > 0 && (candle.OpenTime - _entryTime).TotalHours >= MaxOpenTime;
if (candle.HighPrice >= tp || candle.LowPrice <= sl || timedOut)
{
SellMarket();
_entryPrice = 0m;
_entryTime = default;
}
}
else if (Position < 0)
{
var tp = _entryPrice - TakeProfitShort * priceStep;
var sl = _entryPrice + StopLossShort * priceStep;
var timedOut = MaxOpenTime > 0 && (candle.OpenTime - _entryTime).TotalHours >= MaxOpenTime;
if (candle.LowPrice <= tp || candle.HighPrice >= sl || timedOut)
{
BuyMarket();
_entryPrice = 0m;
_entryTime = default;
}
}
if (_opens.Count < maxShift)
return;
if (Position != 0)
return;
var arr = _opens.ToArray();
var openShift1 = arr[arr.Length - 1 - Shift1];
var openShift2 = arr[arr.Length - 1 - Shift2];
var diffLong = openShift2 - openShift1;
var diffShort = openShift1 - openShift2;
var thLong = DeltaLong * priceStep;
var thShort = DeltaShort * priceStep;
if (candle.OpenTime.Hour != TradeHour)
return;
if (diffLong > thLong && diffShort <= thShort)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_entryTime = candle.OpenTime;
}
else if (diffShort > thShort && diffLong <= thLong)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_entryTime = candle.OpenTime;
}
}
}
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.Strategies import Strategy
from collections import deque
class twenty_200_expert_strategy(Strategy):
def __init__(self):
super(twenty_200_expert_strategy, self).__init__()
self._shift1 = self.Param("Shift1", 6) \
.SetDisplay("Shift 1", "First bar shift", "Signals")
self._shift2 = self.Param("Shift2", 2) \
.SetDisplay("Shift 2", "Second bar shift", "Signals")
self._delta_long = self.Param("DeltaLong", 20) \
.SetDisplay("Delta Long", "Long threshold", "Signals")
self._delta_short = self.Param("DeltaShort", 40) \
.SetDisplay("Delta Short", "Short threshold", "Signals")
self._tp_long = self.Param("TakeProfitLong", 390) \
.SetDisplay("TP Long", "TP for long", "Risk")
self._sl_long = self.Param("StopLossLong", 1470) \
.SetDisplay("SL Long", "SL for long", "Risk")
self._tp_short = self.Param("TakeProfitShort", 320) \
.SetDisplay("TP Short", "TP for short", "Risk")
self._sl_short = self.Param("StopLossShort", 2670) \
.SetDisplay("SL Short", "SL for short", "Risk")
self._trade_hour = self.Param("TradeHour", 12) \
.SetDisplay("Trade Hour", "Hour to open positions", "Signals")
self._max_open_time = self.Param("MaxOpenTime", 504) \
.SetDisplay("Max Open Time", "Maximum position time in hours", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._opens = []
self._entry_price = 0.0
self._entry_time = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(twenty_200_expert_strategy, self).OnReseted()
self._opens = []
self._entry_price = 0.0
self._entry_time = None
def OnStarted2(self, time):
super(twenty_200_expert_strategy, self).OnStarted2(time)
sub = self.SubscribeCandles(self.candle_type)
sub.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._opens.append(float(candle.OpenPrice))
max_shift = max(self._shift1.Value, self._shift2.Value) + 1
while len(self._opens) > max_shift:
self._opens.pop(0)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self.Position > 0:
tp = self._entry_price + self._tp_long.Value * step
sl = self._entry_price - self._sl_long.Value * step
timed_out = self._max_open_time.Value > 0 and self._entry_time is not None and \
(candle.OpenTime - self._entry_time).TotalHours >= self._max_open_time.Value
if high >= tp or low <= sl or timed_out:
self.SellMarket()
self._entry_price = 0.0
self._entry_time = None
elif self.Position < 0:
tp = self._entry_price - self._tp_short.Value * step
sl = self._entry_price + self._sl_short.Value * step
timed_out = self._max_open_time.Value > 0 and self._entry_time is not None and \
(candle.OpenTime - self._entry_time).TotalHours >= self._max_open_time.Value
if low <= tp or high >= sl or timed_out:
self.BuyMarket()
self._entry_price = 0.0
self._entry_time = None
if len(self._opens) < max_shift:
return
if self.Position != 0:
return
arr = self._opens
open_s1 = arr[len(arr) - 1 - self._shift1.Value]
open_s2 = arr[len(arr) - 1 - self._shift2.Value]
diff_long = open_s2 - open_s1
diff_short = open_s1 - open_s2
th_long = self._delta_long.Value * step
th_short = self._delta_short.Value * step
if candle.OpenTime.Hour != self._trade_hour.Value:
return
if diff_long > th_long and diff_short <= th_short:
self.BuyMarket()
self._entry_price = close
self._entry_time = candle.OpenTime
elif diff_short > th_short and diff_long <= th_long:
self.SellMarket()
self._entry_price = close
self._entry_time = candle.OpenTime
def CreateClone(self):
return twenty_200_expert_strategy()