MA Volume Strategie
MA Volume kombiniert einen gleitenden Durchschnitt als Trendfilter mit Volumenspitzen für das Einstiegs-Timing. Steigendes Volumen bei einem Preis über dem Durchschnitt signalisiert starke Akkumulation; fallendes Volumen unterhalb des Durchschnitts deutet auf Distribution hin.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 136%. Die Strategie funktioniert am besten auf dem Aktienmarkt.
Die Strategie handelt in Richtung des gleitenden Durchschnitts, wenn das Volumen zunimmt, und steigt aus, sobald das Volumen versiegt oder der Durchschnitt dreht.
Ein prozentualer Stop schützt vor plötzlichen Trendwenden.
Details
- Einstiegskriterien: Indikatorsignal
- Long/Short: Beide
- Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
- Stops: Ja, prozentbasiert
- Standardwerte:
CandleType= 15 minuteStopLoss= 2%
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: Moving Average, Volume
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Strategy that combines moving average and volume indicators.
/// Buys on MA crossover with volume confirmation, sells on reverse crossover.
/// </summary>
public class MaVolumeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _volumeThreshold;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevClose;
private decimal _prevSma;
private bool _hasPrev;
private decimal _prevVolume;
private int _cooldown;
/// <summary>
/// Data type for candles.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Period for moving average calculation.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Volume threshold multiplier for volume confirmation.
/// </summary>
public decimal VolumeThreshold
{
get => _volumeThreshold.Value;
set => _volumeThreshold.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="MaVolumeStrategy"/>.
/// </summary>
public MaVolumeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetDisplay("MA Period", "Period for moving average calculation", "MA Settings");
_volumeThreshold = Param(nameof(VolumeThreshold), 1.2m)
.SetDisplay("Volume Threshold", "Volume threshold multiplier", "Volume Settings");
_cooldownBars = Param(nameof(CooldownBars), 150)
.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();
_prevClose = 0;
_prevSma = 0;
_hasPrev = false;
_prevVolume = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var volumeSma = new SimpleMovingAverage { Length = 20 };
var priceSma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
// Use volumeSma to track volume average via separate bind
subscription.Bind(priceSma, OnProcess);
subscription
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, priceSma);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var vol = candle.TotalVolume;
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close;
_prevSma = smaValue;
_prevVolume = vol;
_hasPrev = true;
return;
}
// Volume confirmation: current volume is above threshold * previous volume
var volumeOk = _prevVolume > 0 && vol > _prevVolume * VolumeThreshold;
if (_hasPrev)
{
// Price crosses above MA with volume - buy
if (_prevClose <= _prevSma && close > smaValue && volumeOk && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Price crosses below MA with volume - sell
else if (_prevClose >= _prevSma && close < smaValue && volumeOk && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long on MA cross down
else if (_prevClose >= _prevSma && close < smaValue && Position > 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short on MA cross up
else if (_prevClose <= _prevSma && close > smaValue && Position < 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
_prevClose = close;
_prevSma = smaValue;
_prevVolume = vol;
_hasPrev = true;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_volume_strategy(Strategy):
"""
MA Volume strategy.
Buys on MA crossover with volume confirmation, sells on reverse crossover.
"""
def __init__(self):
super(ma_volume_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._ma_period = self.Param("MaPeriod", 20).SetDisplay("MA Period", "Period for moving average calculation", "MA Settings")
self._volume_threshold = self.Param("VolumeThreshold", 1.2).SetDisplay("Volume Threshold", "Volume threshold multiplier", "Volume Settings")
self._cooldown_bars = self.Param("CooldownBars", 150).SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._prev_volume = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ma_volume_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._prev_volume = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(ma_volume_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._prev_volume = 0.0
self._cooldown = 0
price_sma = SimpleMovingAverage()
price_sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(price_sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, price_sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma = float(sma_val)
vol = float(candle.TotalVolume)
cd = self._cooldown_bars.Value
threshold = float(self._volume_threshold.Value)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_close = close
self._prev_sma = sma
self._prev_volume = vol
self._has_prev = True
return
# Volume confirmation: current volume is above threshold * previous volume
volume_ok = self._prev_volume > 0 and vol > self._prev_volume * threshold
if self._has_prev:
# Price crosses above MA with volume - buy
if self._prev_close <= self._prev_sma and close > sma and volume_ok and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
# Price crosses below MA with volume - sell
elif self._prev_close >= self._prev_sma and close < sma and volume_ok and self.Position == 0:
self.SellMarket()
self._cooldown = cd
# Exit long on MA cross down
elif self._prev_close >= self._prev_sma and close < sma and self.Position > 0:
self.SellMarket()
self._cooldown = cd
# Exit short on MA cross up
elif self._prev_close <= self._prev_sma and close > sma and self.Position < 0:
self.BuyMarket()
self._cooldown = cd
self._prev_close = close
self._prev_sma = sma
self._prev_volume = vol
self._has_prev = True
def CreateClone(self):
return ma_volume_strategy()