Chaos Trader Lite 策略
Chaos Trader Lite 策略使用 StockSharp 的高级 API 复刻了 Bill Williams 的“三个智者”入场模型。系统会在所选时间框架(默认 1 小时)的每根完结 K 线后执行判定,并在任意条件满足时挂出止损订单:
- 第一智者——背离柱:识别多头或空头背离 K 线,并要求价格与 Alligator 嘴唇线之间保持最小距离。
- 第二智者——Awesome Oscillator 加速:等待五个连续的 AO 数值显示动能正在增强。
- 第三智者——分形突破:确认两根之前形成的分形,并检查价格是否远离 Alligator 牙齿线,然后准备突破挂单。
出现做多信号时,策略会取消现有的卖出止损、平掉空头、在前一根 K 线高点上方一个最小跳动处挂出买入止损,同时记录防守止损位于该柱低点下方。做空信号执行完全对称的流程。每根新 K 线都会检查保存的止损价位,一旦被触及便以市价平仓。
指标与计算
- Alligator 嘴唇:对中位价进行 5 周期平滑移动平均(SMMA),并向前平移三根 K 线,通过队列保存以匹配 MetaTrader 的对齐方式。
- Alligator 牙齿:对中位价进行 8 周期 SMMA 并向前平移五根 K 线,作为第三智者的过滤条件。
- Awesome Oscillator:内置指标(中位价的 5 与 34 周期 SMA 差值)为第二智者提供动量序列。
- 分形:检查位于当前柱左侧两个位置的 K 线,当其高点/低点高于(或低于)两侧各两根柱子时认定为有效分形。
交易流程
- 订阅指定类型的蜡烛,并仅处理状态为 Finished 的 K 线。
- 更新 Alligator 与 AO 指标,缓存平移后的结果。
- 依次评估三个智者条件:
- 背离柱需收于上半区(多头)或下半区(空头),且与嘴唇线的距离大于
MagnitudePips * PriceStep。 - AO 加速要求五个值满足
AO[1] > AO[2] > AO[3] > AO[4]且AO[4] < AO[5](空头条件为全体取反)。 - 分形突破要求价格收在确认分形之上(或之下),并且超过 Alligator 牙齿加上距离阈值。
- 背离柱需收于上半区(多头)或下半区(空头),且与嘴唇线的距离大于
- 当条件满足时,按体积
Volume在高点上方一个跳动(或低点下方一个跳动)注册BuyStop或SellStop,同时取消反向挂单并平掉反向持仓。 - 维护追踪止损:多头仅上移,空头仅下移;若被新蜡烛突破,则立即以市价平仓。
参数
MagnitudePips(默认 10) —— 背离柱与 Alligator 嘴唇之间的最小点差。UseFirstWiseMan(默认 true) —— 启用/禁用背离柱信号。UseSecondWiseMan(默认 true) —— 启用/禁用 AO 加速信号。UseThirdWiseMan(默认 true) —— 启用/禁用分形突破信号。Volume(默认 0.01) —— 止损订单的下单数量。CandleType(默认 1 小时) —— 策略使用的蜡烛类型。
备注
- 原始 MQL4 代码中的买卖价检查使用蜡烛收盘价近似实现。
- MetaTrader 的保证金和手数校验已省略,因为 StockSharp 在提交订单时会自行验证。
- 当出现新的反向信号时会取消对应挂单,以保持与原始
CloseAll行为一致。
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>
/// Chaos Trader Lite strategy implementing Bill Williams' three wise men entry concepts.
/// Uses market orders when divergent bars, Awesome Oscillator accelerations or confirmed fractals appear.
/// </summary>
public class ChaosTraderLiteStrategy : Strategy
{
private readonly StrategyParam<int> _lipsShift;
private readonly StrategyParam<int> _teethShift;
private readonly StrategyParam<int> _magnitudePips;
private readonly StrategyParam<bool> _useFirstWiseMan;
private readonly StrategyParam<bool> _useSecondWiseMan;
private readonly StrategyParam<bool> _useThirdWiseMan;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _lipsSmma = null!;
private SimpleMovingAverage _teethSmma = null!;
private AwesomeOscillator _awesomeOscillator = null!;
private readonly List<decimal> _lipsShiftQueue = new();
private readonly List<decimal> _teethShiftQueue = new();
private CandleInfo? _bar0;
private CandleInfo? _bar1;
private CandleInfo? _bar2;
private CandleInfo? _bar3;
private CandleInfo? _bar4;
private decimal? _lips0;
private decimal? _teeth0;
private decimal? _teeth1;
private decimal? _ao0;
private decimal? _ao1;
private decimal? _ao2;
private decimal? _ao3;
private decimal? _ao4;
private decimal? _ao5;
private decimal? _longStopLoss;
private decimal? _shortStopLoss;
// Pending entry prices for stop-like behavior
private decimal? _pendingBuyPrice;
private decimal? _pendingSellPrice;
private decimal? _pendingBuyStop;
private decimal? _pendingSellStop;
/// <summary>
/// Magnitude threshold in pips between price and Alligator lips.
/// </summary>
public int MagnitudePips
{
get => _magnitudePips.Value;
set => _magnitudePips.Value = value;
}
/// <summary>
/// Number of candles used to shift the Alligator lips line.
/// </summary>
public int LipsShift
{
get => _lipsShift.Value;
set => _lipsShift.Value = value;
}
/// <summary>
/// Number of candles used to shift the Alligator teeth line.
/// </summary>
public int TeethShift
{
get => _teethShift.Value;
set => _teethShift.Value = value;
}
/// <summary>
/// Enable the first wise man divergent bar setup.
/// </summary>
public bool UseFirstWiseMan
{
get => _useFirstWiseMan.Value;
set => _useFirstWiseMan.Value = value;
}
/// <summary>
/// Enable the second wise man Awesome Oscillator acceleration setup.
/// </summary>
public bool UseSecondWiseMan
{
get => _useSecondWiseMan.Value;
set => _useSecondWiseMan.Value = value;
}
/// <summary>
/// Enable the third wise man fractal breakout setup.
/// </summary>
public bool UseThirdWiseMan
{
get => _useThirdWiseMan.Value;
set => _useThirdWiseMan.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="ChaosTraderLiteStrategy"/>.
/// </summary>
public ChaosTraderLiteStrategy()
{
_magnitudePips = Param(nameof(MagnitudePips), 10)
.SetGreaterThanZero()
.SetDisplay("Magnitude", "Distance from lips in pips", "General");
_lipsShift = Param(nameof(LipsShift), 3)
.SetNotNegative()
.SetDisplay("Lips Shift", "Shift applied to Alligator lips", "Alligator");
_teethShift = Param(nameof(TeethShift), 5)
.SetNotNegative()
.SetDisplay("Teeth Shift", "Shift applied to Alligator teeth", "Alligator");
_useFirstWiseMan = Param(nameof(UseFirstWiseMan), true)
.SetDisplay("First Wise Man", "Enable divergent bar setup", "General");
_useSecondWiseMan = Param(nameof(UseSecondWiseMan), true)
.SetDisplay("Second Wise Man", "Enable Awesome Oscillator setup", "General");
_useThirdWiseMan = Param(nameof(UseThirdWiseMan), true)
.SetDisplay("Third Wise Man", "Enable fractal breakout setup", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
_bar0 = _bar1 = _bar2 = _bar3 = _bar4 = null;
_lipsShiftQueue.Clear();
_teethShiftQueue.Clear();
_lips0 = null;
_teeth0 = null;
_teeth1 = null;
_ao0 = null;
_ao1 = null;
_ao2 = null;
_ao3 = null;
_ao4 = null;
_ao5 = null;
_longStopLoss = null;
_shortStopLoss = null;
_pendingBuyPrice = null;
_pendingSellPrice = null;
_pendingBuyStop = null;
_pendingSellStop = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_lipsSmma = new SimpleMovingAverage { Length = 5 };
_teethSmma = new SimpleMovingAverage { Length = 8 };
_awesomeOscillator = new AwesomeOscillator { ShortMa = { Length = 5 }, LongMa = { Length = 34 } };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _lipsSmma);
DrawIndicator(area, _teethSmma);
DrawIndicator(area, _awesomeOscillator);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Check pending stop-like entries first
CheckPendingEntries(candle);
UpdateBarHistory(candle);
var median = (candle.HighPrice + candle.LowPrice) / 2m;
var candleInput = new CandleIndicatorValue(_lipsSmma, candle);
var lipsValue = _lipsSmma.Process(new DecimalIndicatorValue(_lipsSmma, median, candle.ServerTime) { IsFinal = true });
var teethValue = _teethSmma.Process(new DecimalIndicatorValue(_teethSmma, median, candle.ServerTime) { IsFinal = true });
var awesomeValue = _awesomeOscillator.Process(new CandleIndicatorValue(_awesomeOscillator, candle));
if (lipsValue.IsFinal)
{
var lips = lipsValue.ToDecimal();
_lipsShiftQueue.Add(lips);
if (_lipsShiftQueue.Count > LipsShift)
{
_lips0 = _lipsShiftQueue[0];
try { _lipsShiftQueue.RemoveAt(0); } catch { }
}
}
if (teethValue.IsFinal)
{
var teeth = teethValue.ToDecimal();
_teethShiftQueue.Add(teeth);
if (_teethShiftQueue.Count > TeethShift)
{
_teeth1 = _teeth0;
_teeth0 = _teethShiftQueue[0];
try { _teethShiftQueue.RemoveAt(0); } catch { }
}
}
if (awesomeValue.IsFinal)
{
var ao = awesomeValue.ToDecimal();
_ao5 = _ao4;
_ao4 = _ao3;
_ao3 = _ao2;
_ao2 = _ao1;
_ao1 = _ao0;
_ao0 = ao;
}
var upFractal = GetUpFractal();
var downFractal = GetDownFractal();
if (_lipsSmma.IsFormed && _teethSmma.IsFormed)
EvaluateSignals(candle, upFractal, downFractal);
UpdateProtection(candle);
}
private void CheckPendingEntries(ICandleMessage candle)
{
// Simulate buy stop: if candle high reaches or exceeds pending buy price, enter long
if (_pendingBuyPrice is decimal buyPrice && candle.HighPrice >= buyPrice)
{
if (Position <= 0)
{
if (Position < 0)
{
BuyMarket();
_shortStopLoss = null;
}
if (Volume > 0)
{
BuyMarket();
_longStopLoss = _pendingBuyStop;
}
}
_pendingBuyPrice = null;
_pendingBuyStop = null;
}
// Simulate sell stop: if candle low reaches or goes below pending sell price, enter short
if (_pendingSellPrice is decimal sellPrice && candle.LowPrice <= sellPrice)
{
if (Position >= 0)
{
if (Position > 0)
{
SellMarket();
_longStopLoss = null;
}
if (Volume > 0)
{
SellMarket();
_shortStopLoss = _pendingSellStop;
}
}
_pendingSellPrice = null;
_pendingSellStop = null;
}
}
private void EvaluateSignals(ICandleMessage candle, decimal? upFractal, decimal? downFractal)
{
if (_bar0 is not CandleInfo current || _bar1 is not CandleInfo previous)
return;
var point = Security?.PriceStep ?? 1m;
var magnitudeThreshold = MagnitudePips * point;
if (UseFirstWiseMan && _lips0 is decimal lips)
{
if (IsBullishDivergent(current, previous))
{
var distance = lips - current.High;
if (distance > magnitudeThreshold)
PlaceBuySetup(current, point);
}
if (IsBearishDivergent(current, previous))
{
var distance = current.Low - lips;
if (distance > magnitudeThreshold)
PlaceSellSetup(current, point);
}
}
if (UseSecondWiseMan && _ao1.HasValue && _ao2.HasValue && _ao3.HasValue && _ao4.HasValue && _ao5.HasValue)
{
var currentAo = _ao1.Value;
var bar2Ao = _ao2.Value;
var bar3Ao = _ao3.Value;
var bar4Ao = _ao4.Value;
var bar5Ao = _ao5.Value;
var bullishAcceleration = currentAo > bar2Ao && bar2Ao > bar3Ao && bar3Ao > bar4Ao && bar4Ao < bar5Ao;
if (bullishAcceleration)
PlaceBuySetup(current, point);
var bearishAcceleration = currentAo < bar2Ao && bar2Ao < bar3Ao && bar3Ao < bar4Ao && bar4Ao > bar5Ao;
if (bearishAcceleration)
PlaceSellSetup(current, point);
}
if (UseThirdWiseMan && _teeth0.HasValue)
{
var teeth = _teeth0.Value;
var offset = MagnitudePips * point;
if (upFractal.HasValue && candle.ClosePrice > teeth + offset)
PlaceBuySetup(current, point);
if (downFractal.HasValue && candle.ClosePrice < teeth - offset)
PlaceSellSetup(current, point);
}
}
private void UpdateProtection(ICandleMessage candle)
{
if (Position > 0 && _longStopLoss is decimal longStop)
{
if (candle.LowPrice <= longStop)
{
SellMarket();
_longStopLoss = null;
}
}
else if (Position < 0 && _shortStopLoss is decimal shortStop)
{
if (candle.HighPrice >= shortStop)
{
BuyMarket();
_shortStopLoss = null;
}
}
}
private void PlaceBuySetup(CandleInfo bar, decimal point)
{
if (Volume <= 0)
return;
var entryPrice = bar.High + point;
if (entryPrice <= 0m)
return;
var stopPrice = bar.Low - point;
// Cancel pending sell
_pendingSellPrice = null;
_pendingSellStop = null;
// Set pending buy entry (simulates buy stop order)
_pendingBuyPrice = entryPrice;
_pendingBuyStop = stopPrice;
}
private void PlaceSellSetup(CandleInfo bar, decimal point)
{
if (Volume <= 0)
return;
var entryPrice = bar.Low - point;
if (entryPrice <= 0m)
return;
var stopPrice = bar.High + point;
// Cancel pending buy
_pendingBuyPrice = null;
_pendingBuyStop = null;
// Set pending sell entry (simulates sell stop order)
_pendingSellPrice = entryPrice;
_pendingSellStop = stopPrice;
}
private static bool IsBullishDivergent(CandleInfo current, CandleInfo previous)
{
var median = (current.High + current.Low) / 2m;
return current.Low < previous.Low && current.Close > median;
}
private static bool IsBearishDivergent(CandleInfo current, CandleInfo previous)
{
var median = (current.High + current.Low) / 2m;
return current.High > previous.High && current.Close < median;
}
private decimal? GetUpFractal()
{
if (_bar0 is not CandleInfo bar0 || _bar1 is not CandleInfo bar1 || _bar2 is not CandleInfo bar2 ||
_bar3 is not CandleInfo bar3 || _bar4 is not CandleInfo bar4)
return null;
return bar2.High > bar3.High && bar2.High > bar4.High && bar2.High > bar1.High && bar2.High > bar0.High
? bar2.High
: null;
}
private decimal? GetDownFractal()
{
if (_bar0 is not CandleInfo bar0 || _bar1 is not CandleInfo bar1 || _bar2 is not CandleInfo bar2 ||
_bar3 is not CandleInfo bar3 || _bar4 is not CandleInfo bar4)
return null;
return bar2.Low < bar3.Low && bar2.Low < bar4.Low && bar2.Low < bar1.Low && bar2.Low < bar0.Low
? bar2.Low
: null;
}
private void UpdateBarHistory(ICandleMessage candle)
{
_bar4 = _bar3;
_bar3 = _bar2;
_bar2 = _bar1;
_bar1 = _bar0;
_bar0 = new CandleInfo
{
Open = candle.OpenPrice,
High = candle.HighPrice,
Low = candle.LowPrice,
Close = candle.ClosePrice
};
}
private struct CandleInfo
{
public decimal Open { get; init; }
public decimal High { get; init; }
public decimal Low { get; init; }
public decimal Close { get; init; }
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import (
SimpleMovingAverage, AwesomeOscillator, CandleIndicatorValue
)
from indicator_extensions import *
class chaos_trader_lite_strategy(Strategy):
"""Chaos Trader Lite: Bill Williams three wise men with divergent bars, AO and fractals."""
def __init__(self):
super(chaos_trader_lite_strategy, self).__init__()
self._magnitude_pips = self.Param("MagnitudePips", 10) \
.SetGreaterThanZero() \
.SetDisplay("Magnitude", "Distance from lips in pips", "General")
self._lips_shift = self.Param("LipsShift", 3) \
.SetDisplay("Lips Shift", "Shift applied to Alligator lips", "Alligator")
self._teeth_shift = self.Param("TeethShift", 5) \
.SetDisplay("Teeth Shift", "Shift applied to Alligator teeth", "Alligator")
self._use_first = self.Param("UseFirstWiseMan", True) \
.SetDisplay("First Wise Man", "Enable divergent bar setup", "General")
self._use_second = self.Param("UseSecondWiseMan", True) \
.SetDisplay("Second Wise Man", "Enable Awesome Oscillator setup", "General")
self._use_third = self.Param("UseThirdWiseMan", True) \
.SetDisplay("Third Wise Man", "Enable fractal breakout setup", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._bars = [None] * 5 # bar0..bar4, each = (o, h, lo, c)
self._lips_queue = []
self._teeth_queue = []
self._lips0 = None
self._teeth0 = None
self._teeth1 = None
self._ao_hist = [None] * 6 # ao0..ao5
self._long_sl = None
self._short_sl = None
self._pending_buy = None
self._pending_sell = None
self._pending_buy_stop = None
self._pending_sell_stop = None
@property
def MagnitudePips(self):
return int(self._magnitude_pips.Value)
@property
def LipsShift(self):
return int(self._lips_shift.Value)
@property
def TeethShift(self):
return int(self._teeth_shift.Value)
@property
def UseFirstWiseMan(self):
return self._use_first.Value
@property
def UseSecondWiseMan(self):
return self._use_second.Value
@property
def UseThirdWiseMan(self):
return self._use_third.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(chaos_trader_lite_strategy, self).OnStarted2(time)
self._bars = [None] * 5
self._lips_queue = []
self._teeth_queue = []
self._lips0 = None
self._teeth0 = None
self._teeth1 = None
self._ao_hist = [None] * 6
self._long_sl = None
self._short_sl = None
self._pending_buy = None
self._pending_sell = None
self._pending_buy_stop = None
self._pending_sell_stop = None
self._lips_sma = SimpleMovingAverage()
self._lips_sma.Length = 5
self._teeth_sma = SimpleMovingAverage()
self._teeth_sma.Length = 8
self._ao = AwesomeOscillator()
self._ao.ShortMa.Length = 5
self._ao.LongMa.Length = 34
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
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
h = float(candle.HighPrice)
lo = float(candle.LowPrice)
o = float(candle.OpenPrice)
c = float(candle.ClosePrice)
# Check pending entries
self._check_pending(candle)
# Update bar history
self._bars[4] = self._bars[3]
self._bars[3] = self._bars[2]
self._bars[2] = self._bars[1]
self._bars[1] = self._bars[0]
self._bars[0] = (o, h, lo, c)
median = (h + lo) / 2.0
# Lips
lips_val = process_float(self._lips_sma, Decimal(median), candle.ServerTime, True)
if lips_val.IsFinal:
lv = float(lips_val.Value)
self._lips_queue.append(lv)
if len(self._lips_queue) > self.LipsShift:
self._lips0 = self._lips_queue.pop(0)
# Teeth
teeth_val = process_float(self._teeth_sma, Decimal(median), candle.ServerTime, True)
if teeth_val.IsFinal:
tv = float(teeth_val.Value)
self._teeth_queue.append(tv)
if len(self._teeth_queue) > self.TeethShift:
self._teeth1 = self._teeth0
self._teeth0 = self._teeth_queue.pop(0)
# AO
ao_val = self._ao.Process(CandleIndicatorValue(self._ao, candle))
if ao_val.IsFinal:
av = float(ao_val.Value)
self._ao_hist[5] = self._ao_hist[4]
self._ao_hist[4] = self._ao_hist[3]
self._ao_hist[3] = self._ao_hist[2]
self._ao_hist[2] = self._ao_hist[1]
self._ao_hist[1] = self._ao_hist[0]
self._ao_hist[0] = av
up_fractal = self._get_up_fractal()
down_fractal = self._get_down_fractal()
if self._lips_sma.IsFormed and self._teeth_sma.IsFormed:
self._evaluate_signals(candle, up_fractal, down_fractal)
self._update_protection(candle)
def _check_pending(self, candle):
h = float(candle.HighPrice)
lo = float(candle.LowPrice)
if self._pending_buy is not None and h >= self._pending_buy:
if self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self._short_sl = None
self.BuyMarket()
self._long_sl = self._pending_buy_stop
self._pending_buy = None
self._pending_buy_stop = None
if self._pending_sell is not None and lo <= self._pending_sell:
if self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self._long_sl = None
self.SellMarket()
self._short_sl = self._pending_sell_stop
self._pending_sell = None
self._pending_sell_stop = None
def _evaluate_signals(self, candle, up_fractal, down_fractal):
cur = self._bars[0]
prev = self._bars[1]
if cur is None or prev is None:
return
sec = self.Security
point = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 1.0
mag_thresh = self.MagnitudePips * point
if self.UseFirstWiseMan and self._lips0 is not None:
lips = self._lips0
if self._is_bull_div(cur, prev):
dist = lips - cur[1]
if dist > mag_thresh:
self._place_buy(cur, point)
if self._is_bear_div(cur, prev):
dist = cur[2] - lips
if dist > mag_thresh:
self._place_sell(cur, point)
if self.UseSecondWiseMan:
ao1 = self._ao_hist[1]
ao2 = self._ao_hist[2]
ao3 = self._ao_hist[3]
ao4 = self._ao_hist[4]
ao5 = self._ao_hist[5]
if ao1 is not None and ao2 is not None and ao3 is not None and ao4 is not None and ao5 is not None:
if ao1 > ao2 and ao2 > ao3 and ao3 > ao4 and ao4 < ao5:
self._place_buy(cur, point)
if ao1 < ao2 and ao2 < ao3 and ao3 < ao4 and ao4 > ao5:
self._place_sell(cur, point)
if self.UseThirdWiseMan and self._teeth0 is not None:
teeth = self._teeth0
offset = self.MagnitudePips * point
c = float(candle.ClosePrice)
if up_fractal is not None and c > teeth + offset:
self._place_buy(cur, point)
if down_fractal is not None and c < teeth - offset:
self._place_sell(cur, point)
def _is_bull_div(self, cur, prev):
median = (cur[1] + cur[2]) / 2.0
return cur[2] < prev[2] and cur[3] > median
def _is_bear_div(self, cur, prev):
median = (cur[1] + cur[2]) / 2.0
return cur[1] > prev[1] and cur[3] < median
def _place_buy(self, bar, point):
entry = bar[1] + point
if entry <= 0:
return
stop = bar[2] - point
self._pending_sell = None
self._pending_sell_stop = None
self._pending_buy = entry
self._pending_buy_stop = stop
def _place_sell(self, bar, point):
entry = bar[2] - point
if entry <= 0:
return
stop = bar[1] + point
self._pending_buy = None
self._pending_buy_stop = None
self._pending_sell = entry
self._pending_sell_stop = stop
def _update_protection(self, candle):
h = float(candle.HighPrice)
lo = float(candle.LowPrice)
if self.Position > 0 and self._long_sl is not None:
if lo <= self._long_sl:
self.SellMarket()
self._long_sl = None
elif self.Position < 0 and self._short_sl is not None:
if h >= self._short_sl:
self.BuyMarket()
self._short_sl = None
def _get_up_fractal(self):
b = self._bars
if b[0] is None or b[1] is None or b[2] is None or b[3] is None or b[4] is None:
return None
if b[2][1] > b[3][1] and b[2][1] > b[4][1] and b[2][1] > b[1][1] and b[2][1] > b[0][1]:
return b[2][1]
return None
def _get_down_fractal(self):
b = self._bars
if b[0] is None or b[1] is None or b[2] is None or b[3] is None or b[4] is None:
return None
if b[2][2] < b[3][2] and b[2][2] < b[4][2] and b[2][2] < b[1][2] and b[2][2] < b[0][2]:
return b[2][2]
return None
def OnReseted(self):
super(chaos_trader_lite_strategy, self).OnReseted()
self._bars = [None] * 5
self._lips_queue = []
self._teeth_queue = []
self._lips0 = None
self._teeth0 = None
self._teeth1 = None
self._ao_hist = [None] * 6
self._long_sl = None
self._short_sl = None
self._pending_buy = None
self._pending_sell = None
self._pending_buy_stop = None
self._pending_sell_stop = None
def CreateClone(self):
return chaos_trader_lite_strategy()