Iron Bot Statistical Trend Filter Strategy
该策略利用斐波那契区间和Z分数计算的统计趋势水平进行突破交易。
详情
- 入场条件:
- 多头: 价格突破趋势线和高位水平,Z分数≥0。
- 空头: 价格跌破趋势线和低位水平,Z分数≤0。
- 多空: 双向。
- 出场条件:
- 止损:距离开仓价
SlRatio百分比。 - 止盈:在四个水平之一 (
Tp1Ratio–Tp4Ratio)。
- 止损:距离开仓价
- Stops: 是。
- 默认值:
ZLength= 40.AnalysisWindow= 44.HighTrendLimit= 0.236.LowTrendLimit= 0.786.EmaLength= 200.
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: Z-score, EMA, price action
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Any
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
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>
/// Trades when price breaks statistical trend levels using Z-Score and Fibonacci ranges.
/// </summary>
public class IronBotStatisticalTrendFilterStrategy : Strategy
{
private readonly StrategyParam<int> _zLength;
private readonly StrategyParam<int> _analysisWindow;
private readonly StrategyParam<decimal> _highTrendLimit;
private readonly StrategyParam<decimal> _lowTrendLimit;
private readonly StrategyParam<decimal> _slRatio;
private readonly StrategyParam<decimal> _tp1Ratio;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _highestHigh;
private decimal _lowestLow;
private int _barCount;
/// <summary>
/// Z-score length.
/// </summary>
public int ZLength
{
get => _zLength.Value;
set => _zLength.Value = value;
}
/// <summary>
/// Trend analysis window.
/// </summary>
public int AnalysisWindow
{
get => _analysisWindow.Value;
set => _analysisWindow.Value = value;
}
/// <summary>
/// High trend Fibonacci level.
/// </summary>
public decimal HighTrendLimit
{
get => _highTrendLimit.Value;
set => _highTrendLimit.Value = value;
}
/// <summary>
/// Low trend Fibonacci level.
/// </summary>
public decimal LowTrendLimit
{
get => _lowTrendLimit.Value;
set => _lowTrendLimit.Value = value;
}
/// <summary>
/// Stop loss percent.
/// </summary>
public decimal SlRatio
{
get => _slRatio.Value;
set => _slRatio.Value = value;
}
/// <summary>
/// Take profit percent.
/// </summary>
public decimal Tp1Ratio
{
get => _tp1Ratio.Value;
set => _tp1Ratio.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="IronBotStatisticalTrendFilterStrategy"/> class.
/// </summary>
public IronBotStatisticalTrendFilterStrategy()
{
_zLength = Param(nameof(ZLength), 40)
.SetDisplay("Z Length", "Length for Z-score.", "General");
_analysisWindow = Param(nameof(AnalysisWindow), 44)
.SetDisplay("Analysis Window", "Lookback for trend.", "General");
_highTrendLimit = Param(nameof(HighTrendLimit), 0.236m)
.SetDisplay("Fibo High", "High trend Fibonacci.", "General");
_lowTrendLimit = Param(nameof(LowTrendLimit), 0.786m)
.SetDisplay("Fibo Low", "Low trend Fibonacci.", "General");
_slRatio = Param(nameof(SlRatio), 0.008m)
.SetDisplay("Stop %", "Stop loss percent.", "Risk");
_tp1Ratio = Param(nameof(Tp1Ratio), 0.0075m)
.SetDisplay("TP1 %", "Take profit level.", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_entryPrice = 0m;
_highestHigh = 0m;
_lowestLow = decimal.MaxValue;
_barCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0m;
_highestHigh = 0m;
_lowestLow = decimal.MaxValue;
_barCount = 0;
var sma = new SimpleMovingAverage { Length = ZLength };
var std = new StandardDeviation { Length = ZLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, std, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal stdValue)
{
if (candle.State != CandleStates.Finished)
return;
// Track highest/lowest over analysis window manually
_barCount++;
if (_barCount <= AnalysisWindow)
{
if (candle.HighPrice > _highestHigh) _highestHigh = candle.HighPrice;
if (candle.LowPrice < _lowestLow) _lowestLow = candle.LowPrice;
if (_barCount < AnalysisWindow) return;
}
else
{
// Simple rolling update
if (candle.HighPrice > _highestHigh) _highestHigh = candle.HighPrice;
if (candle.LowPrice < _lowestLow) _lowestLow = candle.LowPrice;
}
var zScore = stdValue == 0m ? 0m : (candle.ClosePrice - smaValue) / stdValue;
var range = _highestHigh - _lowestLow;
if (range <= 0) return;
var highTrendLevel = _highestHigh - range * HighTrendLimit;
var trendLine = _highestHigh - range * 0.5m;
var lowTrendLevel = _highestHigh - range * LowTrendLimit;
// Exit logic
if (Position > 0)
{
var pnlPct = (_entryPrice > 0) ? (candle.ClosePrice - _entryPrice) / _entryPrice : 0m;
if (pnlPct <= -SlRatio || pnlPct >= Tp1Ratio)
{
SellMarket();
_entryPrice = 0m;
}
return;
}
else if (Position < 0)
{
var pnlPct = (_entryPrice > 0) ? (_entryPrice - candle.ClosePrice) / _entryPrice : 0m;
if (pnlPct <= -SlRatio || pnlPct >= Tp1Ratio)
{
BuyMarket();
_entryPrice = 0m;
}
return;
}
// Entry logic
var canLong = candle.ClosePrice >= trendLine && candle.ClosePrice >= highTrendLevel && zScore >= 0m;
var canShort = candle.ClosePrice <= trendLine && candle.ClosePrice <= lowTrendLevel && zScore <= 0m;
if (canLong)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (canShort)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class iron_bot_statistical_trend_filter_strategy(Strategy):
def __init__(self):
super(iron_bot_statistical_trend_filter_strategy, self).__init__()
self._z_length = self.Param("ZLength", 40) \
.SetDisplay("Z Length", "Length for Z-score", "General")
self._analysis_window = self.Param("AnalysisWindow", 44) \
.SetDisplay("Analysis Window", "Lookback for trend", "General")
self._high_trend_limit = self.Param("HighTrendLimit", 0.236) \
.SetDisplay("Fibo High", "High trend Fibonacci", "General")
self._low_trend_limit = self.Param("LowTrendLimit", 0.786) \
.SetDisplay("Fibo Low", "Low trend Fibonacci", "General")
self._sl_ratio = self.Param("SlRatio", 0.008) \
.SetDisplay("Stop %", "Stop loss percent", "Risk")
self._tp1_ratio = self.Param("Tp1Ratio", 0.0075) \
.SetDisplay("TP1 %", "Take profit level", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._entry_price = 0.0
self._highest_high = 0.0
self._lowest_low = 999999999.0
self._bar_count = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(iron_bot_statistical_trend_filter_strategy, self).OnReseted()
self._entry_price = 0.0
self._highest_high = 0.0
self._lowest_low = 999999999.0
self._bar_count = 0
def OnStarted2(self, time):
super(iron_bot_statistical_trend_filter_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._z_length.Value
std = StandardDeviation()
std.Length = self._z_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, std, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, sma_val, std_val):
if candle.State != CandleStates.Finished:
return
self._bar_count += 1
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if high > self._highest_high:
self._highest_high = high
if low < self._lowest_low:
self._lowest_low = low
aw = self._analysis_window.Value
if self._bar_count < aw:
return
sma_v = float(sma_val)
std_v = float(std_val)
z_score = 0.0 if std_v == 0 else (close - sma_v) / std_v
rng = self._highest_high - self._lowest_low
if rng <= 0:
return
ht = float(self._high_trend_limit.Value)
lt = float(self._low_trend_limit.Value)
high_trend_level = self._highest_high - rng * ht
trend_line = self._highest_high - rng * 0.5
low_trend_level = self._highest_high - rng * lt
sl = float(self._sl_ratio.Value)
tp = float(self._tp1_ratio.Value)
if self.Position > 0:
pnl = (close - self._entry_price) / self._entry_price if self._entry_price > 0 else 0
if pnl <= -sl or pnl >= tp:
self.SellMarket()
self._entry_price = 0.0
return
elif self.Position < 0:
pnl = (self._entry_price - close) / self._entry_price if self._entry_price > 0 else 0
if pnl <= -sl or pnl >= tp:
self.BuyMarket()
self._entry_price = 0.0
return
can_long = close >= trend_line and close >= high_trend_level and z_score >= 0
can_short = close <= trend_line and close <= low_trend_level and z_score <= 0
if can_long:
self.BuyMarket()
self._entry_price = close
elif can_short:
self.SellMarket()
self._entry_price = close
def CreateClone(self):
return iron_bot_statistical_trend_filter_strategy()