在 GitHub 上查看
Potential Entries 策略
概述
Potential Entries Strategy 复刻了 EA_PotentialEntries.mq5 智能交易系统的思想。策略每次评估最近两根已经收盘的蜡烛,寻找反转或动能累积形态。当检测到预设模式时,只在选定方向(做多或做空)开仓,对应模式通过 Pattern Side 参数切换。入场后会根据两根蜡烛的极值重新计算保护价位,从而与原始 MetaTrader 版本的止损设置保持一致。
该实现基于 StockSharp 的高级 API:订阅所需的蜡烛数据,在 ProcessCandle 方法中处理序列,使用 BuyMarket/SellMarket 下达市价单,并在内部记录的止损价被突破时用市价单离场。图表会绘制蜡烛序列以及策略成交,方便快速验证信号。
数据与参数
| 分组 |
名称 |
说明 |
| General |
Pattern Side |
模式方向:Bullish 仅搜索做多信号,Bearish 仅搜索做空信号。 |
| Trading |
Trade Volume |
每次入场使用的市价单手数。开仓前会先平掉相反方向的持仓。 |
| General |
Candle Type |
用于识别形态的蜡烛类型(默认使用 1 小时 K 线)。 |
交易逻辑
策略对比当前完成的蜡烛(C1)与上一根蜡烛(C2),所有影线和实体长度都以价格单位计算。
做多模式
当 Pattern Side = Bullish 时,满足以下任一条件即开多:
- Bullish Hammer(看涨锤子线)
C1 为阳线,C2 为阴线。
C1 下影线至少为实体的两倍,且大于上影线的三倍。
- 发送买入市价单,并把止损设置在
C1 和 C2 的最低价中较低的一侧。
- Bullish Inverted Hammer(看涨倒锤子线)
C1 为阳线,C2 为阴线。
C1 上影线至少为实体的两倍,同时至少是下影线的三倍。
- 按照同样规则买入并设置止损。
- Bullish Momentum(动能增强)
C1 和 C2 都是阳线。
C1 的振幅大于 C2,且实体至少是 C2 的两倍。
- 开多后把止损放在两根蜡烛最低价的较小值。
做空模式
当 Pattern Side = Bearish 时,满足以下任一条件即开空:
- Shooting Star(流星线)
C1 为阴线,C2 为阳线。
C1 上影线至少为实体的两倍,且至少是下影线的三倍。
- 下达卖出市价单,并把止损放在
C1 与 C2 的最高价中较高的一侧。
- Hanging Man(上吊线)
C1 为阴线,C2 为阳线。
C1 下影线至少为实体的两倍,并且超过上影线的三倍。
- 按同样方式开空并更新止损位置。
- Bearish Momentum(动能增强向下)
C1 与 C2 都是阴线。
C1 的实体大于 C2,且振幅至少是 C2 的两倍。
- 建立空头仓位,止损位于两根蜡烛最高价的最大值。
止损与仓位管理
- 策略一次只运行一个方向。如果出现反向信号,会先平掉现有反向仓位再开新单。
- 每次入场都会记录一个新的止损价,基于最新两根蜡烛的极值。之后每根已完成蜡烛都会检查是否击穿该水平,若触发则以市价单离场。
- 当没有持仓时,会清除保存的止损值,避免重复使用过期价格。
使用提示
- 根据需求选择
Bullish 或 Bearish 模式以扫描多头或空头机会。
- 默认的小时蜡烛可以替换为任意可用的蜡烛数据类型。
- 按要求暂未提供 Python 版本,也未创建
PY 目录,仅包含 C# 实现。
- 策略没有设置固定的盈利目标,退出完全依赖止损或人工干预。
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Potential Entries strategy: two-candle reversal patterns with RSI confirmation.
/// Detects bullish/bearish reversals and trades with RSI filter.
/// </summary>
public class PotentialEntriesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _signalCooldownCandles;
private readonly List<ICandleMessage> _candles = new();
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public PotentialEntriesStrategy()
{
_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");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
_candlesSinceTrade = SignalCooldownCandles;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
_candles.Add(candle);
if (_candles.Count > 5)
_candles.RemoveAt(0);
if (_candles.Count >= 2)
{
var curr = _candles[^1];
var prev = _candles[^2];
// Bullish reversal: prev bearish, curr bullish with higher close
var bullish = prev.OpenPrice > prev.ClosePrice
&& curr.ClosePrice > curr.OpenPrice
&& curr.ClosePrice > prev.OpenPrice;
// Bearish reversal: prev bullish, curr bearish with lower close
var bearish = prev.ClosePrice > prev.OpenPrice
&& curr.OpenPrice > curr.ClosePrice
&& curr.ClosePrice < prev.OpenPrice;
if (bullish && rsiValue < 50 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (bearish && rsiValue > 50 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class potential_entries_strategy(Strategy):
def __init__(self):
super(potential_entries_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._candles = []
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(potential_entries_strategy, self).OnReseted()
self._candles.clear()
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(potential_entries_strategy, self).OnStarted2(time)
self._candles.clear()
self._candles_since_trade = self.SignalCooldownCandles
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2:
curr = self._candles[-1]
prev = self._candles[-2]
bullish = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.ClosePrice) > float(prev.OpenPrice))
bearish = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.ClosePrice) < float(prev.OpenPrice))
if bullish and float(rsi_value) < 50 and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif bearish and float(rsi_value) > 50 and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
def CreateClone(self):
return potential_entries_strategy()