权重振荡器策略
该策略将 RSI、资金流量指数、Williams %R 和 DeMarker 组合成一个加权振荡器,并通过简单移动平均线进行平滑。当振荡器穿越设置的高位或低位时开仓或反向。趋势模式决定是顺势交易还是逆势交易。
细节
- 入场条件:
- Trend = Direct:
- 做多:振荡器跌破下限。
- 做空:振荡器升破上限。
- Trend = Against:
- 做多:振荡器升破上限。
- 做空:振荡器跌破下限。
- Trend = Direct:
- 方向:双向。
- 出场条件:反向穿越反转仓位。
- 止损:无明确止损。
- 过滤器:加权振荡器与 SMA 平滑。
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>
/// Trading strategy based on a weighted oscillator composed of RSI and Williams %R.
/// Buys when the combined oscillator crosses below the oversold level.
/// Sells when the combined oscillator crosses above the overbought level.
/// </summary>
public class WeightOscillatorStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<decimal> _highLevel;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevOsc;
private bool _hasPrev;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int WprPeriod { get => _wprPeriod.Value; set => _wprPeriod.Value = value; }
public decimal HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
public decimal LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public WeightOscillatorStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI length", "Indicators");
_wprPeriod = Param(nameof(WprPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("WPR Period", "Williams %R length", "Indicators");
_highLevel = Param(nameof(HighLevel), 70m)
.SetDisplay("High Level", "Overbought level", "Signals");
_lowLevel = Param(nameof(LowLevel), 30m)
.SetDisplay("Low Level", "Oversold level", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Working candles", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevOsc = 0m;
_hasPrev = false;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, wpr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawIndicator(area, wpr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal wprValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Normalize WPR from (-100, 0) to (0, 100) and average with RSI
var normalizedWpr = wprValue + 100m;
var osc = (rsiValue + normalizedWpr) / 2m;
if (_hasPrev)
{
// Buy when oscillator crosses below low level (oversold)
if (_prevOsc > LowLevel && osc <= LowLevel && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
}
// Sell when oscillator crosses above high level (overbought)
else if (_prevOsc < HighLevel && osc >= HighLevel && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
}
}
_prevOsc = osc;
_hasPrev = true;
}
}
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 RelativeStrengthIndex, WilliamsR
from StockSharp.Algo.Strategies import Strategy
class weight_oscillator_strategy(Strategy):
def __init__(self):
super(weight_oscillator_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14)
self._wpr_period = self.Param("WprPeriod", 14)
self._high_level = self.Param("HighLevel", 70.0)
self._low_level = self.Param("LowLevel", 30.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._prev_osc = 0.0
self._has_prev = False
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def WprPeriod(self):
return self._wpr_period.Value
@WprPeriod.setter
def WprPeriod(self, value):
self._wpr_period.Value = value
@property
def HighLevel(self):
return self._high_level.Value
@HighLevel.setter
def HighLevel(self, value):
self._high_level.Value = value
@property
def LowLevel(self):
return self._low_level.Value
@LowLevel.setter
def LowLevel(self, value):
self._low_level.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(weight_oscillator_strategy, self).OnStarted2(time)
self._has_prev = False
self._prev_osc = 0.0
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
wpr = WilliamsR()
wpr.Length = self.WprPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, wpr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_value, wpr_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
rsi_val = float(rsi_value)
wpr_val = float(wpr_value)
normalized_wpr = wpr_val + 100.0
osc = (rsi_val + normalized_wpr) / 2.0
high_lvl = float(self.HighLevel)
low_lvl = float(self.LowLevel)
if self._has_prev:
if self._prev_osc > low_lvl and osc <= low_lvl and self.Position <= 0:
volume = float(self.Volume) + abs(float(self.Position))
self.BuyMarket(volume)
elif self._prev_osc < high_lvl and osc >= high_lvl and self.Position >= 0:
volume = float(self.Volume) + abs(float(self.Position))
self.SellMarket(volume)
self._prev_osc = osc
self._has_prev = True
def OnReseted(self):
super(weight_oscillator_strategy, self).OnReseted()
self._prev_osc = 0.0
self._has_prev = False
def CreateClone(self):
return weight_oscillator_strategy()