Bill Williams Trader 策略
该策略基于 Alligator 指标和 Fractals,是 Bill Williams 交易方法的简化实现。
工作原理
- 计算 Alligator 线(SMMA):
- Jaw 长度 13
- Teeth 长度 8
- Lips 长度 5
- 在完成的蜡烛上检测向上和向下的分形。
- 当价格突破位于 Teeth 之上的最后一个上分形时 买入。
- 当价格跌破位于 Teeth 之下的最后一个下分形时 卖出。
- 当收盘价跌破 Lips 线时 平仓多单。
- 当收盘价突破 Lips 线上方时 平仓空单。
参数
| 名称 | 说明 | 默认值 |
|---|---|---|
JawLength |
Alligator 下颚 SMMA 周期 | 13 |
TeethLength |
Alligator 牙齿 SMMA 周期 | 8 |
LipsLength |
Alligator 嘴唇 SMMA 周期 | 5 |
CandleType |
计算所用的蜡烛类型 | 15 分钟蜡烛 |
所有参数都支持在策略参数界面中优化。
使用方法
- 编译解决方案:
dotnet build - 在 StockSharp 环境中运行该策略,并选择所需的证券和时间框架。
备注
该示例演示了高层 API 的使用,并未实现复杂的头寸管理或风险控制。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bill Williams Trader strategy based on Alligator and Fractals.
/// Buys when price breaks above an upper fractal above the Alligator teeth.
/// Sells when price breaks below a lower fractal below the Alligator teeth.
/// Exits when price crosses the Alligator lips in the opposite direction.
/// </summary>
public class BillWilliamsTraderStrategy : Strategy
{
private readonly StrategyParam<int> _jawLength;
private readonly StrategyParam<int> _teethLength;
private readonly StrategyParam<int> _lipsLength;
private readonly StrategyParam<DataType> _candleType;
private SmoothedMovingAverage _jaw;
private SmoothedMovingAverage _teeth;
private SmoothedMovingAverage _lips;
private readonly decimal[] _highBuffer = new decimal[5];
private readonly decimal[] _lowBuffer = new decimal[5];
private decimal? _upFractal;
private decimal? _downFractal;
/// <summary>
/// Jaw SMMA period.
/// </summary>
public int JawLength
{
get => _jawLength.Value;
set => _jawLength.Value = value;
}
/// <summary>
/// Teeth SMMA period.
/// </summary>
public int TeethLength
{
get => _teethLength.Value;
set => _teethLength.Value = value;
}
/// <summary>
/// Lips SMMA period.
/// </summary>
public int LipsLength
{
get => _lipsLength.Value;
set => _lipsLength.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="BillWilliamsTraderStrategy"/>.
/// </summary>
public BillWilliamsTraderStrategy()
{
_jawLength = Param(nameof(JawLength), 13)
.SetGreaterThanZero()
.SetDisplay("Jaw Length", "Alligator jaw period", "Alligator")
.SetOptimize(10, 20, 1);
_teethLength = Param(nameof(TeethLength), 8)
.SetGreaterThanZero()
.SetDisplay("Teeth Length", "Alligator teeth period", "Alligator")
.SetOptimize(5, 15, 1);
_lipsLength = Param(nameof(LipsLength), 5)
.SetGreaterThanZero()
.SetDisplay("Lips Length", "Alligator lips period", "Alligator")
.SetOptimize(3, 10, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
Array.Clear(_highBuffer);
Array.Clear(_lowBuffer);
_upFractal = null;
_downFractal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_jaw = new SmoothedMovingAverage { Length = JawLength };
_teeth = new SmoothedMovingAverage { Length = TeethLength };
_lips = new SmoothedMovingAverage { Length = LipsLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _jaw);
DrawIndicator(area, _teeth);
DrawIndicator(area, _lips);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
var median = (candle.HighPrice + candle.LowPrice) / 2m;
var isFinal = candle.State == CandleStates.Finished;
var jawVal = _jaw.Process(new DecimalIndicatorValue(_jaw, median, candle.ServerTime) { IsFinal = isFinal });
var teethVal = _teeth.Process(new DecimalIndicatorValue(_teeth, median, candle.ServerTime) { IsFinal = isFinal });
var lipsVal = _lips.Process(new DecimalIndicatorValue(_lips, median, candle.ServerTime) { IsFinal = isFinal });
// shift buffers for fractal detection
for (var i = 0; i < 4; i++)
{
_highBuffer[i] = _highBuffer[i + 1];
_lowBuffer[i] = _lowBuffer[i + 1];
}
_highBuffer[4] = candle.HighPrice;
_lowBuffer[4] = candle.LowPrice;
if (candle.State != CandleStates.Finished || !_jaw.IsFormed || !_teeth.IsFormed || !_lips.IsFormed)
return;
// detect fractals using the middle bar
var h2 = _highBuffer[2];
if (h2 > _highBuffer[0] && h2 > _highBuffer[1] && h2 > _highBuffer[3] && h2 > _highBuffer[4])
_upFractal = h2;
var l2 = _lowBuffer[2];
if (l2 < _lowBuffer[0] && l2 < _lowBuffer[1] && l2 < _lowBuffer[3] && l2 < _lowBuffer[4])
_downFractal = l2;
var jaw = jawVal.ToDecimal();
var teeth = teethVal.ToDecimal();
var lips = lipsVal.ToDecimal();
// entry conditions: fractal breakout with Alligator trend filter
if (_upFractal is decimal up && candle.ClosePrice > up && lips > teeth && teeth > jaw && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_upFractal = null;
}
else if (_downFractal is decimal down && candle.ClosePrice < down && lips < teeth && teeth < jaw && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_downFractal = null;
}
// exit conditions based on lips cross
if (Position > 0 && candle.ClosePrice < lips)
SellMarket(Position);
else if (Position < 0 && candle.ClosePrice > lips)
BuyMarket(-Position);
}
}
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, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class bill_williams_trader_strategy(Strategy):
def __init__(self):
super(bill_williams_trader_strategy, self).__init__()
self._jaw_length = self.Param("JawLength", 13)
self._teeth_length = self.Param("TeethLength", 8)
self._lips_length = self.Param("LipsLength", 5)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._jaw = None
self._teeth = None
self._lips = None
self._high_buffer = [0.0, 0.0, 0.0, 0.0, 0.0]
self._low_buffer = [0.0, 0.0, 0.0, 0.0, 0.0]
self._up_fractal = None
self._down_fractal = None
@property
def JawLength(self):
return self._jaw_length.Value
@JawLength.setter
def JawLength(self, value):
self._jaw_length.Value = value
@property
def TeethLength(self):
return self._teeth_length.Value
@TeethLength.setter
def TeethLength(self, value):
self._teeth_length.Value = value
@property
def LipsLength(self):
return self._lips_length.Value
@LipsLength.setter
def LipsLength(self, value):
self._lips_length.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(bill_williams_trader_strategy, self).OnStarted2(time)
self._high_buffer = [0.0, 0.0, 0.0, 0.0, 0.0]
self._low_buffer = [0.0, 0.0, 0.0, 0.0, 0.0]
self._up_fractal = None
self._down_fractal = None
self._jaw = SmoothedMovingAverage()
self._jaw.Length = self.JawLength
self._teeth = SmoothedMovingAverage()
self._teeth.Length = self.TeethLength
self._lips = SmoothedMovingAverage()
self._lips.Length = self.LipsLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
h = float(candle.HighPrice)
l = float(candle.LowPrice)
median = (h + l) / 2.0
is_final = candle.State == CandleStates.Finished
jaw_result = process_float(self._jaw, median, candle.ServerTime, is_final)
teeth_result = process_float(self._teeth, median, candle.ServerTime, is_final)
lips_result = process_float(self._lips, median, candle.ServerTime, is_final)
for i in range(4):
self._high_buffer[i] = self._high_buffer[i + 1]
self._low_buffer[i] = self._low_buffer[i + 1]
self._high_buffer[4] = h
self._low_buffer[4] = l
if not is_final:
return
if not self._jaw.IsFormed or not self._teeth.IsFormed or not self._lips.IsFormed:
return
h2 = self._high_buffer[2]
if h2 > self._high_buffer[0] and h2 > self._high_buffer[1] and h2 > self._high_buffer[3] and h2 > self._high_buffer[4]:
self._up_fractal = h2
l2 = self._low_buffer[2]
if l2 < self._low_buffer[0] and l2 < self._low_buffer[1] and l2 < self._low_buffer[3] and l2 < self._low_buffer[4]:
self._down_fractal = l2
jaw_val = float(jaw_result)
teeth_val = float(teeth_result)
lips_val = float(lips_result)
close = float(candle.ClosePrice)
pos = float(self.Position)
vol = float(self.Volume)
if self._up_fractal is not None and close > self._up_fractal and lips_val > teeth_val and teeth_val > jaw_val and pos <= 0:
self.BuyMarket(vol + abs(pos))
self._up_fractal = None
elif self._down_fractal is not None and close < self._down_fractal and lips_val < teeth_val and teeth_val < jaw_val and pos >= 0:
self.SellMarket(vol + abs(pos))
self._down_fractal = None
pos = float(self.Position)
if pos > 0 and close < lips_val:
self.SellMarket(pos)
elif pos < 0 and close > lips_val:
self.BuyMarket(-pos)
def OnReseted(self):
super(bill_williams_trader_strategy, self).OnReseted()
self._jaw = None
self._teeth = None
self._lips = None
self._high_buffer = [0.0, 0.0, 0.0, 0.0, 0.0]
self._low_buffer = [0.0, 0.0, 0.0, 0.0, 0.0]
self._up_fractal = None
self._down_fractal = None
def CreateClone(self):
return bill_williams_trader_strategy()