PROphet 策略
PROphet 策略通过分析最近三根已完成K线的价格区间,在指定的交易时间内产生信号。自定义函数结合这些区间与用户设置的系数,若结果为正,则在相应方向开仓。
多头使用系数 X1..X4 和 BuyStopPoints 设定的移动止损。空头使用系数 Y1..Y4 和 SellStopPoints。当价格向有利方向移动超过点差加上两倍止损距离时,止损随之上移或下移。仓位在18:00之后或触及移动止损时关闭。
细节
- 入场条件
- 多头:
Qu(X1,X2,X3,X4) > 0且当前小时在10到18之间。 - 空头:
Qu(Y1,Y2,Y3,Y4) > 0且当前小时在10到18之间。
- 多头:
- 出场条件
- 多头:小时 > 18 或最佳买价跌破移动止损。
- 空头:小时 > 18 或最佳卖价升破移动止损。
- 参数
EnableBuy– 允许开多。EnableSell– 允许开空。X1, X2, X3, X4– 多头信号函数系数。Y1, Y2, Y3, Y4– 空头信号函数系数。BuyStopPoints– 多头移动止损的点数距离。SellStopPoints– 空头移动止损的点数距离。CandleType– 计算所用的K线类型(默认5分钟)。
- 过滤
- 类别:日内
- 方向:双向
- 指标:无
- 止损:移动止损
- 复杂度:中等
- 时间框架:短期
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>
/// PROphet strategy translated from MQL.
/// Uses price ranges of previous candles to generate signals
/// and applies trailing stops.
/// </summary>
public class PROphetStrategy : Strategy
{
private readonly StrategyParam<int> _x1;
private readonly StrategyParam<int> _x2;
private readonly StrategyParam<int> _x3;
private readonly StrategyParam<int> _x4;
private readonly StrategyParam<int> _y1;
private readonly StrategyParam<int> _y2;
private readonly StrategyParam<int> _y3;
private readonly StrategyParam<int> _y4;
private readonly StrategyParam<decimal> _stopMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal _stopPrice;
private decimal _prevHigh1;
private decimal _prevLow1;
private decimal _prevHigh2;
private decimal _prevLow2;
private decimal _prevHigh3;
private decimal _prevLow3;
private int _historyCount;
/// <summary>First coefficient for the buy signal.</summary>
public int X1 { get => _x1.Value; set => _x1.Value = value; }
/// <summary>Second coefficient for the buy signal.</summary>
public int X2 { get => _x2.Value; set => _x2.Value = value; }
/// <summary>Third coefficient for the buy signal.</summary>
public int X3 { get => _x3.Value; set => _x3.Value = value; }
/// <summary>Fourth coefficient for the buy signal.</summary>
public int X4 { get => _x4.Value; set => _x4.Value = value; }
/// <summary>First coefficient for the sell signal.</summary>
public int Y1 { get => _y1.Value; set => _y1.Value = value; }
/// <summary>Second coefficient for the sell signal.</summary>
public int Y2 { get => _y2.Value; set => _y2.Value = value; }
/// <summary>Third coefficient for the sell signal.</summary>
public int Y3 { get => _y3.Value; set => _y3.Value = value; }
/// <summary>Fourth coefficient for the sell signal.</summary>
public int Y4 { get => _y4.Value; set => _y4.Value = value; }
/// <summary>Stop loss multiplier.</summary>
public decimal StopMultiplier { get => _stopMultiplier.Value; set => _stopMultiplier.Value = value; }
/// <summary>The candle type.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public PROphetStrategy()
{
_x1 = Param(nameof(X1), 9).SetDisplay("X1", "First coefficient for buy", "Buy");
_x2 = Param(nameof(X2), 29).SetDisplay("X2", "Second coefficient for buy", "Buy");
_x3 = Param(nameof(X3), 94).SetDisplay("X3", "Third coefficient for buy", "Buy");
_x4 = Param(nameof(X4), 125).SetDisplay("X4", "Fourth coefficient for buy", "Buy");
_y1 = Param(nameof(Y1), 61).SetDisplay("Y1", "First coefficient for sell", "Sell");
_y2 = Param(nameof(Y2), 100).SetDisplay("Y2", "Second coefficient for sell", "Sell");
_y3 = Param(nameof(Y3), 117).SetDisplay("Y3", "Third coefficient for sell", "Sell");
_y4 = Param(nameof(Y4), 31).SetDisplay("Y4", "Fourth coefficient for sell", "Sell");
_stopMultiplier = Param(nameof(StopMultiplier), 0.005m)
.SetDisplay("Stop Multiplier", "Stop loss as fraction of price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for calculations", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stopPrice = 0m;
_prevHigh1 = 0m;
_prevLow1 = 0m;
_prevHigh2 = 0m;
_prevLow2 = 0m;
_prevHigh3 = 0m;
_prevLow3 = 0m;
_historyCount = 0;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_historyCount = 0;
_stopPrice = 0;
var atr = new AverageTrueRange { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (_historyCount >= 3)
{
if (Position > 0)
{
// Trailing stop for long
var newStop = close - atrValue * 2;
if (newStop > _stopPrice)
_stopPrice = newStop;
if (close <= _stopPrice)
SellMarket(Math.Abs(Position));
}
else if (Position < 0)
{
// Trailing stop for short
var newStop = close + atrValue * 2;
if (newStop < _stopPrice || _stopPrice == 0)
_stopPrice = newStop;
if (close >= _stopPrice)
BuyMarket(Math.Abs(Position));
}
else
{
var buySignal = Qu(X1, X2, X3, X4);
var sellSignal = Qu(Y1, Y2, Y3, Y4);
if (buySignal > 0 && sellSignal <= 0)
{
_stopPrice = close - atrValue * 2;
BuyMarket();
}
else if (sellSignal > 0 && buySignal <= 0)
{
_stopPrice = close + atrValue * 2;
SellMarket();
}
}
}
UpdateHistory(candle);
}
private void UpdateHistory(ICandleMessage candle)
{
_prevHigh3 = _prevHigh2;
_prevLow3 = _prevLow2;
_prevHigh2 = _prevHigh1;
_prevLow2 = _prevLow1;
_prevHigh1 = candle.HighPrice;
_prevLow1 = candle.LowPrice;
if (_historyCount < 3)
_historyCount++;
}
private decimal Qu(int q1, int q2, int q3, int q4)
{
return (q1 - 100) * Math.Abs(_prevHigh1 - _prevLow2)
+ (q2 - 100) * Math.Abs(_prevHigh3 - _prevLow2)
+ (q3 - 100) * Math.Abs(_prevHigh2 - _prevLow1)
+ (q4 - 100) * Math.Abs(_prevHigh2 - _prevLow3);
}
}
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.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class pr_ophet_strategy(Strategy):
def __init__(self):
super(pr_ophet_strategy, self).__init__()
self._x1 = self.Param("X1", 9)
self._x2 = self.Param("X2", 29)
self._x3 = self.Param("X3", 94)
self._x4 = self.Param("X4", 125)
self._y1 = self.Param("Y1", 61)
self._y2 = self.Param("Y2", 100)
self._y3 = self.Param("Y3", 117)
self._y4 = self.Param("Y4", 31)
self._stop_multiplier = self.Param("StopMultiplier", 0.005)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._stop_price = 0.0
self._prev_high1 = 0.0
self._prev_low1 = 0.0
self._prev_high2 = 0.0
self._prev_low2 = 0.0
self._prev_high3 = 0.0
self._prev_low3 = 0.0
self._history_count = 0
@property
def X1(self):
return self._x1.Value
@X1.setter
def X1(self, value):
self._x1.Value = value
@property
def X2(self):
return self._x2.Value
@X2.setter
def X2(self, value):
self._x2.Value = value
@property
def X3(self):
return self._x3.Value
@X3.setter
def X3(self, value):
self._x3.Value = value
@property
def X4(self):
return self._x4.Value
@X4.setter
def X4(self, value):
self._x4.Value = value
@property
def Y1(self):
return self._y1.Value
@Y1.setter
def Y1(self, value):
self._y1.Value = value
@property
def Y2(self):
return self._y2.Value
@Y2.setter
def Y2(self, value):
self._y2.Value = value
@property
def Y3(self):
return self._y3.Value
@Y3.setter
def Y3(self, value):
self._y3.Value = value
@property
def Y4(self):
return self._y4.Value
@Y4.setter
def Y4(self, value):
self._y4.Value = value
@property
def StopMultiplier(self):
return self._stop_multiplier.Value
@StopMultiplier.setter
def StopMultiplier(self, value):
self._stop_multiplier.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(pr_ophet_strategy, self).OnStarted2(time)
self._history_count = 0
self._stop_price = 0.0
self._prev_high1 = 0.0
self._prev_low1 = 0.0
self._prev_high2 = 0.0
self._prev_low2 = 0.0
self._prev_high3 = 0.0
self._prev_low3 = 0.0
atr = AverageTrueRange()
atr.Length = 14
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def _qu(self, q1, q2, q3, q4):
return ((q1 - 100) * abs(self._prev_high1 - self._prev_low2)
+ (q2 - 100) * abs(self._prev_high3 - self._prev_low2)
+ (q3 - 100) * abs(self._prev_high2 - self._prev_low1)
+ (q4 - 100) * abs(self._prev_high2 - self._prev_low3))
def ProcessCandle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
atr_val = float(atr_value)
if self._history_count >= 3:
if self.Position > 0:
new_stop = close - atr_val * 2.0
if new_stop > self._stop_price:
self._stop_price = new_stop
if close <= self._stop_price:
self.SellMarket()
elif self.Position < 0:
new_stop = close + atr_val * 2.0
if new_stop < self._stop_price or self._stop_price == 0.0:
self._stop_price = new_stop
if close >= self._stop_price:
self.BuyMarket()
else:
buy_signal = self._qu(int(self.X1), int(self.X2), int(self.X3), int(self.X4))
sell_signal = self._qu(int(self.Y1), int(self.Y2), int(self.Y3), int(self.Y4))
if buy_signal > 0.0 and sell_signal <= 0.0:
self._stop_price = close - atr_val * 2.0
self.BuyMarket()
elif sell_signal > 0.0 and buy_signal <= 0.0:
self._stop_price = close + atr_val * 2.0
self.SellMarket()
self._update_history(candle)
def _update_history(self, candle):
self._prev_high3 = self._prev_high2
self._prev_low3 = self._prev_low2
self._prev_high2 = self._prev_high1
self._prev_low2 = self._prev_low1
self._prev_high1 = float(candle.HighPrice)
self._prev_low1 = float(candle.LowPrice)
if self._history_count < 3:
self._history_count += 1
def OnReseted(self):
super(pr_ophet_strategy, self).OnReseted()
self._stop_price = 0.0
self._prev_high1 = 0.0
self._prev_low1 = 0.0
self._prev_high2 = 0.0
self._prev_low2 = 0.0
self._prev_high3 = 0.0
self._prev_low3 = 0.0
self._history_count = 0
def CreateClone(self):
return pr_ophet_strategy()