Vwap Macd Strategy
Стратегия основана на VWAP и MACD. Вход в длинные позиции, когда цена выше VWAP и MACD > сигнальной. Вход в короткие, когда цена ниже VWAP и MACD < сигнальной. Выход, когда MACD пересекает сигнальную линию в противоположную сторону.
Тестирование показывает среднегодичную доходность около 181%. Стратегию лучше запускать на крипторынке.
VWAP определяет внутридневную стоимость, а перекрестные сигналы MACD показывают смену импульса. Сделки запускаются, когда MACD разворачивается рядом с уровнем VWAP.
Подходит краткосрочным трейдерам-моментумщикам. Правила стопов по ATR предотвращают чрезмерный риск.
Детали
- Критерии входа:
- Long:
Close > VWAP && MACD > Signal - Short:
Close < VWAP && MACD < Signal
- Long:
- Long/Short: Оба направления
- Критерии выхода: пересечение 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()