ボラティリティ・アクション戦略
この戦略は、4時間足で計算されたBill WilliamsのAlligatorトレンドフィルターと短期のボラティリティブレイクアウトを組み合わせます。
トレードルール
- ロングエントリーの条件:
- 期間1のATRが、Volatility Coefと期間ATR PeriodのATRの積より大きい。
- ローソク足が陽線で24本の高値を更新する。
- Alligatorラインが上向きに整列(Lips > Teeth > Jaw)し、始値と終値の両方がTeeth線より上にある。
- ショートエントリー: 上記条件が反対方向に当てはまるとき。
エントリー時に、戦略はATR(1)の倍数としてストップロスとテイクプロフィットのレベルを設定します:
- ストップロス = エントリー価格 ± Stop Coef × ATR(1)
- テイクプロフィット = エントリー価格 ± Profit Coef × ATR(1)
パラメーター
- Volatility Coef – 高速ATRと低速ATRを比較する乗数。
- ATR Period – 低速ATRの期間。
- Stop Coef – ストップロス用ATR乗数。
- Profit Coef – テイクプロフィット用ATR乗数。
- Candle Type – メイン分析の時間軸(Alligatorは4H足を使用)。
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()