Стратегия Majors Volume Sum
Стратегия суммирует объём со знаком за последние свечи и входит в позицию, когда краткосрочная сумма превышает долю от своего исторического максимума.
Подробности
- Условия входа:
- Сумма объёма за 10 периодов выше
Threshold× максимум и нет позиции — открыть лонг. - Сумма объёма за 10 периодов ниже
-Threshold× максимум и нет позиции — открыть шорт.
- Сумма объёма за 10 периодов выше
- Направление: Лонг и шорт.
- Условия выхода:
- Противоположный сигнал закрывает позицию.
- Стопы: Нет.
- Параметры по умолчанию:
Threshold= 0.75
- Фильтры:
- Категория: Объём
- Направление: Оба
- Индикаторы: SMA
- Стопы: Нет
- Сложность: Низкая
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
using System;
using System.Linq;
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>
/// Majors Volume Sum Strategy - trades based on volume momentum using EMA smoothing.
/// </summary>
public class MajorsVolumeSumStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private decimal _prevClose;
private decimal _volumeEma;
private decimal _maxAbs;
private bool _isReady;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public MajorsVolumeSumStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for processing", "General");
_emaLength = Param(nameof(EmaLength), 10)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Smoothing period for volume", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_volumeEma = 0;
_maxAbs = 0;
_isReady = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!_isReady)
{
_prevClose = candle.ClosePrice;
_isReady = true;
return;
}
// Volume direction based on price change
var direction = candle.ClosePrice > _prevClose ? 1m : -1m;
var signedVol = direction * candle.TotalVolume;
// Simple EMA of signed volume
var k = 2m / (EmaLength + 1m);
_volumeEma = _volumeEma * (1m - k) + signedVol * k;
var absEma = Math.Abs(_volumeEma);
if (absEma > _maxAbs)
_maxAbs = absEma;
// Trade when volume momentum is strong relative to its history
var threshold = _maxAbs * 0.5m;
if (_volumeEma > threshold && candle.ClosePrice > emaVal && Position <= 0)
BuyMarket();
else if (_volumeEma < -threshold && candle.ClosePrice < emaVal && Position >= 0)
SellMarket();
_prevClose = candle.ClosePrice;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class majors_volume_sum_strategy(Strategy):
def __init__(self):
super(majors_volume_sum_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles for processing", "General")
self._ema_length = self.Param("EmaLength", 10) \
.SetDisplay("EMA Length", "Smoothing period for volume", "General")
self._prev_close = 0.0
self._volume_ema = 0.0
self._max_abs = 0.0
self._is_ready = False
@property
def candle_type(self):
return self._candle_type.Value
@property
def ema_length(self):
return self._ema_length.Value
def OnReseted(self):
super(majors_volume_sum_strategy, self).OnReseted()
self._prev_close = 0.0
self._volume_ema = 0.0
self._max_abs = 0.0
self._is_ready = False
def OnStarted2(self, time):
super(majors_volume_sum_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.ema_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
if not self._is_ready:
self._prev_close = candle.ClosePrice
self._is_ready = True
return
# Volume direction based on price change
direction = (1 if candle.ClosePrice > self._prev_close else -1)
signed_vol = direction * candle.TotalVolume
# Simple EMA of signed volume
k = 2.0 / (float(self.ema_length) + 1.0)
self._volume_ema = self._volume_ema * (1 - k) + signed_vol * k
abs_ema = abs(self._volume_ema)
if abs_ema > self._max_abs:
self._max_abs = abs_ema
# Trade when volume momentum is strong relative to its history
threshold = self._max_abs * 0.5
if self._volume_ema > threshold and candle.ClosePrice > ema_val and self.Position <= 0:
self.BuyMarket()
elif self._volume_ema < -threshold and candle.ClosePrice < ema_val and self.Position >= 0:
self.SellMarket()
self._prev_close = candle.ClosePrice
def CreateClone(self):
return majors_volume_sum_strategy()