Стратегия ColorMETRO WPR
Эта стратегия использует индикатор ColorMETRO Williams %R, который строит быстрые и медленные ступенчатые линии вокруг осциллятора Williams %R. Быстрая линия реагирует на изменения цены оперативно, а медленная сглаживает шум. Сигналы на вход возникают, когда линии пересекаются, указывая на потенциальный разворот импульса. Если быстрая линия пересекает медленную сверху вниз, стратегия предполагает перепроданность рынка и открывает длинную позицию. При пересечении снизу вверх открывается короткая позиция. Текущие позиции закрываются при появлении противоположного сигнала.
Управление рисками реализовано через уровни тейк-профита и стоп-лосса в процентах, что позволяет адаптироваться к различным инструментам. По умолчанию стратегия работает на свечах восьмичасового таймфрейма, что помогает избегать внутридневного шума и сосредоточиться на среднесрочных трендах. Логика симметрична и позволяет работать в обе стороны рынка.
Детали
- Условия входа:
- Лонг: быстрая линия пересекает сверху вниз медленную.
- Шорт: быстрая линия пересекает снизу вверх медленную.
- Направление: обе стороны.
- Условия выхода:
- Лонг: быстрая линия поднимается выше медленной.
- Шорт: быстрая линия опускается ниже медленной.
- Стопы: да, тейк-профит и стоп-лосс в процентах.
- Значения по умолчанию:
WprPeriod= 7.FastStep= 5.SlowStep= 15.TakeProfitPercent= 4.StopLossPercent= 2.CandleType= 8-часовые свечи.
- Фильтры:
- Категория: трендовая.
- Направление: обе стороны.
- Индикаторы: один (на базе 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>
/// Strategy based on the ColorMETRO Williams %R indicator.
/// It trades when the fast step line crosses the slow step line.
/// </summary>
public class ColorMetroWprStrategy : Strategy
{
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<int> _fastStep;
private readonly StrategyParam<int> _slowStep;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
// Indicator instance
private WilliamsR _wpr;
// Previous state for step calculations
private decimal _fMinPrev;
private decimal _fMaxPrev;
private decimal _sMinPrev;
private decimal _sMaxPrev;
private int _fTrend;
private int _sTrend;
// Previous and current step values
private decimal _prevMPlus;
private decimal _prevMMinus;
private decimal _currMPlus;
private decimal _currMMinus;
private bool _isFirstValue;
/// <summary>
/// Period for Williams %R.
/// </summary>
public int WprPeriod
{
get => _wprPeriod.Value;
set => _wprPeriod.Value = value;
}
/// <summary>
/// Step size for the fast line.
/// </summary>
public int FastStep
{
get => _fastStep.Value;
set => _fastStep.Value = value;
}
/// <summary>
/// Step size for the slow line.
/// </summary>
public int SlowStep
{
get => _slowStep.Value;
set => _slowStep.Value = value;
}
/// <summary>
/// Take profit in percent.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Stop loss in percent.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Type of candles used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="ColorMetroWprStrategy"/>.
/// </summary>
public ColorMetroWprStrategy()
{
_wprPeriod = Param(nameof(WprPeriod), 7)
.SetDisplay("Williams %R Period", "Period for Williams %R", "Indicators")
.SetOptimize(5, 15, 1);
_fastStep = Param(nameof(FastStep), 5)
.SetDisplay("Fast Step", "Step size for fast line", "Indicators")
.SetOptimize(3, 10, 1);
_slowStep = Param(nameof(SlowStep), 15)
.SetDisplay("Slow Step", "Step size for slow line", "Indicators")
.SetOptimize(10, 20, 1);
_takeProfitPercent = Param(nameof(TakeProfitPercent), 4m)
.SetDisplay("Take Profit (%)", "Take profit as percentage", "Risk parameters")
.SetOptimize(2m, 6m, 1m);
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetDisplay("Stop Loss (%)", "Stop loss as percentage", "Risk parameters")
.SetOptimize(1m, 4m, 1m);
_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();
_fMinPrev = decimal.MaxValue;
_fMaxPrev = decimal.MinValue;
_sMinPrev = decimal.MaxValue;
_sMaxPrev = decimal.MinValue;
_fTrend = 0;
_sTrend = 0;
_prevMPlus = 0m;
_prevMMinus = 0m;
_currMPlus = 0m;
_currMMinus = 0m;
_isFirstValue = true;
_wpr = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_wpr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _wpr);
DrawOwnTrades(area);
}
StartProtection(
new Unit(TakeProfitPercent, UnitTypes.Percent),
new Unit(StopLossPercent, UnitTypes.Percent));
}
private void ProcessCandle(ICandleMessage candle, decimal wprValue)
{
// Use only finished candles
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var wpr = wprValue + 100m;
var fmax0 = wpr + 2m * FastStep;
var fmin0 = wpr - 2m * FastStep;
if (wpr > _fMaxPrev)
_fTrend = 1;
if (wpr < _fMinPrev)
_fTrend = -1;
if (_fTrend > 0 && fmin0 < _fMinPrev)
fmin0 = _fMinPrev;
if (_fTrend < 0 && fmax0 > _fMaxPrev)
fmax0 = _fMaxPrev;
var smax0 = wpr + 2m * SlowStep;
var smin0 = wpr - 2m * SlowStep;
if (wpr > _sMaxPrev)
_sTrend = 1;
if (wpr < _sMinPrev)
_sTrend = -1;
if (_sTrend > 0 && smin0 < _sMinPrev)
smin0 = _sMinPrev;
if (_sTrend < 0 && smax0 > _sMaxPrev)
smax0 = _sMaxPrev;
var mPlus = _fTrend > 0 ? fmin0 + FastStep : fmax0 - FastStep;
var mMinus = _sTrend > 0 ? smin0 + SlowStep : smax0 - SlowStep;
_fMinPrev = fmin0;
_fMaxPrev = fmax0;
_sMinPrev = smin0;
_sMaxPrev = smax0;
if (_isFirstValue)
{
_prevMPlus = mPlus;
_prevMMinus = mMinus;
_currMPlus = mPlus;
_currMMinus = mMinus;
_isFirstValue = false;
return;
}
_prevMPlus = _currMPlus;
_prevMMinus = _currMMinus;
_currMPlus = mPlus;
_currMMinus = mMinus;
var prevFastAboveSlow = _prevMPlus > _prevMMinus;
var prevFastBelowSlow = _prevMPlus < _prevMMinus;
if (prevFastAboveSlow)
{
if (Position < 0)
BuyMarket();
if (_currMPlus <= _currMMinus && Position <= 0)
BuyMarket();
}
else if (prevFastBelowSlow)
{
if (Position > 0)
SellMarket();
if (_currMPlus >= _currMMinus && Position >= 0)
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import WilliamsR
from StockSharp.Algo.Strategies import Strategy
class color_metro_wpr_strategy(Strategy):
def __init__(self):
super(color_metro_wpr_strategy, self).__init__()
self._wpr_period = self.Param("WprPeriod", 7) \
.SetDisplay("Williams %R Period", "Period for Williams %R", "Indicators")
self._fast_step = self.Param("FastStep", 5) \
.SetDisplay("Fast Step", "Step size for fast line", "Indicators")
self._slow_step = self.Param("SlowStep", 15) \
.SetDisplay("Slow Step", "Step size for slow line", "Indicators")
self._take_profit_percent = self.Param("TakeProfitPercent", 4.0) \
.SetDisplay("Take Profit (%)", "Take profit as percentage", "Risk parameters")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop Loss (%)", "Stop loss as percentage", "Risk parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._wpr = None
self._f_min_prev = 999999.0
self._f_max_prev = -999999.0
self._s_min_prev = 999999.0
self._s_max_prev = -999999.0
self._f_trend = 0
self._s_trend = 0
self._prev_m_plus = 0.0
self._prev_m_minus = 0.0
self._curr_m_plus = 0.0
self._curr_m_minus = 0.0
self._is_first_value = True
@property
def wpr_period(self):
return self._wpr_period.Value
@property
def fast_step(self):
return self._fast_step.Value
@property
def slow_step(self):
return self._slow_step.Value
@property
def take_profit_percent(self):
return self._take_profit_percent.Value
@property
def stop_loss_percent(self):
return self._stop_loss_percent.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_metro_wpr_strategy, self).OnReseted()
self._f_min_prev = 999999.0
self._f_max_prev = -999999.0
self._s_min_prev = 999999.0
self._s_max_prev = -999999.0
self._f_trend = 0
self._s_trend = 0
self._prev_m_plus = 0.0
self._prev_m_minus = 0.0
self._curr_m_plus = 0.0
self._curr_m_minus = 0.0
self._is_first_value = True
self._wpr = None
def OnStarted2(self, time):
super(color_metro_wpr_strategy, self).OnStarted2(time)
self._wpr = WilliamsR()
self._wpr.Length = self.wpr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._wpr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._wpr)
self.DrawOwnTrades(area)
self.StartProtection(
takeProfit=Unit(float(self.take_profit_percent), UnitTypes.Percent),
stopLoss=Unit(float(self.stop_loss_percent), UnitTypes.Percent))
def process_candle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
wpr = float(wpr_value) + 100.0
fs = float(self.fast_step)
ss = float(self.slow_step)
fmax0 = wpr + 2.0 * fs
fmin0 = wpr - 2.0 * fs
if wpr > self._f_max_prev:
self._f_trend = 1
if wpr < self._f_min_prev:
self._f_trend = -1
if self._f_trend > 0 and fmin0 < self._f_min_prev:
fmin0 = self._f_min_prev
if self._f_trend < 0 and fmax0 > self._f_max_prev:
fmax0 = self._f_max_prev
smax0 = wpr + 2.0 * ss
smin0 = wpr - 2.0 * ss
if wpr > self._s_max_prev:
self._s_trend = 1
if wpr < self._s_min_prev:
self._s_trend = -1
if self._s_trend > 0 and smin0 < self._s_min_prev:
smin0 = self._s_min_prev
if self._s_trend < 0 and smax0 > self._s_max_prev:
smax0 = self._s_max_prev
m_plus = fmin0 + fs if self._f_trend > 0 else fmax0 - fs
m_minus = smin0 + ss if self._s_trend > 0 else smax0 - ss
self._f_min_prev = fmin0
self._f_max_prev = fmax0
self._s_min_prev = smin0
self._s_max_prev = smax0
if self._is_first_value:
self._prev_m_plus = m_plus
self._prev_m_minus = m_minus
self._curr_m_plus = m_plus
self._curr_m_minus = m_minus
self._is_first_value = False
return
self._prev_m_plus = self._curr_m_plus
self._prev_m_minus = self._curr_m_minus
self._curr_m_plus = m_plus
self._curr_m_minus = m_minus
prev_fast_above_slow = self._prev_m_plus > self._prev_m_minus
prev_fast_below_slow = self._prev_m_plus < self._prev_m_minus
if prev_fast_above_slow:
if self.Position < 0:
self.BuyMarket()
if self._curr_m_plus <= self._curr_m_minus and self.Position <= 0:
self.BuyMarket()
elif prev_fast_below_slow:
if self.Position > 0:
self.SellMarket()
if self._curr_m_plus >= self._curr_m_minus and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return color_metro_wpr_strategy()