Vwap Macd Strategy
策略结合VWAP和MACD。当价格高于VWAP且MACD在信号线上方时做多;价格低于VWAP且MACD在信号线下方时做空。MACD与信号线反向交叉时离场。
测试表明年均收益约为 181%,该策略在加密市场表现最佳。
VWAP体现当日价值,MACD交叉揭示动量转变。交易在MACD靠近VWAP时反转发出信号。适合短线动量交易者,ATR规则控制风险。
细节
- 入场条件:
- 多头:
Close > VWAP && MACD > Signal - 空头:
Close < VWAP && MACD < Signal
- 多头:
- 多/空: 双向
- 离场条件: MACD反向交叉
- 止损: 百分比止损,使用
StopLossPercent - 默认值:
MacdFastPeriod= 12MacdSlowPeriod= 26MacdSignalPeriod= 9StopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- 过滤器:
- 类别: Mean reversion
- 方向: 双向
- 指标: VWAP, MACD
- 止损: 是
- 复杂度: 中等
- 时间框架: 中期
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
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;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on VWAP and MACD.
/// Enters long when price is above VWAP and MACD > Signal.
/// Enters short when price is below VWAP and MACD < Signal.
/// Exits when MACD crosses its signal line in the opposite direction.
/// </summary>
public class VwapMacdStrategy : Strategy
{
private readonly StrategyParam<int> _macdFastPeriod;
private readonly StrategyParam<int> _macdSlowPeriod;
private readonly StrategyParam<int> _macdSignalPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergenceSignal _macd;
private VolumeWeightedMovingAverage _vwap;
private decimal _prevMacd;
private decimal _prevSignal;
private int _cooldown;
/// <summary>
/// MACD fast EMA period.
/// </summary>
public int MacdFastPeriod
{
get => _macdFastPeriod.Value;
set => _macdFastPeriod.Value = value;
}
/// <summary>
/// MACD slow EMA period.
/// </summary>
public int MacdSlowPeriod
{
get => _macdSlowPeriod.Value;
set => _macdSlowPeriod.Value = value;
}
/// <summary>
/// MACD signal line period.
/// </summary>
public int MacdSignalPeriod
{
get => _macdSignalPeriod.Value;
set => _macdSignalPeriod.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop loss percentage value.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="VwapMacdStrategy"/>.
/// </summary>
public VwapMacdStrategy()
{
_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
.SetDisplay("MACD Fast Period", "Fast EMA period for MACD calculation", "Indicators")
;
_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26)
.SetDisplay("MACD Slow Period", "Slow EMA period for MACD calculation", "Indicators")
;
_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9)
.SetDisplay("MACD Signal Period", "Signal line period for MACD calculation", "Indicators")
;
_cooldownBars = Param(nameof(CooldownBars), 30)
.SetRange(1, 200)
.SetDisplay("Cooldown Bars", "Bars between entries", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetDisplay("Stop Loss (%)", "Stop loss percentage from entry price", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Timeframe of data for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security, DataType)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macd?.Reset();
_vwap?.Reset();
_prevMacd = 0;
_prevSignal = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create MACD indicator
_macd = new()
{
Macd =
{
ShortMa = { Length = MacdFastPeriod },
LongMa = { Length = MacdSlowPeriod },
},
SignalMa = { Length = MacdSignalPeriod }
};
_vwap = new() { Length = MacdSignalPeriod };
// Create subscription
var subscription = SubscribeCandles(CandleType);
// Process candles with MACD
subscription
.BindEx(_macd, ProcessCandle)
.Start();
// Setup chart visualization
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
// MACD in separate area
var macdArea = CreateChartArea();
if (macdArea != null)
{
DrawIndicator(macdArea, _macd);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Get VWAP value (calculated per day)
var vwap = _vwap.Process(candle).ToDecimal();
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
// Extract MACD and Signal values
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
{
return;
}
// Detect MACD crosses
bool macdCrossedAboveSignal = _prevMacd <= _prevSignal && macd > signal;
bool macdCrossedBelowSignal = _prevMacd >= _prevSignal && macd < signal;
// Check if strategy is ready for trading
if (!IsFormedAndOnlineAndAllowTrading())
{
// Store current values for next candle
_prevMacd = macd;
_prevSignal = signal;
return;
}
// Trading logic
if (_cooldown > 0)
_cooldown--;
if (_cooldown == 0 && candle.ClosePrice > vwap * 1.001m && macdCrossedAboveSignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_cooldown = CooldownBars;
}
else if (_cooldown == 0 && candle.ClosePrice < vwap * 0.999m && macdCrossedBelowSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_cooldown = CooldownBars;
}
// Exit logic based on MACD crosses
if (Position > 0 && macdCrossedBelowSignal)
{
ClosePosition();
_cooldown = CooldownBars;
}
else if (Position < 0 && macdCrossedAboveSignal)
{
ClosePosition();
_cooldown = CooldownBars;
}
// Store current values for next candle
_prevMacd = macd;
_prevSignal = signal;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal, VolumeWeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class vwap_macd_strategy(Strategy):
"""
Strategy based on VWAP and MACD.
Enters long when price is above VWAP and MACD crosses above Signal.
Enters short when price is below VWAP and MACD crosses below Signal.
Exits when MACD crosses its signal line in the opposite direction.
"""
def __init__(self):
super(vwap_macd_strategy, self).__init__()
self._macd_fast_period = self.Param("MacdFastPeriod", 12) \
.SetDisplay("MACD Fast Period", "Fast EMA period for MACD calculation", "Indicators")
self._macd_slow_period = self.Param("MacdSlowPeriod", 26) \
.SetDisplay("MACD Slow Period", "Slow EMA period for MACD calculation", "Indicators")
self._macd_signal_period = self.Param("MacdSignalPeriod", 9) \
.SetDisplay("MACD Signal Period", "Signal line period for MACD calculation", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 30) \
.SetRange(1, 200) \
.SetDisplay("Cooldown Bars", "Bars between entries", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop Loss (%)", "Stop loss percentage from entry price", "Risk Management")
self._candle_type = self.Param("CandleType", tf(15)) \
.SetDisplay("Candle Type", "Timeframe of data for strategy", "General")
self._vwap = None
self._prev_macd = 0.0
self._prev_signal = 0.0
self._cooldown = 0
@property
def CandleType(self):
return self._candle_type.Value
def OnReseted(self):
super(vwap_macd_strategy, self).OnReseted()
self._vwap = None
self._prev_macd = 0.0
self._prev_signal = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(vwap_macd_strategy, self).OnStarted2(time)
self._prev_macd = 0.0
self._prev_signal = 0.0
self._cooldown = 0
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast_period.Value
macd.Macd.LongMa.Length = self._macd_slow_period.Value
macd.SignalMa.Length = self._macd_signal_period.Value
self._vwap = VolumeWeightedMovingAverage()
self._vwap.Length = self._macd_signal_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(macd, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
macd_area = self.CreateChartArea()
if macd_area is not None:
self.DrawIndicator(macd_area, macd)
def ProcessCandle(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
vwap = float(process_candle(self._vwap, candle))
if macd_value.Macd is None or macd_value.Signal is None:
return
macd = float(macd_value.Macd)
signal = float(macd_value.Signal)
macd_crossed_above = self._prev_macd <= self._prev_signal and macd > signal
macd_crossed_below = self._prev_macd >= self._prev_signal and macd < signal
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_macd = macd
self._prev_signal = signal
return
if self._cooldown > 0:
self._cooldown -= 1
cooldown_val = int(self._cooldown_bars.Value)
if self._cooldown == 0 and float(candle.ClosePrice) > vwap * 1.001 and macd_crossed_above and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._cooldown = cooldown_val
elif self._cooldown == 0 and float(candle.ClosePrice) < vwap * 0.999 and macd_crossed_below and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._cooldown = cooldown_val
if self.Position > 0 and macd_crossed_below:
self.ClosePosition()
self._cooldown = cooldown_val
elif self.Position < 0 and macd_crossed_above:
self.ClosePosition()
self._cooldown = cooldown_val
self._prev_macd = macd
self._prev_signal = signal
def CreateClone(self):
return vwap_macd_strategy()