在 GitHub 上查看
Histo Scalper 策略
概述
Histo Scalper Strategy 是将 HistoScalperEA v1.0 移植到 StockSharp 的版本。策略同时结合 ADX、ATR、布林带、Bulls/Bears Power、CCI、MACD、RSI 与随机指标八个直方图过滤器。只有在所有启用的过滤器统一给出同方向信号,并且至少有一个过滤器在上一根 K 线上给出相反信号时,才会开仓,从而保留原始 EA 的“双 K 线确认”逻辑。
信号生成
- ADX:比较 +DI 与 −DI,可根据需要反向解释。
- ATR:将当前 ATR 与 SMA 基线比较,计算百分比偏离度。多头需要大于
AtrPositiveThreshold,空头需要低于 AtrNegativeThreshold。
- 布林带:收盘价突破上轨或下轨时产生信号。
- Bulls/Bears Power:多头使用 Bulls Power,空头使用 Bears Power 的绝对值。
- CCI:价格进入超买或超卖区域时触发。
- MACD:监控 MACD 主线与信号线之间的差值(直方图)。
- RSI:使用经典超买/超卖水平。
- 随机指标:比较 %K 线与自定义上下限。
若某个启用的过滤器返回中性值,则当根 K 线不进行交易。策略会缓存前一根 K 线的信号,以确保满足“上一根方向相反”的限制。
风险管理
- 交易手数由
TradeVolume 控制。
AllowPyramiding 允许在已有仓位的方向上继续加仓;否则信号翻转时直接反手。
TakeProfitPoints 与 StopLossPoints 以价格步长为单位,在下单后通过 SetTakeProfit 和 SetStopLoss 立即设置。
UseTimeFilter、SessionStart、SessionEnd 可限制每日的交易时间段。
参数
| 参数 |
说明 |
TradeVolume |
每次开仓的基础手数。 |
AllowPyramiding |
是否允许在同方向加仓。 |
CloseOnOppositeSignal |
综合信号反向时是否立即平仓。 |
UseTimeFilter, SessionStart, SessionEnd |
日内交易时间窗口。 |
UseTakeProfit, TakeProfitPoints |
启用并设置止盈(价格步长)。 |
UseStopLoss, StopLossPoints |
启用并设置止损(价格步长)。 |
UseIndicator1 … UseIndicator8 |
启用具体过滤器。 |
ModeIndicatorX |
指定过滤器采用正向或反向逻辑。 |
| 其他指标参数 |
对应原始 EA 的周期、阈值等设置。 |
与 MQL 版本的差异
- 未实现篮子平仓、声音提示以及网格开仓功能。
- 未移植自动手数、保本与跟踪止损逻辑,请使用固定止盈止损控制风险。
- 未包含点差检查和经纪商特定保护措施。
使用建议
- 启动前设置好
Security 与 Portfolio。
- 根据目标周期调整
CandleType。
- 结合市场波动调整各指标阈值。
- 优化时可以暂时关闭部分过滤器以降低维度。
- 利用
AllowPyramiding 与 CloseOnOppositeSignal 控制快速行情下的仓位风险。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Multi-indicator scalping strategy combining MACD, RSI, and CCI filters.
/// Buys when all indicators agree on bullish signal. Sells on bearish agreement.
/// </summary>
public class HistoScalperStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _cciPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
public HistoScalperStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergence();
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var cci = new CommodityChannelIndex { Length = CciPeriod };
decimal? prevMacd = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(macd, rsi, cci, (candle, macdLine, rsiVal, cciVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevMacd.HasValue)
{
if (prevMacd.Value <= 0 && macdLine > 0 && rsiVal < 70m && cciVal > -100m && Position <= 0)
BuyMarket();
else if (prevMacd.Value >= 0 && macdLine < 0 && rsiVal > 30m && cciVal < 100m && Position >= 0)
SellMarket();
}
prevMacd = macdLine;
})
.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.Indicators import MovingAverageConvergenceDivergence, RelativeStrengthIndex, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class histo_scalper_strategy(Strategy):
def __init__(self):
super(histo_scalper_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI period", "Indicators")
self._macd = None
self._rsi = None
self._cci = None
self._prev_macd = None
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def cci_period(self):
return self._cci_period.Value
def OnReseted(self):
super(histo_scalper_strategy, self).OnReseted()
self._macd = None
self._rsi = None
self._cci = None
self._prev_macd = None
def OnStarted2(self, time):
super(histo_scalper_strategy, self).OnStarted2(time)
self._macd = MovingAverageConvergenceDivergence()
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
self._cci = CommodityChannelIndex()
self._cci.Length = self.cci_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.Bind(self._macd, self._rsi, self._cci, self._process_candle)
subscription.Start()
def _process_candle(self, candle, macd_value, rsi_value, cci_value):
if candle.State != CandleStates.Finished:
return
if not self._macd.IsFormed or not self._rsi.IsFormed or not self._cci.IsFormed:
return
macd_line = float(macd_value)
rsi_val = float(rsi_value)
cci_val = float(cci_value)
if self._prev_macd is not None:
if self._prev_macd <= 0.0 and macd_line > 0.0 and rsi_val < 70.0 and cci_val > -100.0 and self.Position <= 0:
self.BuyMarket()
elif self._prev_macd >= 0.0 and macd_line < 0.0 and rsi_val > 30.0 and cci_val < 100.0 and self.Position >= 0:
self.SellMarket()
self._prev_macd = macd_line
def CreateClone(self):
return histo_scalper_strategy()