Home
/
Strategy examples
View on GitHub
Macd Volume Strategy
Strategy combining MACD (Moving Average Convergence Divergence) with volume confirmation. Enters positions when MACD line crosses the Signal line and confirms with increased volume.
Testing indicates an average annual return of about 175%. It performs best in the stocks market.
MACD crossovers are filtered by an increase in volume to confirm momentum. Buy signals come on bullish crosses with expanding volume; sells do the opposite.
Momentum traders watching for volume spikes may find it valuable. Risk is limited using an ATR stop.
Details
Entry Criteria :
Long: MACD crosses above Signal && Volume > AvgVolume * VolumeMultiplier
Short: MACD crosses below Signal && Volume > AvgVolume * VolumeMultiplier
Long/Short : Both
Exit Criteria :
MACD cross in opposite direction
Stops : Percent-based at StopLossPercent
Default Values :
MacdFast = 12
MacdSlow = 26
MacdSignal = 9
VolumePeriod = 20
VolumeMultiplier = 1.5m
StopLossPercent = 2.0m
CandleType = TimeSpan.FromMinutes(5).TimeFrame()
Filters :
Category: Breakout
Direction: Both
Indicators: MACD, Volume
Stops: Yes
Complexity: Intermediate
Timeframe: Mid-term
Seasonality: No
Neural Networks: No
Divergence: No
Risk Level: Medium
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>
/// Strategy combining MACD crossover with trend confirmation.
/// Enters on MACD line crossing Signal line.
/// </summary>
public class MacdVolumeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Strategy constructor.
/// </summary>
public MacdVolumeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 100)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal();
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var macdArea = CreateChartArea();
if (macdArea != null)
DrawIndicator(macdArea, macd);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macdTyped)
return;
if (macdTyped.Macd is not decimal macdLine || macdTyped.Signal is not decimal signalLine)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Entry: MACD bullish
if (macdLine > signalLine && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Entry: MACD bearish
else if (macdLine < signalLine && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit on MACD crossover against position
if (Position > 0 && macdLine < signalLine)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && macdLine > signalLine)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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 MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class macd_volume_strategy(Strategy):
"""
Strategy combining MACD crossover with trend confirmation.
Enters on MACD line crossing Signal line.
"""
def __init__(self):
super(macd_volume_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 100) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General") \
.SetRange(5, 500)
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnStarted2(self, time):
super(macd_volume_strategy, self).OnStarted2(time)
self._cooldown = 0
macd = MovingAverageConvergenceDivergenceSignal()
subscription = self.SubscribeCandles(self.candle_type)
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
if macd_value.Macd is None or macd_value.Signal is None:
return
macd_line = float(macd_value.Macd)
signal_line = float(macd_value.Signal)
if self._cooldown > 0:
self._cooldown -= 1
return
# Entry: MACD bullish
if macd_line > signal_line and self.Position == 0:
self.BuyMarket()
self._cooldown = self.cooldown_bars
# Entry: MACD bearish
elif macd_line < signal_line and self.Position == 0:
self.SellMarket()
self._cooldown = self.cooldown_bars
# Exit on MACD crossover against position
if self.Position > 0 and macd_line < signal_line:
self.SellMarket()
self._cooldown = self.cooldown_bars
elif self.Position < 0 and macd_line > signal_line:
self.BuyMarket()
self._cooldown = self.cooldown_bars
def OnReseted(self):
super(macd_volume_strategy, self).OnReseted()
self._cooldown = 0
def CreateClone(self):
return macd_volume_strategy()