波动行动策略
该策略结合了短期波动性突破和比尔·威廉姆斯的 Alligator 趋势过滤器,Alligator 在4小时周期上计算。
交易规则
- 做多 条件:
- 周期为1的ATR大于 Volatility Coef 与周期为 ATR Period 的ATR的乘积。
- K线收阳并且创下最近24根K线的新高。
- 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 使用4小时K线)。
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()