Стратегия «Покупка, когда на улицах кровь»
Стратегия покупает, когда текущая просадка относительно недавнего максимума опускается ниже порога стандартного отклонения. Позиция закрывается после фиксированного числа баров.
Детали
- Условия входа:
- Лонг: просадка ≤ среднее +
StdDevThreshold× стандартное отклонение
- Лонг: просадка ≤ среднее +
- Направление: только лонг
- Условия выхода: позиция закрывается после
ExitBarsбаров - Стопы: нет
- Значения по умолчанию:
LookbackPeriod= 50StdDevLength= 50StdDevThreshold= -1mExitBars= 35CandleType= TimeSpan.FromMinutes(1).TimeFrame()
- Фильтры:
- Категория: Reversal
- Направление: Long
- Индикаторы: Highest, SMA, StandardDeviation
- Стопы: Нет
- Сложность: Базовая
- Таймфрейм: Среднесрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Blood In The Streets strategy.
/// The drawdown is the percent distance of the close below the highest high of the last LookbackPeriod candles. When it falls to
/// its StdDevLength mean plus StdDevThreshold standard deviations or lower, the strategy buys, and it closes the long after ExitBars candles.
/// </summary>
public class BloodInTheStreetsStrategy : Strategy
{
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<int> _stdDevLength;
private readonly StrategyParam<decimal> _stdDevThreshold;
private readonly StrategyParam<int> _exitBars;
private readonly StrategyParam<DataType> _candleType;
private Highest _highest;
private SimpleMovingAverage _drawdownMean;
private StandardDeviation _drawdownDeviation;
private int _barsInPosition;
/// <summary>
/// Candles the highest high spans.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Candles the drawdown mean and standard deviation span.
/// </summary>
public int StdDevLength
{
get => _stdDevLength.Value;
set => _stdDevLength.Value = value;
}
/// <summary>
/// Standard deviations from the mean the drawdown has to reach.
/// </summary>
public decimal StdDevThreshold
{
get => _stdDevThreshold.Value;
set => _stdDevThreshold.Value = value;
}
/// <summary>
/// Candles a position is held.
/// </summary>
public int ExitBars
{
get => _exitBars.Value;
set => _exitBars.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BloodInTheStreetsStrategy()
{
_lookbackPeriod = Param(nameof(LookbackPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Lookback Period", "Candles the highest high spans", "Indicators");
_stdDevLength = Param(nameof(StdDevLength), 50)
.SetGreaterThanZero()
.SetDisplay("StdDev Length", "Candles the drawdown mean and standard deviation span", "Indicators");
_stdDevThreshold = Param(nameof(StdDevThreshold), -1m)
.SetDisplay("StdDev Threshold", "Standard deviations from the mean the drawdown has to reach", "Indicators");
_exitBars = Param(nameof(ExitBars), 35)
.SetGreaterThanZero()
.SetDisplay("Exit Bars", "Candles a position is held", "Exit");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highest = null;
_drawdownMean = null;
_drawdownDeviation = null;
_barsInPosition = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highest = new Highest { Length = LookbackPeriod };
_drawdownMean = new SimpleMovingAverage { Length = StdDevLength };
_drawdownDeviation = new StandardDeviation { Length = StdDevLength };
_barsInPosition = 0;
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;
var highestValue = _highest.Process(new DecimalIndicatorValue(_highest, candle.HighPrice, candle.OpenTime) { IsFinal = true });
if (!highestValue.IsFormed)
return;
var highest = highestValue.GetValue<decimal>();
if (highest <= 0)
return;
var drawdown = (candle.ClosePrice - highest) / highest * 100m;
var meanValue = _drawdownMean.Process(new DecimalIndicatorValue(_drawdownMean, drawdown, candle.OpenTime) { IsFinal = true });
var deviationValue = _drawdownDeviation.Process(new DecimalIndicatorValue(_drawdownDeviation, drawdown, candle.OpenTime) { IsFinal = true });
if (Position > 0)
_barsInPosition++;
if (!meanValue.IsFormed || !deviationValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if (_barsInPosition >= ExitBars)
SellMarket(Position);
return;
}
var threshold = meanValue.GetValue<decimal>() + StdDevThreshold * deviationValue.GetValue<decimal>();
if (Position == 0 && drawdown <= threshold)
{
BuyMarket(Volume);
_barsInPosition = 0;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, SimpleMovingAverage, StandardDeviation, DecimalIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class blood_in_the_streets_strategy(Strategy):
"""
Blood In The Streets strategy.
The drawdown is the percent distance of the close below the highest high of the last LookbackPeriod candles. When it falls to
its StdDevLength mean plus StdDevThreshold standard deviations or lower, the strategy buys, and it closes the long after ExitBars candles.
"""
def __init__(self):
super(blood_in_the_streets_strategy, self).__init__()
self._lookback_period = self.Param("LookbackPeriod", 50).SetGreaterThanZero().SetDisplay("Lookback Period", "Candles the highest high spans", "Indicators")
self._std_dev_length = self.Param("StdDevLength", 50).SetGreaterThanZero().SetDisplay("StdDev Length", "Candles the drawdown mean and standard deviation span", "Indicators")
self._std_dev_threshold = self.Param("StdDevThreshold", -1.0).SetDisplay("StdDev Threshold", "Standard deviations from the mean the drawdown has to reach", "Indicators")
self._exit_bars = self.Param("ExitBars", 35).SetGreaterThanZero().SetDisplay("Exit Bars", "Candles a position is held", "Exit")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._highest = None
self._drawdown_mean = None
self._drawdown_deviation = None
self._bars_in_position = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(blood_in_the_streets_strategy, self).OnReseted()
self._highest = None
self._drawdown_mean = None
self._drawdown_deviation = None
self._bars_in_position = 0
def OnStarted2(self, time):
super(blood_in_the_streets_strategy, self).OnStarted2(time)
self._highest = Highest()
self._highest.Length = self._lookback_period.Value
self._drawdown_mean = SimpleMovingAverage()
self._drawdown_mean.Length = self._std_dev_length.Value
self._drawdown_deviation = StandardDeviation()
self._drawdown_deviation.Length = self._std_dev_length.Value
self._bars_in_position = 0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process(self, indicator, value, time):
indicator_input = DecimalIndicatorValue(indicator, value, time)
indicator_input.IsFinal = True
return indicator.Process(indicator_input)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
highest_value = self._process(self._highest, candle.HighPrice, candle.OpenTime)
if not highest_value.IsFormed:
return
highest = highest_value.GetValue[Decimal](None)
if highest <= 0:
return
drawdown = (candle.ClosePrice - highest) / highest * Decimal(100)
mean_value = self._process(self._drawdown_mean, drawdown, candle.OpenTime)
deviation_value = self._process(self._drawdown_deviation, drawdown, candle.OpenTime)
if self.Position > 0:
self._bars_in_position += 1
if not mean_value.IsFormed or not deviation_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
if self._bars_in_position >= self._exit_bars.Value:
self.SellMarket(self.Position)
return
threshold = mean_value.GetValue[Decimal](None) + Decimal(self._std_dev_threshold.Value) * deviation_value.GetValue[Decimal](None)
if self.Position == 0 and drawdown <= threshold:
self.BuyMarket(self.Volume)
self._bars_in_position = 0
def CreateClone(self):
return blood_in_the_streets_strategy()