Стратегия Один-Два-Три Разворот
Стратегия Один-Два-Три Разворот ищет бычий паттерн 1-2-3 в динамике цены. Длинная позиция открывается, когда выполняются условия: текущий минимум ниже вчерашнего, вчерашний минимум ниже минимума три бара назад, минимум два бара назад ниже минимума четыре бара назад, а максимум два бара назад ниже максимума три бара назад. Позиция закрывается по достижении заданного числа баров или при закрытии цены выше скользящей средней.
Детали
- Условия входа:
- Текущий минимум < предыдущего минимума.
- Предыдущий минимум < минимума три бара назад.
- Минимум два бара назад < минимума четыре бара назад.
- Максимум два бара назад < максимума три бара назад.
- Направление: Только лонг.
- Условия выхода:
- Держать
DaysToHoldбаров или закрытие выше скользящей средней.
- Держать
- Стопы: Нет.
- Значения по умолчанию:
DaysToHold= 7MaLength= 200
- Фильтры:
- Категория: Разворот
- Направление: Лонг
- Индикаторы: Прайс экшен, SMA
- Стопы: Нет
- Сложность: Низкая
- Таймфрейм: Дневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// One-Two-Three Reversal Strategy.
/// A long opens on a bullish 1-2-3 pattern: the current low is below the previous low, the previous low is below the low
/// three bars ago, the low two bars ago is below the low four bars ago and the high two bars ago is below the high three
/// bars ago. The long closes after DaysToHold bars or when the close crosses above the MaLength SMA.
/// </summary>
public class OneTwoThreeReversalStrategy : Strategy
{
private readonly StrategyParam<int> _daysToHold;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<DataType> _candleType;
private readonly List<ICandleMessage> _history = [];
private decimal? _prevClose;
private decimal? _prevMa;
private int _barsInPosition;
/// <summary>
/// Bars to hold the position.
/// </summary>
public int DaysToHold
{
get => _daysToHold.Value;
set => _daysToHold.Value = value;
}
/// <summary>
/// SMA period of the exit.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public OneTwoThreeReversalStrategy()
{
_daysToHold = Param(nameof(DaysToHold), 7)
.SetGreaterThanZero()
.SetDisplay("Days To Hold", "Bars to hold the position", "Trading");
_maLength = Param(nameof(MaLength), 200)
.SetGreaterThanZero()
.SetDisplay("MA Length", "SMA period of the exit", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_history.Clear();
_prevClose = null;
_prevMa = null;
_barsInPosition = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var sma = new SimpleMovingAverage { Length = MaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue)
{
if (candle.State != CandleStates.Finished)
return;
_history.Add(candle);
if (_history.Count > 5)
_history.RemoveAt(0);
decimal? ma = smaValue.IsFormed ? smaValue.GetValue<decimal>() : null;
var prevClose = _prevClose;
var prevMa = _prevMa;
_prevClose = candle.ClosePrice;
_prevMa = ma;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
_barsInPosition++;
var crossAboveMa = ma is decimal m && prevMa is decimal pm && prevClose is decimal pc && pc <= pm && candle.ClosePrice > m;
if (_barsInPosition >= DaysToHold || crossAboveMa)
{
SellMarket(Position);
_barsInPosition = 0;
}
return;
}
if (_history.Count < 5)
return;
// _history[4] is the current bar, _history[0] is the bar four bars ago.
var current = _history[4];
var bar1 = _history[3];
var bar2 = _history[2];
var bar3 = _history[1];
var bar4 = _history[0];
var pattern = current.LowPrice < bar1.LowPrice
&& bar1.LowPrice < bar3.LowPrice
&& bar2.LowPrice < bar4.LowPrice
&& bar2.HighPrice < bar3.HighPrice;
if (pattern && Position == 0)
{
BuyMarket(Volume);
_barsInPosition = 0;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class one_two_three_reversal_strategy(Strategy):
"""
One-Two-Three Reversal Strategy.
A long opens on a bullish 1-2-3 pattern: the current low is below the previous low, the previous low is below the low
three bars ago, the low two bars ago is below the low four bars ago and the high two bars ago is below the high three
bars ago. The long closes after DaysToHold bars or when the close crosses above the MaLength SMA.
"""
def __init__(self):
super(one_two_three_reversal_strategy, self).__init__()
self._days_to_hold = self.Param("DaysToHold", 7).SetGreaterThanZero().SetDisplay("Days To Hold", "Bars to hold the position", "Trading")
self._ma_length = self.Param("MaLength", 200).SetGreaterThanZero().SetDisplay("MA Length", "SMA period of the exit", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._history = []
self._prev_close = None
self._prev_ma = None
self._bars_in_position = 0
def OnReseted(self):
super(one_two_three_reversal_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(one_two_three_reversal_strategy, self).OnStarted2(time)
self._reset_state()
sma = SimpleMovingAverage()
sma.Length = self._ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
self._history.append((float(candle.LowPrice), float(candle.HighPrice)))
if len(self._history) > 5:
self._history.pop(0)
ma = float(sma_value.GetValue[Decimal](None)) if sma_value.IsFormed else None
close = float(candle.ClosePrice)
prev_close = self._prev_close
prev_ma = self._prev_ma
self._prev_close = close
self._prev_ma = ma
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
self._bars_in_position += 1
cross_above_ma = ma is not None and prev_ma is not None and prev_close is not None and prev_close <= prev_ma and close > ma
if self._bars_in_position >= self._days_to_hold.Value or cross_above_ma:
self.SellMarket(self.Position)
self._bars_in_position = 0
return
if len(self._history) < 5:
return
# history[4] is the current bar, history[0] is the bar four bars ago.
current = self._history[4]
bar1 = self._history[3]
bar2 = self._history[2]
bar3 = self._history[1]
bar4 = self._history[0]
pattern = (current[0] < bar1[0]
and bar1[0] < bar3[0]
and bar2[0] < bar4[0]
and bar2[1] < bar3[1])
if pattern and self.Position == 0:
self.BuyMarket(self.Volume)
self._bars_in_position = 0
def CreateClone(self):
return one_two_three_reversal_strategy()