Beginner Breakout 策略
利用最近 Period 根K线的最高价和最低价构建通道。当收盘价接近上边界时做多,接近下边界时做空。
入场规则
- 做多:Close >= high − (high − low) *
ShiftPercent/ 100 且当前趋势不是向上。 - 做空:Close <= low + (high − low) *
ShiftPercent/ 100 且当前趋势不是向下。
出场规则
- 相反信号会平掉当前仓位并在相反方向开仓。
参数
Period– 计算通道时回溯的K线数量。ShiftPercent– 相对通道边界的百分比偏移。CandleType– 使用的K线周期。Volume– 交易量。StopLoss– 以价格单位表示的止损。TakeProfit– 以价格单位表示的止盈。
指标
- Highest
- Lowest
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on Beginner indicator breakout.
/// Opens a long position when price closes near the recent high.
/// Opens a short position when price closes near the recent low.
/// </summary>
public class BeginnerBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<decimal> _shiftPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private Highest _highest = null!;
private Lowest _lowest = null!;
private TrendDirections _trend = TrendDirections.None;
/// <summary>
/// Lookback period for highest/highest calculation.
/// </summary>
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Percentage shift from the channel borders.
/// </summary>
public decimal ShiftPercent
{
get => _shiftPercent.Value;
set => _shiftPercent.Value = value;
}
/// <summary>
/// Candle type to work on.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop loss in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="BeginnerBreakoutStrategy"/> class.
/// </summary>
public BeginnerBreakoutStrategy()
{
_period = Param(nameof(Period), 9)
.SetDisplay("Period", "Lookback period for highs/lows", "General")
.SetGreaterThanZero()
;
_shiftPercent = Param(nameof(ShiftPercent), 30m)
.SetDisplay("Shift %", "Percentage shift from channel", "General")
.SetGreaterThanZero()
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
_stopLoss = Param(nameof(StopLoss), 2m)
.SetDisplay("Stop Loss", "Stop loss in percent", "Risk")
.SetGreaterThanZero();
_takeProfit = Param(nameof(TakeProfit), 4m)
.SetDisplay("Take Profit", "Take profit in percent", "Risk")
.SetGreaterThanZero();
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_trend = TrendDirections.None;
_highest = null!;
_lowest = null!;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highest = new Highest { Length = Period };
_lowest = new Lowest { Length = Period };
StartProtection(new Unit(TakeProfit, UnitTypes.Percent), new Unit(StopLoss, UnitTypes.Percent));
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_highest, _lowest, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _highest);
DrawIndicator(area, _lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highValue, decimal lowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_highest.IsFormed || !_lowest.IsFormed)
return;
var range = (highValue - lowValue) * ShiftPercent / 100m;
var close = candle.ClosePrice;
if (_trend != TrendDirections.Down && close <= lowValue + range)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
{
SellMarket();
_trend = TrendDirections.Down;
}
}
else if (_trend != TrendDirections.Up && close >= highValue - range)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
{
BuyMarket();
_trend = TrendDirections.Up;
}
}
}
private enum TrendDirections
{
None,
Up,
Down
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class beginner_breakout_strategy(Strategy):
# TrendDirections: 0=None, 1=Up, 2=Down
def __init__(self):
super(beginner_breakout_strategy, self).__init__()
self._period = self.Param("Period", 9) \
.SetDisplay("Period", "Lookback period for highs/lows", "General")
self._shift_percent = self.Param("ShiftPercent", 30.0) \
.SetDisplay("Shift %", "Percentage shift from channel", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
self._stop_loss = self.Param("StopLoss", 2.0) \
.SetDisplay("Stop Loss", "Stop loss in percent", "Risk")
self._take_profit = self.Param("TakeProfit", 4.0) \
.SetDisplay("Take Profit", "Take profit in percent", "Risk")
self._trend = 0 # 0=None, 1=Up, 2=Down
@property
def period(self):
return self._period.Value
@property
def shift_percent(self):
return self._shift_percent.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def stop_loss(self):
return self._stop_loss.Value
@property
def take_profit(self):
return self._take_profit.Value
def OnReseted(self):
super(beginner_breakout_strategy, self).OnReseted()
self._trend = 0
def OnStarted2(self, time):
super(beginner_breakout_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self.period
lowest = Lowest()
lowest.Length = self.period
self.StartProtection(
takeProfit=Unit(float(self.take_profit), UnitTypes.Percent),
stopLoss=Unit(float(self.stop_loss), UnitTypes.Percent))
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def process_candle(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
high_value = float(high_value)
low_value = float(low_value)
shift = float(self.shift_percent)
rng = (high_value - low_value) * shift / 100.0
close = float(candle.ClosePrice)
if self._trend != 2 and close <= low_value + rng:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._trend = 2
elif self._trend != 1 and close >= high_value - rng:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._trend = 1
def CreateClone(self):
return beginner_breakout_strategy()