枢轴点反转策略
日内枢轴点及其支撑阻力位常成为价格转折点。本策略根据前一日的高点、低点和收盘价计算经典枢轴点,并观察价格在 S1 或 R1 附近的反弹。
测试表明年均收益约为 127%,该策略在股票市场表现最佳。
当价格接近支撑位 S1 并出现看涨蜡烛时做多;当价格测试阻力位 R1 且出现看跌蜡烛时做空。达到中轴位或触及保护性止损时离场。
该方法在每天开盘时重新计算枢轴点,适合存在明确日内波动区间的交易时段。
细节
- 入场条件:在 S1 附近的看涨蜡烛或在 R1 附近的看跌蜡烛。
- 多/空:双向。
- 退出条件:价格穿越中轴位或止损。
- 止损:是,按百分比。
- 默认值:
CandleType= 5 分钟StopLossPercent= 2 每个UTC日的枢轴点由前一日的最高价、最低价和收盘价计算。做多需要一根触及S1并收于其上方的阳线,做空需要一根触及R1并收于其下方的阴线;止损为入场价的百分比。
- 过滤条件:
- 类别: 均值回归
- 方向: 双向
- 指标: 枢轴点
- 止损: 有
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 是
- 神经网络: 无
- 背离: 无
- 风险级别: 中等
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.
/// Each day the classic floor pivots come from the previous day's high, low and close: P = (H + L + C) / 3, R1 = 2P - L, S1 = 2P - H.
/// While flat, a bullish candle that dips to S1 and closes above it buys, and a bearish candle that reaches R1 and closes below it sells.
/// The position closes when the close reaches the central pivot or at the percent stop.
/// </summary>
public class PivotPointReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _day;
private decimal _dayHigh;
private decimal _dayLow;
private decimal _dayClose;
private (decimal Pivot, decimal R1, decimal S1)? _levels;
/// <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 PivotPointReversalStrategy()
{
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_day = null;
_dayHigh = default;
_dayLow = default;
_dayClose = default;
_levels = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_day = null;
_levels = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
if (_day != day)
{
// A new day takes its pivots from the day that has just ended.
if (_day != null)
{
var pivot = (_dayHigh + _dayLow + _dayClose) / 3m;
_levels = (pivot, 2 * pivot - _dayLow, 2 * pivot - _dayHigh);
}
_day = day;
_dayHigh = candle.HighPrice;
_dayLow = candle.LowPrice;
}
else
{
_dayHigh = Math.Max(_dayHigh, candle.HighPrice);
_dayLow = Math.Min(_dayLow, candle.LowPrice);
}
_dayClose = candle.ClosePrice;
if (_levels is not { } levels || !IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= levels.Pivot)
SellMarket(Position);
}
else if (Position < 0)
{
if (close <= levels.Pivot)
BuyMarket(-Position);
}
else if (close > candle.OpenPrice && candle.LowPrice <= levels.S1 && close > levels.S1)
{
BuyMarket(Volume);
}
else if (close < candle.OpenPrice && candle.HighPrice >= levels.R1 && close < levels.R1)
{
SellMarket(Volume);
}
}
}
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, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class pivot_point_reversal_strategy(Strategy):
"""
Pivot Point Reversal strategy.
Each day the classic floor pivots come from the previous day's high, low and close: P = (H + L + C) / 3, R1 = 2P - L, S1 = 2P - H.
While flat, a bullish candle that dips to S1 and closes above it buys, and a bearish candle that reaches R1 and closes below it sells.
The position closes when the close reaches the central pivot or at the percent stop.
"""
def __init__(self):
super(pivot_point_reversal_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._day = None
self._day_high = Decimal(0)
self._day_low = Decimal(0)
self._day_close = Decimal(0)
self._levels = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(pivot_point_reversal_strategy, self).OnReseted()
self._day = None
self._day_high = Decimal(0)
self._day_low = Decimal(0)
self._day_close = Decimal(0)
self._levels = None
def OnStarted2(self, time):
super(pivot_point_reversal_strategy, self).OnStarted2(time)
self._day = None
self._levels = None
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
if self._day is None or self._day != day:
# A new day takes its pivots from the day that has just ended.
if self._day is not None:
pivot = (self._day_high + self._day_low + self._day_close) / Decimal(3)
self._levels = (pivot, Decimal(2) * pivot - self._day_low, Decimal(2) * pivot - self._day_high)
self._day = day
self._day_high = candle.HighPrice
self._day_low = candle.LowPrice
else:
self._day_high = Math.Max(self._day_high, candle.HighPrice)
self._day_low = Math.Min(self._day_low, candle.LowPrice)
self._day_close = candle.ClosePrice
if self._levels is None or not self.IsFormedAndOnlineAndAllowTrading():
return
pivot, r1, s1 = self._levels
close = candle.ClosePrice
if self.Position > 0:
if close >= pivot:
self.SellMarket(self.Position)
elif self.Position < 0:
if close <= pivot:
self.BuyMarket(-self.Position)
elif close > candle.OpenPrice and candle.LowPrice <= s1 and close > s1:
self.BuyMarket(self.Volume)
elif close < candle.OpenPrice and candle.HighPrice >= r1 and close < r1:
self.SellMarket(self.Volume)
def CreateClone(self):
return pivot_point_reversal_strategy()