Стратегия повторяет классическую систему MetaTrader "Bykov Trend" на базе высокоуровневого API StockSharp. Индикатор основан на осцилляторе Williams %R и фиксирует разворот тенденции. При переходе тренда от медвежьего к бычьему открывается длинная позиция, при смене бычьего тренда на медвежий открывается короткая.
Система торгует одним инструментом на выбранном таймфрейме и удерживает только одну позицию. Противоположные сигналы разворачивают позицию.
Детали
Условия входа
Лонг: Williams %R поднимается выше -K после нахождения ниже -100 + K (K = 33 - Risk).
Шорт: Williams %R опускается ниже -100 + K после нахождения выше -K.
Направления: обе стороны.
Условия выхода
Противоположный сигнал закрывает текущую позицию и открывает новую в обратном направлении.
Стопы: отсутствуют.
Значения по умолчанию
Risk = 3 (K = 30).
SSP = 9 (период Williams %R).
CandleType = часовые свечи.
Фильтры
Категория: трендовая
Направление: обе
Индикаторы: Williams %R
Стопы: нет
Сложность: простая
Таймфрейм: произвольный
Сезонность: нет
Нейросети: нет
Дивергенция: нет
Уровень риска: средний
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>
/// Bykov Trend strategy based on Williams %R indicator.
/// Generates buy or sell signals when trend changes occur.
/// </summary>
public class BykovTrendStrategy : Strategy
{
private readonly StrategyParam<int> _risk;
private readonly StrategyParam<int> _ssp;
private readonly StrategyParam<DataType> _candleType;
private bool _previousUptrend;
/// <summary>
/// Risk parameter from original indicator (K = 33 - Risk).
/// </summary>
public int Risk
{
get => _risk.Value;
set => _risk.Value = value;
}
/// <summary>
/// Williams %R period.
/// </summary>
public int Ssp
{
get => _ssp.Value;
set => _ssp.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="BykovTrendStrategy" />.
/// </summary>
public BykovTrendStrategy()
{
_risk = Param(nameof(Risk), 3)
.SetGreaterThanZero()
.SetDisplay("Risk", "Risk parameter from original indicator", "Indicator")
.SetOptimize(1, 10, 1);
_ssp = Param(nameof(Ssp), 9)
.SetGreaterThanZero()
.SetDisplay("SSP", "Williams %R period", "Indicator")
.SetOptimize(5, 20, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for indicator", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousUptrend = false;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var wpr = new WilliamsR { Length = Ssp };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(wpr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, wpr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal wprValue)
{
// Process only finished candles
if (candle.State != CandleStates.Finished)
return;
// Ensure that strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
var k = 33 - Risk;
var uptrend = _previousUptrend;
if (wprValue < -100 + k)
uptrend = false;
else if (wprValue > -k)
uptrend = true;
var buySignal = !_previousUptrend && uptrend;
var sellSignal = _previousUptrend && !uptrend;
_previousUptrend = uptrend;
if (buySignal)
{
// Close short positions and enter long
BuyMarket();
}
else if (sellSignal)
{
// Close long positions and enter short
SellMarket();
}
}
}
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
from StockSharp.Algo.Indicators import WilliamsR
from StockSharp.Algo.Strategies import Strategy
class bykov_trend_strategy(Strategy):
def __init__(self):
super(bykov_trend_strategy, self).__init__()
self._risk = self.Param("Risk", 3) \
.SetDisplay("Risk", "Risk parameter from original indicator", "Indicator")
self._ssp = self.Param("Ssp", 9) \
.SetDisplay("SSP", "Williams %R period", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for indicator", "General")
self._previous_uptrend = False
@property
def risk(self):
return self._risk.Value
@property
def ssp(self):
return self._ssp.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bykov_trend_strategy, self).OnReseted()
self._previous_uptrend = False
def OnStarted2(self, time):
super(bykov_trend_strategy, self).OnStarted2(time)
wpr = WilliamsR()
wpr.Length = self.ssp
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(wpr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, wpr)
self.DrawOwnTrades(area)
def process_candle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
wpr_value = float(wpr_value)
k = 33 - self.risk
uptrend = self._previous_uptrend
if wpr_value < -100 + k:
uptrend = False
elif wpr_value > -k:
uptrend = True
buy_signal = not self._previous_uptrend and uptrend
sell_signal = self._previous_uptrend and not uptrend
self._previous_uptrend = uptrend
if buy_signal:
self.BuyMarket()
elif sell_signal:
self.SellMarket()
def CreateClone(self):
return bykov_trend_strategy()