趋势线回调策略
市场往往会尊重通过先前波峰或波谷绘制的趋势线。本策略自动对近期价格动作拟合回归线,并在价格靠近这些趋势线并出现反弹且符合主趋势方向时入场。
测试表明年均收益约为 124%,该策略在外汇市场表现最佳。
系统存储近期K线,计算向上或向下倾斜的支撑和阻力线。当价格接近趋势线且蜡烛在移动平均线正确一侧确认反弹时,进入交易。止损按价格百分比设置,移动平均线被突破时离场。
通过只在主趋势方向交易并等待在支撑或阻力处的明确反应,该方法力求捕捉延续走势而不是追逐突破。
细节
- 入场条件:价格触及计算出的趋势线并在移动平均线上方/下方以趋势方向收盘。
- 多/空:双向。
- 退出条件:价格穿越移动平均线或止损。
- 止损:是,按百分比。
- 默认值:
TrendlinePeriod= 20MAPeriod= 20BounceThresholdPercent= 0.5CandleType= 5 分钟StopLossPercent= 2
- 过滤条件:
- 类别: 趋势跟随
- 方向: 双向
- 指标: 均线, 趋势线
- 止损: 有
- 复杂度: 高级
- 时间框架: 日内
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险级别: 中等
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>
/// Trendline Bounce strategy.
/// Calculates linear regression of recent lows (support) and highs (resistance).
/// Buys on bounce off support trendline, sells on bounce off resistance.
/// Uses SMA for exit signals.
/// </summary>
public class TrendlineBounceStrategy : Strategy
{
private readonly StrategyParam<int> _trendlinePeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private int _cooldown;
/// <summary>
/// Trendline period.
/// </summary>
public int TrendlinePeriod
{
get => _trendlinePeriod.Value;
set => _trendlinePeriod.Value = value;
}
/// <summary>
/// MA Period.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public TrendlineBounceStrategy()
{
_trendlinePeriod = Param(nameof(TrendlinePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Trendline Period", "Lookback for trendline", "Indicators");
_maPeriod = Param(nameof(MAPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for SMA", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highs.Clear();
_lows.Clear();
_cooldown = 0;
var sma = new SimpleMovingAverage { Length = MAPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
_highs.Add(candle.HighPrice);
_lows.Add(candle.LowPrice);
if (_highs.Count > TrendlinePeriod)
{
_highs.RemoveAt(0);
_lows.RemoveAt(0);
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_highs.Count < TrendlinePeriod)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Calculate linear regression for support (lows) and resistance (highs)
var supportLevel = GetLinRegValue(_lows);
var resistanceLevel = GetLinRegValue(_highs);
var buffer = (resistanceLevel - supportLevel) * 0.05m;
if (buffer <= 0)
return;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
// Bounce off support (buy)
if (Position == 0 && candle.LowPrice <= supportLevel + buffer && isBullish)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Bounce off resistance (sell)
else if (Position == 0 && candle.HighPrice >= resistanceLevel - buffer && isBearish)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit using SMA
else if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
private static decimal GetLinRegValue(List<decimal> values)
{
var n = values.Count;
if (n == 0) return 0;
decimal sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
for (int i = 0; i < n; i++)
{
sumX += i;
sumY += values[i];
sumXY += i * values[i];
sumX2 += i * i;
}
var denom = n * sumX2 - sumX * sumX;
if (denom == 0) return sumY / n;
var slope = (n * sumXY - sumX * sumY) / denom;
var intercept = (sumY - slope * sumX) / n;
return slope * (n - 1) + intercept;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class trendline_bounce_strategy(Strategy):
"""
Trendline Bounce strategy.
Calculates linear regression of recent lows (support) and highs (resistance).
Buys on bounce off support trendline, sells on bounce off resistance.
Uses SMA for exit signals.
"""
def __init__(self):
super(trendline_bounce_strategy, self).__init__()
self._trendline_period = self.Param("TrendlinePeriod", 20).SetDisplay("Trendline Period", "Lookback for trendline", "Indicators")
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for SMA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._highs = []
self._lows = []
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(trendline_bounce_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._cooldown = 0
def OnStarted2(self, time):
super(trendline_bounce_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _get_lin_reg_value(self, values):
n = len(values)
if n == 0:
return 0.0
sum_x = 0.0
sum_y = 0.0
sum_xy = 0.0
sum_x2 = 0.0
for i in range(n):
sum_x += i
sum_y += values[i]
sum_xy += i * values[i]
sum_x2 += i * i
denom = n * sum_x2 - sum_x * sum_x
if denom == 0:
return sum_y / n
slope = (n * sum_xy - sum_x * sum_y) / denom
intercept = (sum_y - slope * sum_x) / n
return slope * (n - 1) + intercept
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
self._highs.append(float(candle.HighPrice))
self._lows.append(float(candle.LowPrice))
tp = self._trendline_period.Value
if len(self._highs) > tp:
self._highs.pop(0)
self._lows.pop(0)
if len(self._highs) < tp:
return
if self._cooldown > 0:
self._cooldown -= 1
return
# Calculate linear regression for support (lows) and resistance (highs)
support_level = self._get_lin_reg_value(self._lows)
resistance_level = self._get_lin_reg_value(self._highs)
buffer = (resistance_level - support_level) * 0.05
if buffer <= 0:
return
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
close = float(candle.ClosePrice)
sv = float(sma_val)
cd = self._cooldown_bars.Value
# Bounce off support (buy)
if self.Position == 0 and float(candle.LowPrice) <= support_level + buffer and is_bullish:
self.BuyMarket()
self._cooldown = cd
# Bounce off resistance (sell)
elif self.Position == 0 and float(candle.HighPrice) >= resistance_level - buffer and is_bearish:
self.SellMarket()
self._cooldown = cd
# Exit using SMA
elif self.Position > 0 and close < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > sv:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return trendline_bounce_strategy()