Exp Price Position
Exp Price Position 策略来自原始的 MetaTrader 专家顾问,它结合价格位置与趋势过滤。 策略通过两条中值移动平均线确定最近的摆动水平,然后使用一对平滑移动平均线来识别趋势方向。 只有当价格位置与趋势方向一致并且与当前K线结构相符时才会开仓。
该策略适用于在价格回撤到动态中值水平后趋势反转的市场。通过移动止损和收益比来管理风险。
细节
- 入场条件:价格在最后摆动水平之上且趋势向上时做多;在其下方且趋势向下时做空。
- 做多/做空:双向。
- 出场条件:反向信号或保护性止损。
- 止损:有,采用移动止损和盈亏比。
- 默认值:
FastPeriod= 2SlowPeriod= 30MedianFastPeriod= 26MedianSlowPeriod= 20TpSlRatio= 3mTrailingStopPips= 10mCandleType= TimeSpan.FromHours(1)
- 过滤器:
- 类别:趋势跟随
- 方向:双向
- 指标:Smoothed Moving Average, Simple Moving Average
- 止损:移动止损
- 复杂度:中等
- 时间框架:日内
- 季节性:否
- 神经网络:否
- 背离:否
- 风险水平:中等
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>
/// Port of the ExpPricePosition MetaTrader expert.
/// Combines price position with a step trend filter based on smoothed moving averages.
/// </summary>
public class ExpPricePositionStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _medianFastPeriod;
private readonly StrategyParam<int> _medianSlowPeriod;
private readonly StrategyParam<decimal> _tpSlRatio;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<bool> _useTrailingStop;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
private decimal _lastCrossLevel;
private bool _hasCrossLevel;
/// <summary>
/// Fast smoothed moving average period (default: 2).
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow smoothed moving average period (default: 30).
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Median SMMA period used for price position (default: 26).
/// </summary>
public int MedianFastPeriod
{
get => _medianFastPeriod.Value;
set => _medianFastPeriod.Value = value;
}
/// <summary>
/// Median SMA period used for price position (default: 20).
/// </summary>
public int MedianSlowPeriod
{
get => _medianSlowPeriod.Value;
set => _medianSlowPeriod.Value = value;
}
/// <summary>
/// Take profit to stop loss ratio (default: 3).
/// </summary>
public decimal TpSlRatio
{
get => _tpSlRatio.Value;
set => _tpSlRatio.Value = value;
}
/// <summary>
/// Trailing stop value in points (default: 10).
/// </summary>
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Use trailing stop protection.
/// </summary>
public bool UseTrailingStop
{
get => _useTrailingStop.Value;
set => _useTrailingStop.Value = value;
}
/// <summary>
/// Candle type for subscription.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes <see cref="ExpPricePositionStrategy"/>.
/// </summary>
public ExpPricePositionStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 2)
.SetDisplay("Fast Period", "Fast SMMA period", "Parameters")
.SetGreaterThanZero();
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetDisplay("Slow Period", "Slow SMMA period", "Parameters")
.SetGreaterThanZero();
_medianFastPeriod = Param(nameof(MedianFastPeriod), 26)
.SetDisplay("Median Fast Period", "Median SMMA period", "Parameters")
.SetGreaterThanZero();
_medianSlowPeriod = Param(nameof(MedianSlowPeriod), 20)
.SetDisplay("Median Slow Period", "Median SMA period", "Parameters")
.SetGreaterThanZero();
_tpSlRatio = Param(nameof(TpSlRatio), 3m)
.SetDisplay("TP/SL Ratio", "Take profit to stop loss ratio", "Risk");
_trailingStopPips = Param(nameof(TrailingStopPips), 10m)
.SetDisplay("Trailing Stop", "Trailing stop value in points", "Risk");
_useTrailingStop = Param(nameof(UseTrailingStop), true)
.SetDisplay("Use Trailing Stop", "Enable trailing stop", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
_lastCrossLevel = 0m;
_hasCrossLevel = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (UseTrailingStop)
{
StartProtection(
stopLoss: new Unit(TrailingStopPips, UnitTypes.Absolute),
takeProfit: new Unit(TrailingStopPips * TpSlRatio, UnitTypes.Absolute));
}
var fast = new SmoothedMovingAverage { Length = FastPeriod };
var slow = new SmoothedMovingAverage { Length = SlowPeriod };
var medianFast = new SmoothedMovingAverage { Length = MedianFastPeriod };
var medianSlow = new SMA { Length = MedianSlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, medianFast, medianSlow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle,
decimal fast, decimal slow, decimal medianFast, decimal medianSlow)
{
if (candle.State != CandleStates.Finished)
return;
var signal = (medianFast + medianSlow) / 2m;
if (candle.OpenPrice <= signal && candle.ClosePrice > signal)
{
_lastCrossLevel = candle.LowPrice;
_hasCrossLevel = true;
}
else if (candle.OpenPrice >= signal && candle.ClosePrice < signal)
{
_lastCrossLevel = candle.HighPrice;
_hasCrossLevel = true;
}
if (!_hasCrossLevel)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast is null || _prevSlow is null)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var pricePos = candle.ClosePrice > _lastCrossLevel ? 1 : -1;
var stepUp = fast > slow && fast > _prevFast && _prevFast > _prevSlow;
var stepDown = fast < slow && fast < _prevFast && _prevFast < _prevSlow;
if (pricePos > 0 && stepUp && candle.ClosePrice > candle.OpenPrice && candle.LowPrice < fast && Position <= 0)
BuyMarket();
else if (pricePos < 0 && stepDown && candle.ClosePrice < candle.OpenPrice && candle.HighPrice > fast && Position >= 0)
SellMarket();
_prevFast = fast;
_prevSlow = slow;
}
}
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 SmoothedMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_price_position_strategy(Strategy):
"""
Price position with step trend filter based on smoothed moving averages.
Combines SMMA crossover with price position relative to signal level.
"""
def __init__(self):
super(exp_price_position_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 2) \
.SetDisplay("Fast Period", "Fast SMMA period", "Parameters")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow Period", "Slow SMMA period", "Parameters")
self._median_fast_period = self.Param("MedianFastPeriod", 26) \
.SetDisplay("Median Fast Period", "Median SMMA period", "Parameters")
self._median_slow_period = self.Param("MedianSlowPeriod", 20) \
.SetDisplay("Median Slow Period", "Median SMA period", "Parameters")
self._tp_sl_ratio = self.Param("TpSlRatio", 3.0) \
.SetDisplay("TP/SL Ratio", "Take profit to stop loss ratio", "Risk")
self._trailing_stop_pips = self.Param("TrailingStopPips", 10.0) \
.SetDisplay("Trailing Stop", "Trailing stop in points", "Risk")
self._use_trailing = self.Param("UseTrailingStop", True) \
.SetDisplay("Use Trailing Stop", "Enable trailing stop", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._prev_fast = None
self._prev_slow = None
self._last_cross_level = 0.0
self._has_cross_level = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_price_position_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
self._last_cross_level = 0.0
self._has_cross_level = False
def OnStarted2(self, time):
super(exp_price_position_strategy, self).OnStarted2(time)
if self._use_trailing.Value:
ts = float(self._trailing_stop_pips.Value)
ratio = float(self._tp_sl_ratio.Value)
self.StartProtection(
Unit(float(ts * ratio), UnitTypes.Absolute),
Unit(float(ts), UnitTypes.Absolute)
)
fast = SmoothedMovingAverage()
fast.Length = self._fast_period.Value
slow = SmoothedMovingAverage()
slow.Length = self._slow_period.Value
median_fast = SmoothedMovingAverage()
median_fast.Length = self._median_fast_period.Value
median_slow = SimpleMovingAverage()
median_slow.Length = self._median_slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, median_fast, median_slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val, mf_val, ms_val):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_val)
slow_val = float(slow_val)
mf_val = float(mf_val)
ms_val = float(ms_val)
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
signal = (mf_val + ms_val) / 2.0
if open_p <= signal and close > signal:
self._last_cross_level = float(candle.LowPrice)
self._has_cross_level = True
elif open_p >= signal and close < signal:
self._last_cross_level = float(candle.HighPrice)
self._has_cross_level = True
if not self._has_cross_level:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
price_pos = 1 if close > self._last_cross_level else -1
step_up = fast_val > slow_val and fast_val > self._prev_fast and self._prev_fast > self._prev_slow
step_down = fast_val < slow_val and fast_val < self._prev_fast and self._prev_fast < self._prev_slow
if (price_pos > 0 and step_up and close > open_p and
float(candle.LowPrice) < fast_val and self.Position <= 0):
self.BuyMarket()
elif (price_pos < 0 and step_down and close < open_p and
float(candle.HighPrice) > fast_val and self.Position >= 0):
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_price_position_strategy()