Стратегия Volatile Action
Стратегия сочетает прорыв волатильности на коротком интервале с трендовым фильтром Alligator Билла Вильямса, рассчитанным на таймфрейме 4 часа.
Правила торговли
- Покупка, если:
- ATR с периодом 1 превышает произведение Volatility Coef и ATR с периодом ATR Period.
- Свеча бычья и устанавливает новый максимум за 24 свечи.
- Линии Alligator направлены вверх (Lips > Teeth > Jaw), а цена открытия и закрытия выше линии Teeth.
- Продажа, если выполняются зеркальные условия.
При входе устанавливаются уровни стоп-лосса и тейк-профита как произведения ATR(1):
- Стоп-лосс = цена входа ± Stop Coef × ATR(1)
- Тейк-профит = цена входа ± Profit Coef × ATR(1)
Параметры
- Volatility Coef – множитель сравнения быстрого и медленного ATR.
- ATR Period – период медленного ATR.
- Stop Coef – множитель ATR для стоп-лосса.
- Profit Coef – множитель ATR для тейк-профита.
- Candle Type – таймфрейм основного анализа (Alligator использует свечи 4 часа).
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>
/// Volatile Action strategy.
/// Combines volatility breakout (ATR) with Alligator trend filter.
/// </summary>
public class VolatileActionStrategy : Strategy
{
private readonly StrategyParam<decimal> _volatilityCoef;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private AverageTrueRange _atr1;
private AverageTrueRange _atrBase;
private SmoothedMovingAverage _jaw;
private SmoothedMovingAverage _teeth;
private SmoothedMovingAverage _lips;
public decimal VolatilityCoef { get => _volatilityCoef.Value; set => _volatilityCoef.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VolatileActionStrategy()
{
_volatilityCoef = Param(nameof(VolatilityCoef), 1m)
.SetGreaterThanZero()
.SetDisplay("Volatility Coef", "ATR1 multiplier against base ATR", "General");
_atrPeriod = Param(nameof(AtrPeriod), 23)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Base ATR period", "General");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for main calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_atr1 = default;
_atrBase = default;
_jaw = default;
_teeth = default;
_lips = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_atr1 = new AverageTrueRange { Length = 1 };
_atrBase = new AverageTrueRange { Length = AtrPeriod };
_jaw = new SmoothedMovingAverage { Length = 13 };
_teeth = new SmoothedMovingAverage { Length = 8 };
_lips = new SmoothedMovingAverage { Length = 5 };
Indicators.Add(_atr1);
Indicators.Add(_atrBase);
Indicators.Add(_jaw);
Indicators.Add(_teeth);
Indicators.Add(_lips);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var atr1Val = _atr1.Process(candle);
var atrBaseVal = _atrBase.Process(candle);
var median = (candle.HighPrice + candle.LowPrice) / 2m;
var jawVal = _jaw.Process(median, candle.OpenTime, true);
var teethVal = _teeth.Process(median, candle.OpenTime, true);
var lipsVal = _lips.Process(median, candle.OpenTime, true);
if (!atr1Val.IsFormed || !atrBaseVal.IsFormed || !jawVal.IsFormed || !teethVal.IsFormed || !lipsVal.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var atr1 = atr1Val.ToDecimal();
var atrBase = atrBaseVal.ToDecimal();
var jaw = jawVal.ToDecimal();
var teeth = teethVal.ToDecimal();
var lips = lipsVal.ToDecimal();
var hl = candle.HighPrice - candle.LowPrice;
if (hl == 0) return;
var hc = Math.Abs(candle.HighPrice - candle.ClosePrice);
var lc = Math.Abs(candle.LowPrice - candle.ClosePrice);
// Alligator bullish: lips > teeth > jaw
var bullGator = lips > teeth && teeth > jaw;
// Alligator bearish: lips < teeth < jaw
var bearGator = lips < teeth && teeth < jaw;
// Volatility breakout
var volBreakout = atrBase > 0 && atr1 > VolatilityCoef * atrBase;
if (Position == 0)
{
if (volBreakout && bullGator && candle.ClosePrice > candle.OpenPrice)
{
BuyMarket();
}
else if (volBreakout && bearGator && candle.ClosePrice < candle.OpenPrice)
{
SellMarket();
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageTrueRange, SmoothedMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class volatile_action_strategy(Strategy):
def __init__(self):
super(volatile_action_strategy, self).__init__()
self._volatility_coef = self.Param("VolatilityCoef", 1.0) \
.SetDisplay("Volatility Coef", "ATR1 multiplier against base ATR", "General")
self._atr_period = self.Param("AtrPeriod", 23) \
.SetDisplay("ATR Period", "Base ATR period", "General")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for main calculation", "General")
self._atr1 = None
self._atr_base = None
self._jaw = None
self._teeth = None
self._lips = None
@property
def volatility_coef(self):
return self._volatility_coef.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(volatile_action_strategy, self).OnReseted()
self._atr1 = None
self._atr_base = None
self._jaw = None
self._teeth = None
self._lips = None
def OnStarted2(self, time):
super(volatile_action_strategy, self).OnStarted2(time)
self._atr1 = AverageTrueRange()
self._atr1.Length = 1
self._atr_base = AverageTrueRange()
self._atr_base.Length = self.atr_period
self._jaw = SmoothedMovingAverage()
self._jaw.Length = 13
self._teeth = SmoothedMovingAverage()
self._teeth.Length = 8
self._lips = SmoothedMovingAverage()
self._lips.Length = 5
self.Indicators.Add(self._atr1)
self.Indicators.Add(self._atr_base)
self.Indicators.Add(self._jaw)
self.Indicators.Add(self._teeth)
self.Indicators.Add(self._lips)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
cv1 = CandleIndicatorValue(self._atr1, candle)
atr1_val = self._atr1.Process(cv1)
cv2 = CandleIndicatorValue(self._atr_base, candle)
atr_base_val = self._atr_base.Process(cv2)
median = (candle.HighPrice + candle.LowPrice) / 2
t = candle.OpenTime
jaw_val = process_float(self._jaw, median, t, True)
teeth_val = process_float(self._teeth, median, t, True)
lips_val = process_float(self._lips, median, t, True)
if (not atr1_val.IsFormed or not atr_base_val.IsFormed or
not jaw_val.IsFormed or not teeth_val.IsFormed or not lips_val.IsFormed):
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
atr1 = float(atr1_val)
atr_base = float(atr_base_val)
jaw = float(jaw_val)
teeth = float(teeth_val)
lips = float(lips_val)
bull_gator = lips > teeth and teeth > jaw
bear_gator = lips < teeth and teeth < jaw
vol_breakout = atr_base > 0 and atr1 > float(self.volatility_coef) * atr_base
if self.Position == 0:
if vol_breakout and bull_gator and float(candle.ClosePrice) > float(candle.OpenPrice):
self.BuyMarket()
elif vol_breakout and bear_gator and float(candle.ClosePrice) < float(candle.OpenPrice):
self.SellMarket()
def CreateClone(self):
return volatile_action_strategy()