ColorMETRO WPR 策略
该策略使用 ColorMETRO Williams %R 指标,该指标在 Williams %R 振荡器周围构建快慢阶梯线。 快线对价格变化反应迅速,而慢线用于平滑噪音。当两条线交叉时表明动量可能发生变化,此时产生交易信号。 快线从上向下穿越慢线时,策略认为市场超卖并建立多头;快线从下向上穿越慢线时建立空头。 出现相反信号时会平仓当前仓位。
风险控制通过基于百分比的止盈和止损实现,使策略能够适应不同价格区间的工具。 默认使用八小时K线,可过滤日内波动,更关注中期趋势。策略对多空对称,可在两个方向上操作。
细节
- 入场条件:
- 多头:快线自上而下穿越慢线。
- 空头:快线自下而上穿越慢线。
- 方向:双向。
- 出场条件:
- 多头:快线重新上穿慢线。
- 空头:快线重新下穿慢线。
- 止损/止盈:是,基于百分比的止盈和止损。
- 默认参数:
WprPeriod= 7。FastStep= 5。SlowStep= 15。TakeProfitPercent= 4。StopLossPercent= 2。CandleType= 8 小时K线。
- 过滤器:
- 分类:趋势跟随。
- 方向:双向。
- 指标:单一(基于 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()