在 GitHub 上查看
Previous Candle Breakdown 策略
Previous Candle Breakdown 策略监控用户设置的高级别K线(默认4小时)的上一根已收盘K线。当当前K线突破该参考K线的高点或低点,并超过指定的点差偏移量时,就会开仓。策略可选用两条移动均线做趋势过滤,并在进场后使用固定止损、止盈以及按点数移动的跟踪止损来控制风险。
主要特性
- 以上一根完成的参考K线的高低点作为突破基准,所有信号均围绕该高低点生成。
- 支持四种移动均线类型(SMA、EMA、平滑MA、加权MA),可分别设置快、慢线的周期与位移。只要两条均线的周期都大于0,过滤条件就要求快线在多头时高于慢线、空头时低于慢线。
- 将所有以“点”(pips)表示的距离(偏移、止损、止盈、跟踪止损和跟踪步长)转换为价格单位。对于小数位为3或5的品种,一个点等于10个最小价格步长,与原始MQL实现一致。
- 支持固定手数或按照账户权益的百分比(结合止损距离)动态计算下单数量。
- 限制每个方向的最大持仓次数,并可在浮动收益达到指定金额时强制平掉所有仓位。
- 跟踪止损逻辑完全复刻MQL5专家顾问:价格必须先超过
TrailingStop + TrailingStep 的距离,止损才会按离散步长向盈利方向推进。
参数
| 参数 |
说明 |
CandleType |
参考K线的时间周期(默认4小时)。 |
IndentPips |
触发突破前在参考高/低点上加减的点差。 |
FastPeriod / SlowPeriod |
快慢移动平均线周期。任意一个为0时关闭趋势过滤。 |
FastShift / SlowShift |
各自移动平均线的水平位移(以K线数量计)。 |
MaType |
移动平均线类型(Simple、Exponential、Smoothed、Weighted)。 |
StopLossPips |
初始止损的点数距离(0表示不使用)。 |
TakeProfitPips |
止盈的点数距离(0表示不使用)。 |
TrailingStopPips |
跟踪止损的点数距离。需要 TrailingStepPips 大于0。 |
TrailingStepPips |
每次移动跟踪止损所需的最小价格改善幅度。 |
OrderVolume |
固定下单量;若为0,则使用风险百分比计算。 |
RiskPercent |
当 OrderVolume = 0 时,每笔交易占账户权益的风险百分比(需设定非零止损)。 |
MaxPositions |
每个方向允许的最大建仓次数。 |
ProfitClose |
当浮动利润达到该金额(账户货币)时平掉所有仓位。 |
交易流程
- 记录
CandleType 对应的上一根已收盘K线的最高价和最低价。
- 在当前K线更新时:
- 若启用了移动均线过滤且历史数据不足,则等待;否则判断快慢均线的多空关系。
- 计算突破触发价:上一根高点 + 偏移、上一根低点 − 偏移。
- 当前K线最高价突破上方触发价时,尝试做多(同时检查过滤条件、最大持仓限制以及同一根K线的重复进场锁定)。
- 当前K线最低价跌破下方触发价时,尝试做空,逻辑相同。
- 建仓后会在内存中保存止损和止盈价格(若启用)。当价格触及对应水平时,通过市价单平仓。
- 跟踪止损在价格向盈利方向移动超过
TrailingStop + TrailingStep 后启动,并且每次至少要再推进一个 TrailingStepPips 才会再次调整。
- 根据平均持仓价实时计算浮动盈亏,达到
ProfitClose 时立即清空所有仓位。
- 风险百分比头寸管理依赖于证券的
PriceStep 和 StepPrice 元数据,同时也会遵守 MaxPositions 限制。
注意事项
- 将
TrailingStopPips 设为0可关闭跟踪止损。若开启跟踪功能,务必同时设置正值的 TrailingStepPips。
- 策略会保存最新的进场K线时间,防止在同一根参考K线内重复开仓,与原始EA的行为一致。
- 若品种缺少
PriceStep 或 StepPrice 信息,则无法按风险计算下单量,此时需指定 OrderVolume 才能交易。
- 代码中的注释全部使用英文,以符合仓库规范。
文件
CS/PreviousCandleBreakdownStrategy.cs – C# 实现文件。
根据要求,本策略不提供 Python 版本。
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>
/// Previous candle breakdown strategy (simplified).
/// Enters when price breaks above/below the previous candle's high/low.
/// </summary>
public class PreviousCandleBreakdownStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public PreviousCandleBreakdownStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
decimal prevHigh = 0, prevLow = 0;
bool hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind((ICandleMessage candle) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevHigh = candle.HighPrice;
prevLow = candle.LowPrice;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevHigh = candle.HighPrice;
prevLow = candle.LowPrice;
return;
}
// Breakout above previous high
if (candle.ClosePrice > prevHigh && Position <= 0)
{
BuyMarket();
}
// Breakdown below previous low
else if (candle.ClosePrice < prevLow && Position >= 0)
{
SellMarket();
}
prevHigh = candle.HighPrice;
prevLow = candle.LowPrice;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
}
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.Strategies import Strategy
class previous_candle_breakdown_strategy(Strategy):
def __init__(self):
super(previous_candle_breakdown_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
def OnReseted(self):
super(previous_candle_breakdown_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(previous_candle_breakdown_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
if not self._has_prev:
self._prev_high = h
self._prev_low = l
self._has_prev = True
return
if c > self._prev_high and self.Position <= 0:
self.BuyMarket()
elif c < self._prev_low and self.Position >= 0:
self.SellMarket()
self._prev_high = h
self._prev_low = l
def CreateClone(self):
return previous_candle_breakdown_strategy()