Hpcs Inter7 策略
概述
Hpcs Inter7 策略是从 MetaTrader 4 专家顾问 _HPCS_Inter7_MT4_EA_V01_We.mq4 转换而来的布林带突破系统。算法在所选 K 线序列上计算标准布林带,当收盘价向带外突破时,会顺势开仓。每次开仓后都会立即设置固定距离的止损与止盈,以复现原始 EA 的风险控制方式。
交易逻辑
- 做空入场:若前一根 K 线收盘价位于下轨之上,而最新完成的 K 线收盘价跌破下轨,则以市价卖出,对应原始条件
Close[0] < LowerBand[0] && Close[1] > LowerBand[1]。 - 做多入场:若前一根 K 线收盘价位于上轨之下,而最新完成的 K 线收盘价突破上轨,则以市价买入,复现条件
Close[0] > UpperBand[0] && Close[1] < UpperBand[1]。 - 每根 K 线仅一笔交易:策略记录触发信号的 K 线开盘时间,如果同一根 K 线再次出现信号将被忽略,对应 MQL4 中的
gdt_Candle变量。 - 保护性订单:在开仓后立即调用
SetStopLoss与SetTakeProfit,按照配置的距离在入场价上下对称放置止损止盈,确保持仓始终具有预定义的风险与收益。
参数
| 名称 | 说明 | 默认值 | 可优化 |
|---|---|---|---|
BollingerLength |
参与布林带计算的 K 线数量。 | 20 | 是 |
BollingerDeviation |
布林带宽度所使用的标准差倍数。 | 2 | 是 |
CandleType |
用于计算的 K 线类型(默认 1 分钟)。 | 1 分钟 K 线 | 否 |
ProtectionDistancePoints |
以价格步长为单位的止损与止盈距离。 | 10 | 是 |
其他说明
- 策略使用 StockSharp 高阶 API(
SubscribeCandles().Bind(...)),无需维护自定义历史数组。 - 启动时调用
StartProtection(),平台会自动管理由SetStopLoss与SetTakeProfit创建的保护单。 - 下单量由基类
Strategy.Volume控制,与原始 EA 固定 1 手的做法一致。 - 该策略源自外汇市场,但只要标的提供有效的布林带信号并具有合理的
PriceStep,同样可以应用于其他品种。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy converted from _HPCS_Inter7_MT4_EA_V01_We.mq4.
/// Sells when price crosses below the lower Bollinger band and buys when price crosses above the upper band.
/// </summary>
public class HpcsInter7Strategy : Strategy
{
private readonly StrategyParam<int> _bollingerLength;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _bandPercent;
private decimal? _prevClose;
private decimal? _prevLower;
private decimal? _prevUpper;
/// <summary>
/// Initializes a new instance of the <see cref="HpcsInter7Strategy"/> class.
/// </summary>
public HpcsInter7Strategy()
{
_bollingerLength = Param(nameof(BollingerLength), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Length", "Number of candles included in the Bollinger Bands calculation", "Indicators");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Deviation", "Standard deviation multiplier for the Bollinger Bands", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Time frame used for Bollinger Bands", "General");
_bandPercent = Param(nameof(BandPercent), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Band Percent", "MA percentage band width", "Indicators");
}
/// <summary>
/// Bollinger Bands length.
/// </summary>
public int BollingerLength
{
get => _bollingerLength.Value;
set => _bollingerLength.Value = value;
}
/// <summary>
/// Bollinger Bands deviation multiplier.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal BandPercent
{
get => _bandPercent.Value;
set => _bandPercent.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = null;
_prevLower = null;
_prevUpper = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
_prevLower = null;
_prevUpper = null;
var bollinger = new ExponentialMovingAverage { Length = BollingerLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal middle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var upper = middle * (1 + BandPercent);
var lower = middle * (1 - BandPercent);
if (_prevClose.HasValue && _prevLower.HasValue && _prevUpper.HasValue)
{
// Downward cross through the lower band - open short
if (_prevClose.Value > _prevLower.Value && candle.ClosePrice < lower && Position >= 0)
{
SellMarket();
}
// Upward cross through the upper band - open long
else if (_prevClose.Value < _prevUpper.Value && candle.ClosePrice > upper && Position <= 0)
{
BuyMarket();
}
}
_prevClose = candle.ClosePrice;
_prevLower = lower;
_prevUpper = upper;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class hpcs_inter7_strategy(Strategy):
def __init__(self):
super(hpcs_inter7_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._bollinger_length = self.Param("BollingerLength", 20)
self._band_percent = self.Param("BandPercent", 0.01)
self._prev_close = 0.0
self._prev_lower = 0.0
self._prev_upper = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BollingerLength(self):
return self._bollinger_length.Value
@BollingerLength.setter
def BollingerLength(self, value):
self._bollinger_length.Value = value
@property
def BandPercent(self):
return self._band_percent.Value
@BandPercent.setter
def BandPercent(self, value):
self._band_percent.Value = value
def OnReseted(self):
super(hpcs_inter7_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_lower = 0.0
self._prev_upper = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(hpcs_inter7_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_lower = 0.0
self._prev_upper = 0.0
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.BollingerLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self._process_candle).Start()
def _process_candle(self, candle, middle_value):
if candle.State != CandleStates.Finished:
return
middle = float(middle_value)
band_pct = float(self.BandPercent)
upper = middle * (1.0 + band_pct)
lower = middle * (1.0 - band_pct)
close = float(candle.ClosePrice)
if self._has_prev:
# Downward cross through lower band - short
if self._prev_close > self._prev_lower and close < lower and self.Position >= 0:
self.SellMarket()
# Upward cross through upper band - long
elif self._prev_close < self._prev_upper and close > upper and self.Position <= 0:
self.BuyMarket()
self._prev_close = close
self._prev_lower = lower
self._prev_upper = upper
self._has_prev = True
def CreateClone(self):
return hpcs_inter7_strategy()