Genie Pivot 策略
该策略实现了最初用 MQL4 编写的 "Genie" 枢轴反转剥头皮系统。它扫描最近的八根 K 线以在关键点检测突然的反转。当连续七个最低点下降且当前 K 线形成更高的最低点并收于前一高点之上时触发做多。当连续七个最高点上升且当前 K 线形成更低的最高点并收于前一低点之下时触发做空。
策略使用固定的仓位大小(Strategy.Volume),并同时应用以绝对价格单位表示的追踪止损和止盈。这些参数可以优化,使系统能够捕捉快速反转并保护已获利润。
细节
- 入场条件:
- 多头:
Low[7] > Low[6] > ... > Low[1]且Low[1] < Low[0]且High[1] < Close[0]。 - 空头:
High[7] < High[6] < ... < High[1]且High[1] > High[0]且Low[1] > Close[0]。
- 多头:
- 多空方向:双向。
- 出场条件:
- 触发追踪止损或止盈。
- 止损/止盈:
- 止盈:距入场价的绝对距离。
- 追踪止损:随着盈利移动的绝对距离。
- 默认值:
TakeProfit= 500。TrailingStop= 200。CandleType= 1 分钟。
- 筛选:
- 类型:反转。
- 方向:双向。
- 指标:无。
- 止损:有。
- 复杂度:简单。
- 时间框架:短期。
- 季节性:无。
- 神经网络:无。
- 背离:无。
- 风险级别:中等。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Genie pivot reversal strategy.
/// Enters long after a sequence of falling lows followed by an upside breakout.
/// Enters short after a sequence of rising highs followed by a downside breakout.
/// Applies trailing stop and take-profit in absolute price units.
/// </summary>
public class GeniePivotFixedStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly decimal[] _lows = new decimal[8];
private readonly decimal[] _highs = new decimal[8];
private int _stored;
private int _cooldownRemaining;
/// <summary>
/// Take-profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Trailing stop distance in price units.
/// </summary>
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Candle type used for analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes parameters.
/// </summary>
public GeniePivotFixedStrategy()
{
_takeProfit = Param(nameof(TakeProfit), 500m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Target profit in price units", "Risk Management")
.SetOptimize(100m, 1000m, 100m);
_trailingStop = Param(nameof(TrailingStop), 200m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Trailing stop distance in price units", "Risk Management")
.SetOptimize(50m, 500m, 50m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "General");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Risk Management");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stored = 0;
_cooldownRemaining = 0;
Array.Clear(_lows);
Array.Clear(_highs);
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
stopLoss: new Unit(TrailingStop, UnitTypes.Absolute),
isStopTrailing: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
for (var i = 7; i > 0; i--)
{
_lows[i] = _lows[i - 1];
_highs[i] = _highs[i - 1];
}
_lows[0] = candle.LowPrice;
_highs[0] = candle.HighPrice;
if (_stored < 8)
_stored++;
if (_stored < 5)
return;
if (_cooldownRemaining > 0)
return;
var buySeq = _lows[4] > _lows[3] && _lows[3] > _lows[2] && _lows[2] > _lows[1];
if (buySeq && _lows[1] < _lows[0] && _highs[1] < candle.ClosePrice && candle.ClosePrice > candle.OpenPrice)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
_cooldownRemaining = CooldownBars;
return;
}
var sellSeq = _highs[4] < _highs[3] && _highs[3] < _highs[2] && _highs[2] < _highs[1];
if (sellSeq && _highs[1] > _highs[0] && _lows[1] > candle.ClosePrice && candle.ClosePrice < candle.OpenPrice)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class genie_pivot_fixed_strategy(Strategy):
def __init__(self):
super(genie_pivot_fixed_strategy, self).__init__()
self._take_profit = self.Param("TakeProfit", 500.0) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit", "Target profit in price units", "Risk Management") \
.SetOptimize(100.0, 1000.0, 100.0)
self._trailing_stop = self.Param("TrailingStop", 200.0) \
.SetGreaterThanZero() \
.SetDisplay("Trailing Stop", "Trailing stop distance in price units", "Risk Management") \
.SetOptimize(50.0, 500.0, 50.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "General")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Risk Management")
self._lows = [0.0] * 8
self._highs = [0.0] * 8
self._stored = 0
self._cooldown_remaining = 0
@property
def take_profit(self):
return self._take_profit.Value
@property
def trailing_stop(self):
return self._trailing_stop.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(genie_pivot_fixed_strategy, self).OnReseted()
self._stored = 0
self._cooldown_remaining = 0
self._lows = [0.0] * 8
self._highs = [0.0] * 8
def OnStarted2(self, time):
super(genie_pivot_fixed_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
Unit(float(self.take_profit), UnitTypes.Absolute),
Unit(float(self.trailing_stop), UnitTypes.Absolute),
True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
# Shift arrays
for i in range(7, 0, -1):
self._lows[i] = self._lows[i - 1]
self._highs[i] = self._highs[i - 1]
self._lows[0] = float(candle.LowPrice)
self._highs[0] = float(candle.HighPrice)
if self._stored < 8:
self._stored += 1
if self._stored < 5:
return
if self._cooldown_remaining > 0:
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
# Buy sequence: falling lows [4] > [3] > [2] > [1]
buy_seq = self._lows[4] > self._lows[3] and self._lows[3] > self._lows[2] and self._lows[2] > self._lows[1]
if buy_seq and self._lows[1] < self._lows[0] and self._highs[1] < close and close > open_price:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
return
# Sell sequence: rising highs [4] < [3] < [2] < [1]
sell_seq = self._highs[4] < self._highs[3] and self._highs[3] < self._highs[2] and self._highs[2] < self._highs[1]
if sell_seq and self._highs[1] > self._highs[0] and self._lows[1] > close and close < open_price:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
def CreateClone(self):
return genie_pivot_fixed_strategy()