自适应斐波那契回撤策略
该策略将三条使用斐波那契倍数(0.618、1.618、2.618)构建的SuperTrend线求平均,并通过EMA平滑。交易基于回撤至此自适应趋势,同时使用AMA中线和可选的RSI过滤方向。
细节
- 入场条件:
- 最低价跌破平均SuperTrend且收盘价高于其平滑值。
- 前一根K线相对于AMA中线的位置定义回撤。
- 多头:收盘价高于中线且RSI > 阈值。
- 空头:收盘价低于中线且RSI < 阈值。
- 多空方向:双向。
- 离场条件:
- 收盘价反向穿越平滑后的SuperTrend。
- 止损/止盈:通过
StartProtection设置百分比止损和止盈。 - 默认值:
AtrPeriod= 8SmoothLength= 21AmaLength= 55RsiLength= 7RsiBuy= 70RsiSell= 30TakeProfitPercent= 5StopLossPercent= 0.75
- 筛选项:
- 类别:趋势回撤
- 方向:双向
- 指标:SuperTrend、EMA、AMA、RSI
- 止损:是
- 复杂度:中等
- 时间框架:任意
- 季节性:无
- 神经网络:无
- 背离:无
- 风险级别:中等
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Adaptive Fibonacci Pullback strategy.
/// Combines multiple SuperTrend lines and an adaptive moving average channel.
/// </summary>
public class AdaptiveFibonacciPullbackStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _factor1;
private readonly StrategyParam<decimal> _factor2;
private readonly StrategyParam<decimal> _factor3;
private readonly StrategyParam<int> _smoothLength;
private readonly StrategyParam<int> _amaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _rsiBuy;
private readonly StrategyParam<decimal> _rsiSell;
private readonly StrategyParam<int> _cooldownBars;
private SuperTrend _st1;
private SuperTrend _st2;
private SuperTrend _st3;
private ExponentialMovingAverage _stSmooth;
private ExponentialMovingAverage _amaMid;
private RelativeStrengthIndex _rsi;
private decimal _prevClose;
private decimal _prevSmooth;
private bool _isFirst;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal Factor1 { get => _factor1.Value; set => _factor1.Value = value; }
public decimal Factor2 { get => _factor2.Value; set => _factor2.Value = value; }
public decimal Factor3 { get => _factor3.Value; set => _factor3.Value = value; }
public int SmoothLength { get => _smoothLength.Value; set => _smoothLength.Value = value; }
public int AmaLength { get => _amaLength.Value; set => _amaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public decimal RsiBuy { get => _rsiBuy.Value; set => _rsiBuy.Value = value; }
public decimal RsiSell { get => _rsiSell.Value; set => _rsiSell.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AdaptiveFibonacciPullbackStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_atrPeriod = Param(nameof(AtrPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("SuperTrend ATR Length", "ATR period for SuperTrend", "SuperTrend");
_factor1 = Param(nameof(Factor1), 0.618m)
.SetDisplay("Factor 1", "Weak Fibonacci factor", "SuperTrend");
_factor2 = Param(nameof(Factor2), 1.618m)
.SetDisplay("Factor 2", "Golden Ratio factor", "SuperTrend");
_factor3 = Param(nameof(Factor3), 2.618m)
.SetDisplay("Factor 3", "Extended Fibonacci factor", "SuperTrend");
_smoothLength = Param(nameof(SmoothLength), 21)
.SetGreaterThanZero()
.SetDisplay("Smoothing Length", "EMA length for SuperTrend average", "SuperTrend");
_amaLength = Param(nameof(AmaLength), 55)
.SetGreaterThanZero()
.SetDisplay("AMA Length", "Length for AMA midline", "AMA");
_rsiLength = Param(nameof(RsiLength), 7)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_rsiBuy = Param(nameof(RsiBuy), 50m)
.SetDisplay("RSI Buy Threshold", "RSI must be above for long", "RSI");
_rsiSell = Param(nameof(RsiSell), 50m)
.SetDisplay("RSI Sell Threshold", "RSI must be below for short", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevSmooth = default;
_isFirst = true;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_st1 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor1 };
_st2 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor2 };
_st3 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor3 };
_stSmooth = new ExponentialMovingAverage { Length = SmoothLength };
_amaMid = new ExponentialMovingAverage { Length = AmaLength };
_rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(new IIndicator[] { _st1, _st2, _st3, _amaMid, _rsi }, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (values[0].IsEmpty || values[1].IsEmpty || values[2].IsEmpty ||
values[3].IsEmpty || values[4].IsEmpty)
return;
var st1Val = values[0].ToDecimal();
var st2Val = values[1].ToDecimal();
var st3Val = values[2].ToDecimal();
var mid = values[3].ToDecimal();
var rsiVal = values[4].ToDecimal();
var avg = (st1Val + st2Val + st3Val) / 3m;
var smooth = _stSmooth.Process(new DecimalIndicatorValue(_stSmooth, avg, candle.ServerTime)).ToDecimal();
if (_isFirst)
{
_prevClose = candle.ClosePrice;
_prevSmooth = smooth;
_isFirst = false;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = candle.ClosePrice;
_prevSmooth = smooth;
return;
}
var baseLong = candle.LowPrice < avg && candle.ClosePrice > smooth && _prevClose > mid;
var baseShort = candle.HighPrice > avg && candle.ClosePrice < smooth && _prevClose < mid;
var longEntry = baseLong && candle.ClosePrice > mid && rsiVal > RsiBuy;
var shortEntry = baseShort && candle.ClosePrice < mid && rsiVal < RsiSell;
if (longEntry && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
else if (shortEntry && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit conditions
var longExit = _prevClose > _prevSmooth && candle.ClosePrice <= smooth && Position > 0;
var shortExit = _prevClose < _prevSmooth && candle.ClosePrice >= smooth && Position < 0;
if (longExit)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
else if (shortExit)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevClose = candle.ClosePrice;
_prevSmooth = smooth;
}
}
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, Array
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (SuperTrend, ExponentialMovingAverage,
RelativeStrengthIndex,
IndicatorHelper, IIndicator)
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class adaptive_fibonacci_pullback_strategy(Strategy):
"""Adaptive Fibonacci Pullback Strategy."""
def __init__(self):
super(adaptive_fibonacci_pullback_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._atr_period = self.Param("AtrPeriod", 8) \
.SetDisplay("SuperTrend ATR Length", "ATR period for SuperTrend", "SuperTrend")
self._factor1 = self.Param("Factor1", 0.618) \
.SetDisplay("Factor 1", "Weak Fibonacci factor", "SuperTrend")
self._factor2 = self.Param("Factor2", 1.618) \
.SetDisplay("Factor 2", "Golden Ratio factor", "SuperTrend")
self._factor3 = self.Param("Factor3", 2.618) \
.SetDisplay("Factor 3", "Extended Fibonacci factor", "SuperTrend")
self._smooth_length = self.Param("SmoothLength", 21) \
.SetDisplay("Smoothing Length", "EMA length for SuperTrend average", "SuperTrend")
self._ama_length = self.Param("AmaLength", 55) \
.SetDisplay("AMA Length", "Length for AMA midline", "AMA")
self._rsi_length = self.Param("RsiLength", 7) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._rsi_buy = self.Param("RsiBuy", 50.0) \
.SetDisplay("RSI Buy Threshold", "RSI must be above for long", "RSI")
self._rsi_sell = self.Param("RsiSell", 50.0) \
.SetDisplay("RSI Sell Threshold", "RSI must be below for short", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._st1 = None
self._st2 = None
self._st3 = None
self._st_smooth = None
self._ama_mid = None
self._rsi = None
self._prev_close = 0.0
self._prev_smooth = 0.0
self._is_first = True
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adaptive_fibonacci_pullback_strategy, self).OnReseted()
self._st1 = None
self._st2 = None
self._st3 = None
self._st_smooth = None
self._ama_mid = None
self._rsi = None
self._prev_close = 0.0
self._prev_smooth = 0.0
self._is_first = True
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(adaptive_fibonacci_pullback_strategy, self).OnStarted2(time)
self._st1 = SuperTrend()
self._st1.Length = int(self._atr_period.Value)
self._st1.Multiplier = self._factor1.Value
self._st2 = SuperTrend()
self._st2.Length = int(self._atr_period.Value)
self._st2.Multiplier = self._factor2.Value
self._st3 = SuperTrend()
self._st3.Length = int(self._atr_period.Value)
self._st3.Multiplier = self._factor3.Value
self._st_smooth = ExponentialMovingAverage()
self._st_smooth.Length = int(self._smooth_length.Value)
self._ama_mid = ExponentialMovingAverage()
self._ama_mid.Length = int(self._ama_length.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
indicators = Array[IIndicator]([self._st1, self._st2, self._st3, self._ama_mid, self._rsi])
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(indicators, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, values):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if values[0].IsEmpty or values[1].IsEmpty or values[2].IsEmpty or values[3].IsEmpty or values[4].IsEmpty:
return
st1_v = float(IndicatorHelper.ToDecimal(values[0]))
st2_v = float(IndicatorHelper.ToDecimal(values[1]))
st3_v = float(IndicatorHelper.ToDecimal(values[2]))
mid = float(IndicatorHelper.ToDecimal(values[3]))
rsi_v = float(IndicatorHelper.ToDecimal(values[4]))
avg = (st1_v + st2_v + st3_v) / 3.0
smooth_result = process_float(self._st_smooth, avg, candle.ServerTime, True)
smooth = float(IndicatorHelper.ToDecimal(smooth_result))
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
if self._is_first:
self._prev_close = close
self._prev_smooth = smooth
self._is_first = False
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_smooth = smooth
return
low = float(candle.LowPrice)
high = float(candle.HighPrice)
rsi_buy = float(self._rsi_buy.Value)
rsi_sell = float(self._rsi_sell.Value)
base_long = low < avg and close > smooth and self._prev_close > mid
base_short = high > avg and close < smooth and self._prev_close < mid
long_entry = base_long and close > mid and rsi_v > rsi_buy
short_entry = base_short and close < mid and rsi_v < rsi_sell
if long_entry and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif short_entry and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
# Exit conditions
long_exit = self._prev_close > self._prev_smooth and close <= smooth and self.Position > 0
short_exit = self._prev_close < self._prev_smooth and close >= smooth and self.Position < 0
if long_exit:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif short_exit:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_close = close
self._prev_smooth = smooth
def CreateClone(self):
return adaptive_fibonacci_pullback_strategy()