Williams Alligator ATR 策略
该策略使用 Williams Alligator 指标并结合 ATR 止损。当 Lips 线向上穿越 Jaw 线时开多单。当 Lips 向下穿越 Jaw 或价格跌至 ATR 止损位时平仓。
详情
- 入场:Lips 向上穿越 Jaw。
- 出场:Lips 向下穿越 Jaw 或触发 ATR 止损。
- 指标:Smoothed Moving Average、Average True Range。
- 类型:仅做多。
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>
/// Williams Alligator strategy with ATR-based stop-loss.
/// Uses three smoothed moving averages (Jaw, Teeth, Lips) for trend detection.
/// </summary>
public class WilliamsAlligatorAtrStrategy : Strategy
{
private readonly StrategyParam<int> _jawLength;
private readonly StrategyParam<int> _teethLength;
private readonly StrategyParam<int> _lipsLength;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private bool _prevLipsAboveJaw;
private bool _prevLipsBelowJaw;
private bool _isInitialized;
private decimal _entryPrice;
private int _cooldownRemaining;
public int JawLength { get => _jawLength.Value; set => _jawLength.Value = value; }
public int TeethLength { get => _teethLength.Value; set => _teethLength.Value = value; }
public int LipsLength { get => _lipsLength.Value; set => _lipsLength.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public WilliamsAlligatorAtrStrategy()
{
_jawLength = Param(nameof(JawLength), 13)
.SetGreaterThanZero()
.SetDisplay("Jaw Length", "Alligator jaw period", "Alligator");
_teethLength = Param(nameof(TeethLength), 8)
.SetGreaterThanZero()
.SetDisplay("Teeth Length", "Alligator teeth period", "Alligator");
_lipsLength = Param(nameof(LipsLength), 5)
.SetGreaterThanZero()
.SetDisplay("Lips Length", "Alligator lips period", "Alligator");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for stop-loss", "ATR");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "ATR multiplier for stop-loss", "ATR");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevLipsAboveJaw = false;
_prevLipsBelowJaw = false;
_isInitialized = false;
_entryPrice = 0m;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var jaw = new SmoothedMovingAverage { Length = JawLength };
var lips = new SmoothedMovingAverage { Length = LipsLength };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(jaw, lips, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, jaw);
DrawIndicator(area, lips);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal jawVal, decimal lipsVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevLipsAboveJaw = lipsVal > jawVal;
_prevLipsBelowJaw = lipsVal < jawVal;
_isInitialized = true;
return;
}
if (!_isInitialized)
{
_prevLipsAboveJaw = lipsVal > jawVal;
_prevLipsBelowJaw = lipsVal < jawVal;
_isInitialized = true;
return;
}
var lipsAboveJaw = lipsVal > jawVal;
var lipsBelowJaw = lipsVal < jawVal;
// Check ATR stop for existing positions
if (Position > 0 && _entryPrice > 0 && atrVal > 0)
{
if (candle.ClosePrice <= _entryPrice - AtrMultiplier * atrVal)
{
SellMarket(Math.Abs(Position));
_entryPrice = 0m;
_cooldownRemaining = CooldownBars;
_prevLipsAboveJaw = lipsAboveJaw;
_prevLipsBelowJaw = lipsBelowJaw;
return;
}
}
else if (Position < 0 && _entryPrice > 0 && atrVal > 0)
{
if (candle.ClosePrice >= _entryPrice + AtrMultiplier * atrVal)
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0m;
_cooldownRemaining = CooldownBars;
_prevLipsAboveJaw = lipsAboveJaw;
_prevLipsBelowJaw = lipsBelowJaw;
return;
}
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevLipsAboveJaw = lipsAboveJaw;
_prevLipsBelowJaw = lipsBelowJaw;
return;
}
// Long entry: lips crosses above jaw
if (!_prevLipsAboveJaw && lipsAboveJaw && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = candle.ClosePrice;
_cooldownRemaining = CooldownBars;
}
// Short entry: lips crosses below jaw
else if (!_prevLipsBelowJaw && lipsBelowJaw && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = candle.ClosePrice;
_cooldownRemaining = CooldownBars;
}
_prevLipsAboveJaw = lipsAboveJaw;
_prevLipsBelowJaw = lipsBelowJaw;
}
}
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 SmoothedMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class williams_alligator_atr_strategy(Strategy):
def __init__(self):
super(williams_alligator_atr_strategy, self).__init__()
self._jaw_length = self.Param("JawLength", 13) \
.SetGreaterThanZero() \
.SetDisplay("Jaw Length", "Alligator jaw period", "Alligator")
self._lips_length = self.Param("LipsLength", 5) \
.SetGreaterThanZero() \
.SetDisplay("Lips Length", "Alligator lips period", "Alligator")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "ATR period for stop-loss", "ATR")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("ATR Multiplier", "ATR multiplier for stop-loss", "ATR")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._prev_lips_above_jaw = False
self._prev_lips_below_jaw = False
self._is_initialized = False
self._entry_price = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(williams_alligator_atr_strategy, self).OnReseted()
self._prev_lips_above_jaw = False
self._prev_lips_below_jaw = False
self._is_initialized = False
self._entry_price = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(williams_alligator_atr_strategy, self).OnStarted2(time)
jaw = SmoothedMovingAverage()
jaw.Length = int(self._jaw_length.Value)
lips = SmoothedMovingAverage()
lips.Length = int(self._lips_length.Value)
atr = AverageTrueRange()
atr.Length = int(self._atr_period.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(jaw, lips, atr, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, jaw)
self.DrawIndicator(area, lips)
self.DrawOwnTrades(area)
def _on_process(self, candle, jaw_val, lips_val, atr_val):
if candle.State != CandleStates.Finished:
return
jaw_v = float(jaw_val)
lips_v = float(lips_val)
atr_v = float(atr_val)
close = float(candle.ClosePrice)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_lips_above_jaw = lips_v > jaw_v
self._prev_lips_below_jaw = lips_v < jaw_v
self._is_initialized = True
return
if not self._is_initialized:
self._prev_lips_above_jaw = lips_v > jaw_v
self._prev_lips_below_jaw = lips_v < jaw_v
self._is_initialized = True
return
lips_above_jaw = lips_v > jaw_v
lips_below_jaw = lips_v < jaw_v
atr_mult = float(self._atr_multiplier.Value)
cooldown = int(self._cooldown_bars.Value)
# Check ATR stop for existing positions
if self.Position > 0 and self._entry_price > 0 and atr_v > 0:
if close <= self._entry_price - atr_mult * atr_v:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
self._prev_lips_above_jaw = lips_above_jaw
self._prev_lips_below_jaw = lips_below_jaw
return
elif self.Position < 0 and self._entry_price > 0 and atr_v > 0:
if close >= self._entry_price + atr_mult * atr_v:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
self._prev_lips_above_jaw = lips_above_jaw
self._prev_lips_below_jaw = lips_below_jaw
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_lips_above_jaw = lips_above_jaw
self._prev_lips_below_jaw = lips_below_jaw
return
if not self._prev_lips_above_jaw and lips_above_jaw and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
elif not self._prev_lips_below_jaw and lips_below_jaw and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
self._prev_lips_above_jaw = lips_above_jaw
self._prev_lips_below_jaw = lips_below_jaw
def CreateClone(self):
return williams_alligator_atr_strategy()