BabyShark VWAP 策略
该策略结合了成交量加权平均价 (VWAP) 通道和基于 OBV 的 RSI 过滤。价格跌破下方偏差带并且 RSI 显示超卖时做多;价格升破上方偏差带且 RSI 表现为超买时做空。
采用小幅百分比止损,并在再次入场前等待冷却期。
详情
- 入场条件:价格穿越偏差带并得到 RSI 确认。
- 多空方向:双向。
- 出场条件:回到 VWAP 或触发止损。
- 止损:有。
- 默认参数:
Length= 60RsiLength= 5HigherLevel= 70LowerLevel= 30Cooldown= 10StopLossPercent= 0.6mCandleType= TimeSpan.FromMinutes(1)
- 过滤器:
- 类别:均值回归
- 方向:双向
- 指标:VWAP、RSI、OBV
- 止损:有
- 复杂度:中等
- 周期:日内
- 季节性:无
- 神经网络:无
- 背离:无
- 风险等级:中等
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// BabyShark VWAP strategy.
/// Uses RSI crossover with EMA trend filter and cooldown.
/// Buys when RSI exits oversold in uptrend, sells when RSI exits overbought in downtrend.
/// </summary>
public class BabySharkVwapStrategy : Strategy
{
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevRsi;
private int _barIndex;
private int _lastTradeBar;
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// EMA trend filter period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BabySharkVwapStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_emaLength = Param(nameof(EmaLength), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA trend filter period", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 300)
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
_prevRsi = 0;
_barIndex = 0;
_lastTradeBar = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
_barIndex++;
var cooldownOk = _barIndex - _lastTradeBar > CooldownBars;
// RSI crosses above 45 from below with uptrend
var longSignal = _prevRsi > 0 && _prevRsi < 45 && rsiValue >= 45 && candle.ClosePrice > emaValue;
// RSI crosses below 55 from above with downtrend
var shortSignal = _prevRsi > 0 && _prevRsi > 55 && rsiValue <= 55 && candle.ClosePrice < emaValue;
if (longSignal && Position <= 0 && cooldownOk)
{
BuyMarket();
_lastTradeBar = _barIndex;
}
else if (shortSignal && Position >= 0 && cooldownOk)
{
SellMarket();
_lastTradeBar = _barIndex;
}
_prevRsi = rsiValue;
}
}
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, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class baby_shark_vwap_strategy(Strategy):
def __init__(self):
super(baby_shark_vwap_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "EMA trend filter period", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 300) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_rsi = 0.0
self._bar_index = 0
self._last_trade_bar = 0
@property
def rsi_length(self):
return self._rsi_length.Value
@rsi_length.setter
def rsi_length(self, value):
self._rsi_length.Value = value
@property
def ema_length(self):
return self._ema_length.Value
@ema_length.setter
def ema_length(self, value):
self._ema_length.Value = value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@cooldown_bars.setter
def cooldown_bars(self, value):
self._cooldown_bars.Value = value
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(baby_shark_vwap_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._bar_index = 0
self._last_trade_bar = 0
def OnStarted2(self, time):
super(baby_shark_vwap_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_length
ema = ExponentialMovingAverage()
ema.Length = self.ema_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, rsi_value, ema_value):
if candle.State != CandleStates.Finished:
return
self._bar_index += 1
cooldown_ok = self._bar_index - self._last_trade_bar > self.cooldown_bars
long_signal = self._prev_rsi > 0 and self._prev_rsi < 45 and rsi_value >= 45 and candle.ClosePrice > ema_value
short_signal = self._prev_rsi > 0 and self._prev_rsi > 55 and rsi_value <= 55 and candle.ClosePrice < ema_value
if long_signal and self.Position <= 0 and cooldown_ok:
self.BuyMarket()
self._last_trade_bar = self._bar_index
elif short_signal and self.Position >= 0 and cooldown_ok:
self.SellMarket()
self._last_trade_bar = self._bar_index
self._prev_rsi = float(rsi_value)
def CreateClone(self):
return baby_shark_vwap_strategy()