内包线突破策略
当一根蜡烛的最高点和最低点完全落在前一根蜡烛的范围内时,称为内包线,表示短期犹豫,一旦价格突破该范围,可能产生强劲的行情。本策略等待突破后顺势交易。
测试表明年均收益约为 118%,该策略在股票市场表现最佳。
每根新蜡烛都会与前一根比较。如果出现内包线,系统记录其高低点,并观察收盘价是否突破。若向上突破,则在高点上方收盘后做多并将止损放在形态低点下方;若向下突破,则做空并将止损置于形态高点上方。
若价格未能立即突破,策略在下根蜡烛逆向穿越前一根极值时退出现有仓位。
细节
- 入场条件:突破内包线的高点或低点。
- 多/空:双向。
- 退出条件:价格穿越前一根蜡烛极值或止损。
- 止损:是,放在形态外侧。
- 默认值:
CandleType= 5 分钟StopLossPercent= 1
- 过滤条件:
- 类别: 突破
- 方向: 双向
- 指标: K线形态
- 止损: 有
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险级别: 中等
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>
/// Inside Bar Breakout strategy.
/// Detects inside bar patterns (high lower than previous high, low higher than previous low).
/// Enters on breakout of the inside bar's high (buy) or low (sell).
/// Uses SMA for exit signals.
/// </summary>
public class InsideBarBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private ICandleMessage _prevCandle;
private ICandleMessage _insideBar;
private bool _waitingForBreakout;
private int _cooldown;
/// <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 InsideBarBreakoutStrategy()
{
_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();
_prevCandle = null;
_insideBar = null;
_waitingForBreakout = false;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
_insideBar = null;
_waitingForBreakout = false;
_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;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
_prevCandle = candle;
_waitingForBreakout = false;
return;
}
if (_prevCandle == null)
{
_prevCandle = candle;
return;
}
// Check for breakout of a previously detected inside bar
if (_waitingForBreakout && _insideBar != null && Position == 0)
{
if (candle.HighPrice > _insideBar.HighPrice)
{
BuyMarket();
_cooldown = CooldownBars;
_waitingForBreakout = false;
}
else if (candle.LowPrice < _insideBar.LowPrice)
{
SellMarket();
_cooldown = CooldownBars;
_waitingForBreakout = false;
}
}
// Check if current candle is an inside bar
if (candle.HighPrice < _prevCandle.HighPrice && candle.LowPrice > _prevCandle.LowPrice)
{
_insideBar = candle;
_waitingForBreakout = true;
}
// Exit logic using SMA
if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevCandle = candle;
}
}
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 inside_bar_breakout_strategy(Strategy):
"""
Inside Bar Breakout strategy.
Detects inside bar patterns (high lower than previous high, low higher than previous low).
Enters on breakout of the inside bar's high (buy) or low (sell).
Uses SMA for exit signals.
"""
def __init__(self):
super(inside_bar_breakout_strategy, self).__init__()
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._prev_candle = None
self._inside_bar = None
self._waiting_for_breakout = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(inside_bar_breakout_strategy, self).OnReseted()
self._prev_candle = None
self._inside_bar = None
self._waiting_for_breakout = False
self._cooldown = 0
def OnStarted2(self, time):
super(inside_bar_breakout_strategy, self).OnStarted2(time)
self._prev_candle = None
self._inside_bar = None
self._waiting_for_breakout = False
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 _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_candle = candle
self._waiting_for_breakout = False
return
if self._prev_candle is None:
self._prev_candle = candle
return
cd = self._cooldown_bars.Value
# Check for breakout of a previously detected inside bar
if self._waiting_for_breakout and self._inside_bar is not None and self.Position == 0:
if candle.HighPrice > self._inside_bar.HighPrice:
self.BuyMarket()
self._cooldown = cd
self._waiting_for_breakout = False
elif candle.LowPrice < self._inside_bar.LowPrice:
self.SellMarket()
self._cooldown = cd
self._waiting_for_breakout = False
# Check if current candle is an inside bar
if candle.HighPrice < self._prev_candle.HighPrice and candle.LowPrice > self._prev_candle.LowPrice:
self._inside_bar = candle
self._waiting_for_breakout = True
# Exit logic using SMA
sv = float(sma_val)
close = float(candle.ClosePrice)
if self.Position > 0 and close < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > sv:
self.BuyMarket()
self._cooldown = cd
self._prev_candle = candle
def CreateClone(self):
return inside_bar_breakout_strategy()