枢轴点反转策略
日内枢轴点及其支撑阻力位常成为价格转折点。本策略根据前一日的高点、低点和收盘价计算经典枢轴点,并观察价格在 S1 或 R1 附近的反弹。
测试表明年均收益约为 127%,该策略在股票市场表现最佳。
当价格接近支撑位 S1 并出现看涨蜡烛时做多;当价格测试阻力位 R1 且出现看跌蜡烛时做空。达到中轴位或触及保护性止损时离场。
该方法在每天开盘时重新计算枢轴点,适合存在明确日内波动区间的交易时段。
细节
- 入场条件:在 S1 附近的看涨蜡烛或在 R1 附近的看跌蜡烛。
- 多/空:双向。
- 退出条件:价格穿越中轴位或止损。
- 止损:是,按百分比。
- 默认值:
CandleType= 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>
/// Pivot Point Reversal strategy.
/// Calculates pivot points from a rolling window of highs, lows, closes.
/// P = (H + L + C) / 3, S1 = 2*P - H, R1 = 2*P - L
/// Buys on bounce off S1, sells on bounce off R1, exits at pivot.
/// </summary>
public class PivotPointReversalStrategy : Strategy
{
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private readonly List<decimal> _closes = new();
private int _cooldown;
/// <summary>
/// Lookback period for pivot calculation.
/// </summary>
public int Lookback
{
get => _lookback.Value;
set => _lookback.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 PivotPointReversalStrategy()
{
_lookback = Param(nameof(Lookback), 60)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Lookback for pivot calc", "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();
_closes.Clear();
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highs.Clear();
_lows.Clear();
_closes.Clear();
_cooldown = 0;
var sma = new SimpleMovingAverage { Length = 20 };
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);
_closes.Add(candle.ClosePrice);
if (_highs.Count > Lookback)
{
_highs.RemoveAt(0);
_lows.RemoveAt(0);
_closes.RemoveAt(0);
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_highs.Count < Lookback)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Calculate pivot points from lookback window
decimal high = decimal.MinValue, low = decimal.MaxValue, close = 0;
for (int i = 0; i < _highs.Count; i++)
{
if (_highs[i] > high) high = _highs[i];
if (_lows[i] < low) low = _lows[i];
}
close = _closes[_closes.Count - 1];
var pivot = (high + low + close) / 3;
var r1 = 2 * pivot - low;
var s1 = 2 * pivot - high;
var buffer = (r1 - s1) * 0.02m;
if (buffer <= 0)
return;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
// Bounce off S1 (buy)
if (Position == 0 && candle.LowPrice <= s1 + buffer && isBullish)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Bounce off R1 (sell)
else if (Position == 0 && candle.HighPrice >= r1 - buffer && isBearish)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit at pivot
else if (Position > 0 && candle.ClosePrice > pivot)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice < pivot)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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 pivot_point_reversal_strategy(Strategy):
"""
Pivot Point Reversal strategy.
Calculates pivot points from a rolling window of highs, lows, closes.
P = (H + L + C) / 3, S1 = 2*P - H, R1 = 2*P - L
Buys on bounce off S1, sells on bounce off R1, exits at pivot.
"""
def __init__(self):
super(pivot_point_reversal_strategy, self).__init__()
self._lookback = self.Param("Lookback", 60).SetDisplay("Lookback", "Lookback for pivot calc", "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._closes = []
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(pivot_point_reversal_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._closes = []
self._cooldown = 0
def OnStarted2(self, time):
super(pivot_point_reversal_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._closes = []
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = 20
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 _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
self._highs.append(float(candle.HighPrice))
self._lows.append(float(candle.LowPrice))
self._closes.append(float(candle.ClosePrice))
lb = self._lookback.Value
if len(self._highs) > lb:
self._highs.pop(0)
self._lows.pop(0)
self._closes.pop(0)
if len(self._highs) < lb:
return
if self._cooldown > 0:
self._cooldown -= 1
return
# Calculate pivot points from lookback window
high = max(self._highs)
low = min(self._lows)
close = self._closes[-1]
pivot = (high + low + close) / 3.0
r1 = 2.0 * pivot - low
s1 = 2.0 * pivot - high
buffer = (r1 - s1) * 0.02
if buffer <= 0:
return
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
cd = self._cooldown_bars.Value
# Bounce off S1 (buy)
if self.Position == 0 and float(candle.LowPrice) <= s1 + buffer and is_bullish:
self.BuyMarket()
self._cooldown = cd
# Bounce off R1 (sell)
elif self.Position == 0 and float(candle.HighPrice) >= r1 - buffer and is_bearish:
self.SellMarket()
self._cooldown = cd
# Exit at pivot
elif self.Position > 0 and float(candle.ClosePrice) > pivot:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and float(candle.ClosePrice) < pivot:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return pivot_point_reversal_strategy()