突破柱趋势策略
该策略利用 Parabolic SAR 指标识别趋势反转。在进入交易前,它会等待若干次失败的反转信号,然后顺着新的趋势方向开仓。止损和止盈距离可按点数或按入场价的百分比计算。
参数
- Reversal Mode – 距离计算方式:点数或百分比。
- Delta – 两次反转之间的最小价格变动。
- Negative Signals – 开仓前需要出现的失败反转次数。
- Stop Loss – 入场价到止损位的距离。
- Take Profit – 入场价到止盈位的距离。
- Candle Type – 用于计算的K线类型。
逻辑
- 订阅K线并计算 Parabolic SAR。
- 当指标方向反转且价格移动至少 Delta 时,记录该价格。
- 统计价格逆向移动的失败反转次数。
- 当计数达到 Negative Signals 时,按新趋势方向开仓。
- 每根K线根据 Reversal Mode 检查止损和止盈。
- 当趋势反向或触发风险限制时平仓。
该策略适用于趋势突破交易,可通过调整 delta、止损和止盈距离进行优化。
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>
/// Trend-following breakout strategy based on Parabolic SAR reversals.
/// Opens trades after negative reversals and uses percentage-based SL/TP.
/// </summary>
public class BreakoutBarsTrendStrategy : Strategy
{
private readonly StrategyParam<int> _negatives;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private ParabolicSar _parabolic;
private int _lastTrend; // -1, 0, 1
private int _negativeCounter;
public int Negatives
{
get => _negatives.Value;
set => _negatives.Value = value;
}
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public BreakoutBarsTrendStrategy()
{
_negatives = Param(nameof(Negatives), 1)
.SetNotNegative()
.SetDisplay("Negative Signals", "Negative reversals before entry", "General");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 4m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_parabolic = default;
_lastTrend = 0;
_negativeCounter = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_parabolic = new ParabolicSar();
Indicators.Add(_parabolic);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var sarResult = _parabolic.Process(candle);
if (!sarResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var sarValue = sarResult.ToDecimal();
var trend = sarValue < candle.ClosePrice ? 1 : -1;
if (_lastTrend != 0 && _lastTrend != trend)
{
// Reversal detected
if (trend == 1 && Position < 0)
BuyMarket();
else if (trend == -1 && Position > 0)
SellMarket();
_negativeCounter++;
if (_negativeCounter > Negatives)
{
if (trend == 1 && Position <= 0)
{
BuyMarket();
}
else if (trend == -1 && Position >= 0)
{
SellMarket();
}
_negativeCounter = 0;
}
}
_lastTrend = trend;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ParabolicSar, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class breakout_bars_trend_strategy(Strategy):
def __init__(self):
super(breakout_bars_trend_strategy, self).__init__()
self._negatives = self.Param("Negatives", 1) \
.SetDisplay("Negative Signals", "Negative reversals before entry", "General")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 4.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._parabolic = None
self._last_trend = 0
self._negative_counter = 0
@property
def negatives(self):
return self._negatives.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(breakout_bars_trend_strategy, self).OnReseted()
self._parabolic = None
self._last_trend = 0
self._negative_counter = 0
def OnStarted2(self, time):
super(breakout_bars_trend_strategy, self).OnStarted2(time)
self._parabolic = ParabolicSar()
self.Indicators.Add(self._parabolic)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
cv = CandleIndicatorValue(self._parabolic, candle)
sar_result = self._parabolic.Process(cv)
if not sar_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
sar_value = float(sar_result)
trend = 1 if sar_value < float(candle.ClosePrice) else -1
if self._last_trend != 0 and self._last_trend != trend:
if trend == 1 and self.Position < 0:
self.BuyMarket()
elif trend == -1 and self.Position > 0:
self.SellMarket()
self._negative_counter += 1
if self._negative_counter > int(self.negatives):
if trend == 1 and self.Position <= 0:
self.BuyMarket()
elif trend == -1 and self.Position >= 0:
self.SellMarket()
self._negative_counter = 0
self._last_trend = trend
def CreateClone(self):
return breakout_bars_trend_strategy()