YinYang RSI Volume Trend 策略
YinYang RSI Volume Trend 策略利用成交量加权的价格区域和 RSI 过滤器来捕捉趋势反转。当价格离开下方区域时买入,离开上方区域时卖出。可选的止损和止盈基于动态区域计算。
详情
- 入场条件:价格穿越计算得到的买入区域,并依据重置模式确认可用性。
- 多/空:双向。
- 离场条件:价格到达相反区域或触发可选的止损/止盈。
- 止损:可选。
- 默认参数:
TrendLength= 80UseTakeProfit= trueUseStopLoss= trueStopLossMultiplier= 0.1
- 过滤器:
- 类别: Trend
- 方向: 双向
- 指标: VWMA, EMA, RSI
- 止损: 可选
- 复杂度: 中等
- 时间框架: 任意
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险等级: 中等
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>
/// YinYang RSI Volume Trend strategy.
/// Defines dynamic buy/sell zones using SMA, StdDev, and RSI.
/// Enters long when price crosses above lower zone, short when below upper zone.
/// </summary>
public class YinYangRsiVolumeTrendStrategy : Strategy
{
private readonly StrategyParam<int> _trendLength;
private readonly StrategyParam<decimal> _stopLossMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevZoneHigh;
private decimal _prevZoneLow;
private decimal _prevZoneBasis;
private bool _initialized;
public int TrendLength { get => _trendLength.Value; set => _trendLength.Value = value; }
public decimal StopLossMultiplier { get => _stopLossMultiplier.Value; set => _stopLossMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public YinYangRsiVolumeTrendStrategy()
{
_trendLength = Param(nameof(TrendLength), 40)
.SetGreaterThanZero()
.SetDisplay("Trend Length", "Lookback length", "General");
_stopLossMultiplier = Param(nameof(StopLossMultiplier), 0.5m)
.SetGreaterThanZero()
.SetDisplay("SL Mult %", "Stop distance percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevZoneHigh = 0;
_prevZoneLow = 0;
_prevZoneBasis = 0;
_initialized = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = TrendLength };
var stdDev = new StandardDeviation { Length = TrendLength };
var rsi = new RelativeStrengthIndex { Length = 14 };
_prevClose = 0;
_prevZoneHigh = 0;
_prevZoneLow = 0;
_prevZoneBasis = 0;
_initialized = false;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, stdDev, rsi, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal stdVal, decimal rsiVal)
{
if (candle.State != CandleStates.Finished)
return;
if (stdVal <= 0)
return;
// Dynamic zones based on SMA +/- RSI-weighted StdDev
var rsiWeight = rsiVal / 100m; // 0..1
var zoneWidth = stdVal * (0.5m + rsiWeight);
var zoneBasis = smaVal;
var zoneHigh = zoneBasis + zoneWidth;
var zoneLow = zoneBasis - zoneWidth;
var stopHigh = zoneHigh * (1 + StopLossMultiplier / 100m);
var stopLow = zoneLow * (1 - StopLossMultiplier / 100m);
var close = candle.ClosePrice;
if (!_initialized)
{
_prevClose = close;
_prevZoneHigh = zoneHigh;
_prevZoneLow = zoneLow;
_prevZoneBasis = zoneBasis;
_initialized = true;
return;
}
// Cross detections
var longStart = _prevClose <= _prevZoneLow && close > zoneLow;
var longEnd = _prevClose <= _prevZoneHigh && close > zoneHigh;
var longStopLoss = _prevClose >= _prevZoneLow && close < stopLow;
var shortStart = _prevClose >= _prevZoneHigh && close < zoneHigh;
var shortEnd = _prevClose >= _prevZoneLow && close < zoneLow;
var shortStopLoss = _prevClose <= _prevZoneHigh && close > stopHigh;
// Long entry: price crosses up from below lower zone
if (longStart && Position <= 0)
{
BuyMarket();
}
else if (Position > 0 && (longEnd || longStopLoss))
{
SellMarket();
}
// Short entry: price crosses down from above upper zone
if (shortStart && Position >= 0)
{
SellMarket();
}
else if (Position < 0 && (shortEnd || shortStopLoss))
{
BuyMarket();
}
_prevClose = close;
_prevZoneHigh = zoneHigh;
_prevZoneLow = zoneLow;
_prevZoneBasis = zoneBasis;
}
}
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, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class yin_yang_rsi_volume_trend_strategy(Strategy):
def __init__(self):
super(yin_yang_rsi_volume_trend_strategy, self).__init__()
self._trend_length = self.Param("TrendLength", 40) \
.SetDisplay("Trend Length", "Lookback length", "General")
self._stop_loss_multiplier = self.Param("StopLossMultiplier", 0.5) \
.SetDisplay("SL Mult %", "Stop distance percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_zone_high = 0.0
self._prev_zone_low = 0.0
self._prev_zone_basis = 0.0
self._initialized = False
@property
def trend_length(self):
return self._trend_length.Value
@property
def stop_loss_multiplier(self):
return self._stop_loss_multiplier.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(yin_yang_rsi_volume_trend_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_zone_high = 0.0
self._prev_zone_low = 0.0
self._prev_zone_basis = 0.0
self._initialized = False
def OnStarted2(self, time):
super(yin_yang_rsi_volume_trend_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.trend_length
std_dev = StandardDeviation()
std_dev.Length = self.trend_length
rsi = RelativeStrengthIndex()
rsi.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, std_dev, rsi, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def on_process(self, candle, sma_val, std_val, rsi_val):
if candle.State != CandleStates.Finished:
return
if std_val <= 0:
return
# Dynamic zones based on SMA +/- RSI-weighted StdDev
rsi_weight = rsi_val / 100 # 0..1
zone_width = std_val * (0.5 + rsi_weight)
zone_basis = sma_val
zone_high = zone_basis + zone_width
zone_low = zone_basis - zone_width
stop_high = zone_high * (1 + self.stop_loss_multiplier / 100)
stop_low = zone_low * (1 - self.stop_loss_multiplier / 100)
close = candle.ClosePrice
if not self._initialized:
self._prev_close = close
self._prev_zone_high = zone_high
self._prev_zone_low = zone_low
self._prev_zone_basis = zone_basis
self._initialized = True
return
# Cross detections
long_start = self._prev_close <= self._prev_zone_low and close > zone_low
long_end = self._prev_close <= self._prev_zone_high and close > zone_high
long_stop_loss = self._prev_close >= self._prev_zone_low and close < stop_low
short_start = self._prev_close >= self._prev_zone_high and close < zone_high
short_end = self._prev_close >= self._prev_zone_low and close < zone_low
short_stop_loss = self._prev_close <= self._prev_zone_high and close > stop_high
# Long entry: price crosses up from below lower zone
if long_start and self.Position <= 0:
self.BuyMarket()
elif self.Position > 0 and (long_end or long_stop_loss):
self.SellMarket()
# Short entry: price crosses down from above upper zone
if short_start and self.Position >= 0:
self.SellMarket()
elif self.Position < 0 and (short_end or short_stop_loss):
self.BuyMarket()
self._prev_close = close
self._prev_zone_high = zone_high
self._prev_zone_low = zone_low
self._prev_zone_basis = zone_basis
def CreateClone(self):
return yin_yang_rsi_volume_trend_strategy()