自适应斐波那契回撤策略
该策略将三条使用斐波那契倍数(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
- 止损:是
- 复杂度:中等
- 时间框架:任意
- 季节性:无
- 神经网络:无
- 背离:无
- 风险级别:中等
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>
/// Adaptive Fibonacci Pullback strategy.
/// Three SuperTrend lines with Fibonacci multipliers are averaged and the average is smoothed by an EMA. A long needs the low to dip
/// below the average while the close stays above the smoothed line, the previous and current close above the Kaufman AMA midline and
/// RSI above RsiBuy; a short mirrors this with RSI below RsiSell. Positions close when the close crosses the smoothed line against them,
/// and percent take profit and stop loss protect every trade.
/// </summary>
public class AdaptiveFibonacciPullbackStrategy : Strategy
{
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<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _smooth;
private decimal? _prevClose;
private decimal? _prevSmooth;
/// <summary>
/// ATR period of the SuperTrends.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Multiplier of the first SuperTrend.
/// </summary>
public decimal Factor1
{
get => _factor1.Value;
set => _factor1.Value = value;
}
/// <summary>
/// Multiplier of the second SuperTrend.
/// </summary>
public decimal Factor2
{
get => _factor2.Value;
set => _factor2.Value = value;
}
/// <summary>
/// Multiplier of the third SuperTrend.
/// </summary>
public decimal Factor3
{
get => _factor3.Value;
set => _factor3.Value = value;
}
/// <summary>
/// EMA length that smooths the SuperTrend average.
/// </summary>
public int SmoothLength
{
get => _smoothLength.Value;
set => _smoothLength.Value = value;
}
/// <summary>
/// Period of the AMA midline.
/// </summary>
public int AmaLength
{
get => _amaLength.Value;
set => _amaLength.Value = value;
}
/// <summary>
/// Period of RSI.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI level a long requires to exceed.
/// </summary>
public decimal RsiBuy
{
get => _rsiBuy.Value;
set => _rsiBuy.Value = value;
}
/// <summary>
/// RSI level a short requires to stay below.
/// </summary>
public decimal RsiSell
{
get => _rsiSell.Value;
set => _rsiSell.Value = value;
}
/// <summary>
/// Take profit percentage.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AdaptiveFibonacciPullbackStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period of the SuperTrends", "SuperTrend");
_factor1 = Param(nameof(Factor1), 0.618m)
.SetGreaterThanZero()
.SetDisplay("Factor 1", "Multiplier of the first SuperTrend", "SuperTrend");
_factor2 = Param(nameof(Factor2), 1.618m)
.SetGreaterThanZero()
.SetDisplay("Factor 2", "Multiplier of the second SuperTrend", "SuperTrend");
_factor3 = Param(nameof(Factor3), 2.618m)
.SetGreaterThanZero()
.SetDisplay("Factor 3", "Multiplier of the third SuperTrend", "SuperTrend");
_smoothLength = Param(nameof(SmoothLength), 21)
.SetGreaterThanZero()
.SetDisplay("Smooth Length", "EMA length that smooths the SuperTrend average", "SuperTrend");
_amaLength = Param(nameof(AmaLength), 55)
.SetGreaterThanZero()
.SetDisplay("AMA Length", "Period of the AMA midline", "AMA");
_rsiLength = Param(nameof(RsiLength), 7)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "Period of RSI", "RSI");
_rsiBuy = Param(nameof(RsiBuy), 70m)
.SetDisplay("RSI Buy", "RSI level a long requires to exceed", "RSI");
_rsiSell = Param(nameof(RsiSell), 30m)
.SetDisplay("RSI Sell", "RSI level a short requires to stay below", "RSI");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 5m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 0.75m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_smooth = null;
_prevClose = null;
_prevSmooth = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
_prevSmooth = null;
var st1 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor1 };
var st2 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor2 };
var st3 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor3 };
var ama = new KaufmanAdaptiveMovingAverage { Length = AmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
_smooth = new ExponentialMovingAverage { Length = SmoothLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx([st1, st2, st3, ama, rsi], ProcessCandle)
.Start();
StartProtection(new Unit(TakeProfitPercent, UnitTypes.Percent), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ama);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished)
return;
foreach (var value in values)
{
if (!value.IsFormed)
return;
}
var average = (values[0].ToDecimal() + values[1].ToDecimal() + values[2].ToDecimal()) / 3m;
var mid = values[3].ToDecimal();
var rsi = values[4].ToDecimal();
var smoothValue = _smooth.Process(average, candle.ServerTime, true);
if (!_smooth.IsFormed)
return;
var smooth = smoothValue.ToDecimal();
var close = candle.ClosePrice;
var prevClose = _prevClose;
var prevSmooth = _prevSmooth;
_prevClose = close;
_prevSmooth = smooth;
if (prevClose is not decimal pc || prevSmooth is not decimal ps)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var longEntry = candle.LowPrice < average && close > smooth && pc > mid && close > mid && rsi > RsiBuy;
var shortEntry = candle.HighPrice > average && close < smooth && pc < mid && close < mid && rsi < RsiSell;
if (longEntry && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortEntry && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && pc >= ps && close < smooth)
SellMarket(Position);
else if (Position < 0 && pc <= ps && close > smooth)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal, Array
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import IIndicator, SuperTrend, ExponentialMovingAverage, KaufmanAdaptiveMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class adaptive_fibonacci_pullback_strategy(Strategy):
"""
Adaptive Fibonacci Pullback strategy.
Three SuperTrend lines with Fibonacci multipliers are averaged and the average is smoothed by an EMA. A long needs the low to dip
below the average while the close stays above the smoothed line, the previous and current close above the Kaufman AMA midline and
RSI above RsiBuy; a short mirrors this with RSI below RsiSell. Positions close when the close crosses the smoothed line against them,
and percent take profit and stop loss protect every trade.
"""
def __init__(self):
super(adaptive_fibonacci_pullback_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 8).SetGreaterThanZero().SetDisplay("ATR Period", "ATR period of the SuperTrends", "SuperTrend")
self._factor1 = self.Param("Factor1", 0.618).SetGreaterThanZero().SetDisplay("Factor 1", "Multiplier of the first SuperTrend", "SuperTrend")
self._factor2 = self.Param("Factor2", 1.618).SetGreaterThanZero().SetDisplay("Factor 2", "Multiplier of the second SuperTrend", "SuperTrend")
self._factor3 = self.Param("Factor3", 2.618).SetGreaterThanZero().SetDisplay("Factor 3", "Multiplier of the third SuperTrend", "SuperTrend")
self._smooth_length = self.Param("SmoothLength", 21).SetGreaterThanZero().SetDisplay("Smooth Length", "EMA length that smooths the SuperTrend average", "SuperTrend")
self._ama_length = self.Param("AmaLength", 55).SetGreaterThanZero().SetDisplay("AMA Length", "Period of the AMA midline", "AMA")
self._rsi_length = self.Param("RsiLength", 7).SetGreaterThanZero().SetDisplay("RSI Length", "Period of RSI", "RSI")
self._rsi_buy = self.Param("RsiBuy", 70.0).SetDisplay("RSI Buy", "RSI level a long requires to exceed", "RSI")
self._rsi_sell = self.Param("RsiSell", 30.0).SetDisplay("RSI Sell", "RSI level a short requires to stay below", "RSI")
self._take_profit_percent = self.Param("TakeProfitPercent", 5.0).SetNotNegative().SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 0.75).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._smooth = None
self._prev_close = None
self._prev_smooth = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adaptive_fibonacci_pullback_strategy, self).OnReseted()
self._smooth = None
self._prev_close = None
self._prev_smooth = None
def OnStarted2(self, time):
super(adaptive_fibonacci_pullback_strategy, self).OnStarted2(time)
self._prev_close = None
self._prev_smooth = None
st1 = SuperTrend()
st1.Length = self._atr_period.Value
st1.Multiplier = Decimal(self._factor1.Value)
st2 = SuperTrend()
st2.Length = self._atr_period.Value
st2.Multiplier = Decimal(self._factor2.Value)
st3 = SuperTrend()
st3.Length = self._atr_period.Value
st3.Multiplier = Decimal(self._factor3.Value)
ama = KaufmanAdaptiveMovingAverage()
ama.Length = self._ama_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
self._smooth = ExponentialMovingAverage()
self._smooth.Length = self._smooth_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(Array[IIndicator]([st1, st2, st3, ama, rsi]), self._process_candle).Start()
self.StartProtection(Unit(Decimal(self._take_profit_percent.Value), UnitTypes.Percent), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ama)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _process_candle(self, candle, values):
if candle.State != CandleStates.Finished:
return
for value in values:
if not value.IsFormed:
return
average = (values[0].GetValue[Decimal](None) + values[1].GetValue[Decimal](None) + values[2].GetValue[Decimal](None)) / Decimal(3)
mid = values[3].GetValue[Decimal](None)
rsi = values[4].GetValue[Decimal](None)
smooth_value = process_value(self._smooth, average, candle.ServerTime, True)
if not self._smooth.IsFormed:
return
smooth = smooth_value.GetValue[Decimal](None)
close = candle.ClosePrice
prev_close = self._prev_close
prev_smooth = self._prev_smooth
self._prev_close = close
self._prev_smooth = smooth
if prev_close is None or prev_smooth is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
long_entry = candle.LowPrice < average and close > smooth and prev_close > mid and close > mid and rsi > Decimal(self._rsi_buy.Value)
short_entry = candle.HighPrice > average and close < smooth and prev_close < mid and close < mid and rsi < Decimal(self._rsi_sell.Value)
if long_entry and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_entry and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and prev_close >= prev_smooth and close < smooth:
self.SellMarket(self.Position)
elif self.Position < 0 and prev_close <= prev_smooth and close > smooth:
self.BuyMarket(-self.Position)
def CreateClone(self):
return adaptive_fibonacci_pullback_strategy()