Up3x1 策略(MT5 版本转换)
概述
- 将 MetaTrader 5 专家顾问
up3x1.mq5转换为 StockSharp 高层 API 策略实现。 - 采用三重指数移动平均线(EMA)交叉信号,并带有止损、止盈和移动止损管理。
- 仅在 K 线收盘后处理数据,以模拟原始脚本中
iTickVolume(0) > 1的判断(每根柱子只处理一次)。 - 默认使用 1 小时 K 线,可通过
CandleType参数自定义时间框。
交易逻辑
- 指标配置
- 快速 EMA(
FastPeriod,默认 24)。 - 中速 EMA(
MediumPeriod,默认 60)。 - 慢速 EMA(
SlowPeriod,默认 120)。
- 快速 EMA(
- 做多条件
- 上一根柱子:快速 EMA 低于中速 EMA,且中速 EMA 低于慢速 EMA。
- 当前柱子:中速 EMA 上穿快速 EMA,同时快速 EMA 仍低于慢速 EMA。
- 做空条件
- 上一根柱子:快速 EMA 高于中速 EMA,且中速 EMA 高于慢速 EMA。
- 当前柱子:中速 EMA 上穿快速 EMA,同时二者仍高于慢速 EMA。
- 平仓规则(多空共用)
- 价格相对入场价前进
TakeProfitOffset即止盈(多单用最高价,空单用最低价判断)。 - 价格相对入场价回撤
StopLossOffset即止损(多单看最低价,空单看最高价)。 - 当浮动盈利超过
TrailingStopOffset后启用移动止损,按照固定距离跟随价格(使用柱状图高低价评估)。 - 若快速 EMA 再次下穿中速 EMA 且二者保持在慢速 EMA 上方,则强制平仓(复刻 MQL 代码中的
ma_one_1 > ma_two_1 > ma_three_1判断)。
- 价格相对入场价前进
仓位与风险管理
RiskFraction(默认 0.02)与当前账户净值相乘,近似原脚本FreeMargin * 0.02 / 1000的动态手数公式。BaseVolume(默认 0.1)在无法取得组合价值或计算结果非正时作为备用手数。- 当连续亏损次数超过 1 次后,仓位将按
volume * losses / 3的比例减少;losses计数在盈利后不会清零,与原脚本保持一致。 - 成交量会向下对齐至
Security.VolumeStep,并受Security.MinVolume与Security.MaxVolume约束;若无法满足最小手数则放弃下单。
参数列表
| 参数 | 默认值 | 说明 |
|---|---|---|
FastPeriod |
24 | 快速 EMA 周期。 |
MediumPeriod |
60 | 中速 EMA 周期。 |
SlowPeriod |
120 | 慢速 EMA 周期,用于趋势过滤。 |
TakeProfitOffset |
0.015 | 止盈距离(绝对价格差,需根据品种报价调整)。 |
StopLossOffset |
0.01 | 止损距离(绝对价格差)。 |
TrailingStopOffset |
0.004 | 移动止损距离,设置为 0 可禁用。 |
BaseVolume |
0.1 | 动态手数失效时的备用仓位大小。 |
RiskFraction |
0.02 | 用于动态仓位计算的账户价值比例。 |
CandleType |
1 小时 | 指标计算与信号触发所用的 K 线类型。 |
转换说明
- 由于高层 API 按收盘柱处理数据,移动止损与保护性出场基于 K 线最高/最低价评估,而非逐笔报价,以便在回测与实盘中保持一致性。
- 止损与止盈通过市价平仓执行,而不是挂出保护性委托,从而与高层策略框架兼容。
- 动态仓位依赖
Portfolio.CurrentValue,若该数据不可用则退回BaseVolume,类似原脚本LotCheck回退到手工输入手数。 losses计数不会因盈利而清零,完全沿用 MQL 脚本的累积逻辑。- 所有代码注释均使用英文,以符合项目规范。
使用建议
- 将策略绑定到目标证券和投资组合,并设置
CandleType以匹配在 MT5 中使用的时间周期。 - 根据标的报价精度调整止盈/止损/移动止损的绝对距离(例如 5 位报价的外汇品种中 0.015 ≈ 150 点)。
- 结合账户规模校准
RiskFraction与BaseVolume,确保头寸大小合理。 - 如需关闭移动止损,可将
TrailingStopOffset设为 0。 - 日志中会输出 “Enter long”“Exit short” 等信息,对应原脚本的
Print调试输出。
目录结构
API/2512_Up3x1/
├── CS/Up3x1DynamicSizingStrategy.cs # 转换后的 C# 策略
├── README.md # 英文说明
├── README_zh.md # 中文说明(本文件)
└── README_ru.md # 俄文说明
免责声明
量化策略交易具有高风险。本示例仅供学习研究,请在历史数据与模拟环境中充分验证后再考虑实际部署。
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>
/// EMA triple crossover strategy converted from the MetaTrader 5 "up3x1" expert.
/// Uses three exponential moving averages with optional stop loss, take profit and trailing logic.
/// Position size is reduced after losing trades similar to the original lot optimization routine.
/// </summary>
public class Up3x1DynamicSizingStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _mediumPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<decimal> _takeProfitOffset;
private readonly StrategyParam<decimal> _stopLossOffset;
private readonly StrategyParam<decimal> _trailingStopOffset;
private readonly StrategyParam<decimal> _baseVolume;
private readonly StrategyParam<decimal> _riskFraction;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _mediumEma;
private ExponentialMovingAverage _slowEma;
private bool _hasPrevValues;
private decimal _prevFast;
private decimal _prevMedium;
private decimal _prevSlow;
private decimal _entryPrice;
private decimal _highestPrice;
private decimal _lowestPrice;
private int _losses;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Medium EMA period.
/// </summary>
public int MediumPeriod
{
get => _mediumPeriod.Value;
set => _mediumPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Absolute price distance for take profit.
/// </summary>
public decimal TakeProfitOffset
{
get => _takeProfitOffset.Value;
set => _takeProfitOffset.Value = value;
}
/// <summary>
/// Absolute price distance for stop loss.
/// </summary>
public decimal StopLossOffset
{
get => _stopLossOffset.Value;
set => _stopLossOffset.Value = value;
}
/// <summary>
/// Absolute trailing stop distance.
/// </summary>
public decimal TrailingStopOffset
{
get => _trailingStopOffset.Value;
set => _trailingStopOffset.Value = value;
}
/// <summary>
/// Base volume used when dynamic sizing cannot be calculated.
/// </summary>
public decimal BaseVolume
{
get => _baseVolume.Value;
set => _baseVolume.Value = value;
}
/// <summary>
/// Fraction of portfolio value used for dynamic position sizing.
/// </summary>
public decimal RiskFraction
{
get => _riskFraction.Value;
set => _riskFraction.Value = value;
}
/// <summary>
/// Candle type to subscribe.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public Up3x1DynamicSizingStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 24)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Period of the fastest EMA", "Indicators");
_mediumPeriod = Param(nameof(MediumPeriod), 60)
.SetGreaterThanZero()
.SetDisplay("Medium EMA", "Period of the middle EMA", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 120)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Period of the slowest EMA", "Indicators");
_takeProfitOffset = Param(nameof(TakeProfitOffset), 0.015m)
.SetDisplay("Take Profit", "Absolute take profit distance in price units", "Risk");
_stopLossOffset = Param(nameof(StopLossOffset), 0.01m)
.SetDisplay("Stop Loss", "Absolute stop loss distance in price units", "Risk");
_trailingStopOffset = Param(nameof(TrailingStopOffset), 0.004m)
.SetDisplay("Trailing", "Trailing stop distance that follows price", "Risk");
_baseVolume = Param(nameof(BaseVolume), 0.1m)
.SetDisplay("Base Volume", "Fallback trade volume if dynamic sizing fails", "Money Management");
_riskFraction = Param(nameof(RiskFraction), 0.02m)
.SetDisplay("Risk Fraction", "Fraction of portfolio value used for sizing", "Money Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle series used for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
Volume = BaseVolume;
ResetState();
_losses = 0;
_hasPrevValues = false;
_prevFast = 0m;
_prevMedium = 0m;
_prevSlow = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = BaseVolume;
_fastEma = new EMA
{
Length = FastPeriod
};
_mediumEma = new EMA
{
Length = MediumPeriod
};
_slowEma = new EMA
{
Length = SlowPeriod
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastEma, _mediumEma, _slowEma, ProcessCandle)
.Start();
StartProtection(null, null);
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal mediumValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fastEma.IsFormed || !_mediumEma.IsFormed || !_slowEma.IsFormed)
return;
if (!_hasPrevValues)
{
_prevFast = fastValue;
_prevMedium = mediumValue;
_prevSlow = slowValue;
_hasPrevValues = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fastValue;
_prevMedium = mediumValue;
_prevSlow = slowValue;
return;
}
if (Position > 0)
{
if (TryHandleLongExit(candle, fastValue, mediumValue, slowValue))
{
_prevFast = fastValue;
_prevMedium = mediumValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0)
{
if (TryHandleShortExit(candle, fastValue, mediumValue, slowValue))
{
_prevFast = fastValue;
_prevMedium = mediumValue;
_prevSlow = slowValue;
return;
}
}
else
{
var bullishSetup = _prevFast < _prevMedium && _prevMedium < _prevSlow && mediumValue < fastValue && fastValue < slowValue;
var bearishSetup = _prevFast > _prevMedium && _prevMedium > _prevSlow && mediumValue > fastValue && fastValue > slowValue;
if (bullishSetup)
{
TryEnterLong(candle);
}
else if (bearishSetup)
{
TryEnterShort(candle);
}
}
_prevFast = fastValue;
_prevMedium = mediumValue;
_prevSlow = slowValue;
}
private void TryEnterLong(ICandleMessage candle)
{
var volume = CalculateOrderVolume();
if (volume <= 0m)
{
LogInfo("Skipped long entry because calculated volume is below minimum.");
return;
}
BuyMarket();
_entryPrice = candle.ClosePrice;
_highestPrice = candle.HighPrice;
_lowestPrice = candle.LowPrice;
LogInfo($"Enter long at {candle.ClosePrice} with volume {volume}. Loss counter: {_losses}.");
}
private void TryEnterShort(ICandleMessage candle)
{
var volume = CalculateOrderVolume();
if (volume <= 0m)
{
LogInfo("Skipped short entry because calculated volume is below minimum.");
return;
}
SellMarket();
_entryPrice = candle.ClosePrice;
_highestPrice = candle.HighPrice;
_lowestPrice = candle.LowPrice;
LogInfo($"Enter short at {candle.ClosePrice} with volume {volume}. Loss counter: {_losses}.");
}
private bool TryHandleLongExit(ICandleMessage candle, decimal fastValue, decimal mediumValue, decimal slowValue)
{
if (_entryPrice <= 0m)
return false;
var exitPrice = 0m;
var reason = string.Empty;
if (TakeProfitOffset > 0m)
{
var target = _entryPrice + TakeProfitOffset;
if (candle.HighPrice >= target)
{
exitPrice = target;
reason = "Take profit reached";
}
}
if (exitPrice == 0m && StopLossOffset > 0m)
{
var stop = _entryPrice - StopLossOffset;
if (candle.LowPrice <= stop)
{
exitPrice = stop;
reason = "Stop loss triggered";
}
}
_highestPrice = candle.HighPrice > _highestPrice ? candle.HighPrice : _highestPrice;
if (exitPrice == 0m && TrailingStopOffset > 0m && _highestPrice - _entryPrice > TrailingStopOffset)
{
var trail = _highestPrice - TrailingStopOffset;
if (candle.LowPrice <= trail)
{
exitPrice = trail;
reason = "Trailing stop hit";
}
}
if (exitPrice == 0m)
{
var reversal = _prevFast > _prevMedium && _prevMedium > _prevSlow && slowValue < fastValue && fastValue < mediumValue;
if (reversal)
{
exitPrice = candle.ClosePrice;
reason = "EMA reversal";
}
}
if (exitPrice == 0m)
return false;
ExitPosition(exitPrice, reason);
return true;
}
private bool TryHandleShortExit(ICandleMessage candle, decimal fastValue, decimal mediumValue, decimal slowValue)
{
if (_entryPrice <= 0m)
return false;
var exitPrice = 0m;
var reason = string.Empty;
if (TakeProfitOffset > 0m)
{
var target = _entryPrice - TakeProfitOffset;
if (candle.LowPrice <= target)
{
exitPrice = target;
reason = "Take profit reached";
}
}
if (exitPrice == 0m && StopLossOffset > 0m)
{
var stop = _entryPrice + StopLossOffset;
if (candle.HighPrice >= stop)
{
exitPrice = stop;
reason = "Stop loss triggered";
}
}
_lowestPrice = _lowestPrice == 0m || candle.LowPrice < _lowestPrice ? candle.LowPrice : _lowestPrice;
if (exitPrice == 0m && TrailingStopOffset > 0m && _entryPrice - _lowestPrice > TrailingStopOffset)
{
var trail = _lowestPrice + TrailingStopOffset;
if (candle.HighPrice >= trail)
{
exitPrice = trail;
reason = "Trailing stop hit";
}
}
if (exitPrice == 0m)
{
var reversal = _prevFast > _prevMedium && _prevMedium > _prevSlow && slowValue < fastValue && fastValue < mediumValue;
if (reversal)
{
exitPrice = candle.ClosePrice;
reason = "EMA reversal";
}
}
if (exitPrice == 0m)
return false;
ExitPosition(exitPrice, reason);
return true;
}
private void ExitPosition(decimal exitPrice, string reason)
{
var isLong = Position > 0;
var volume = Math.Abs(Position);
if (volume <= 0m)
return;
var pnl = isLong
? (exitPrice - _entryPrice) * volume
: (_entryPrice - exitPrice) * volume;
if (isLong)
{
SellMarket();
}
else
{
BuyMarket();
}
LogInfo($"Exit {(isLong ? "long" : "short")} at {exitPrice} because {reason}. Approx PnL: {pnl}.");
if (pnl < 0m)
_losses++;
ResetState();
}
private decimal CalculateOrderVolume()
{
var volume = 0m;
var portfolioValue = Portfolio?.CurrentValue ?? 0m;
if (portfolioValue > 0m && RiskFraction > 0m)
volume = portfolioValue * RiskFraction / 1000m;
if (volume <= 0m)
volume = BaseVolume;
if (_losses > 1)
{
var reduction = volume * _losses / 3m;
volume -= reduction;
if (volume <= 0m)
volume = BaseVolume;
}
volume = AdjustVolumeToInstrument(volume);
return volume;
}
private decimal AdjustVolumeToInstrument(decimal volume)
{
var security = Security;
if (security == null)
return volume;
var step = security.VolumeStep ?? 0m;
if (step > 0m)
volume = Math.Floor(volume / step) * step;
var minVolume = security.MinVolume ?? 0m;
if (minVolume > 0m && volume < minVolume)
return 0m;
var maxVolume = security.MaxVolume ?? 0m;
if (maxVolume > 0m && volume > maxVolume)
volume = maxVolume;
return volume;
}
private void ResetState()
{
_entryPrice = 0m;
_highestPrice = 0m;
_lowestPrice = 0m;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class up3x1_dynamic_sizing_strategy(Strategy):
def __init__(self):
super(up3x1_dynamic_sizing_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 24)
self._medium_period = self.Param("MediumPeriod", 60)
self._slow_period = self.Param("SlowPeriod", 120)
self._take_profit_offset = self.Param("TakeProfitOffset", 0.015)
self._stop_loss_offset = self.Param("StopLossOffset", 0.01)
self._trailing_stop_offset = self.Param("TrailingStopOffset", 0.004)
self._base_volume = self.Param("BaseVolume", 0.1)
self._risk_fraction = self.Param("RiskFraction", 0.02)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._has_prev_values = False
self._prev_fast = 0.0
self._prev_medium = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 0.0
self._losses = 0
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def MediumPeriod(self):
return self._medium_period.Value
@MediumPeriod.setter
def MediumPeriod(self, value):
self._medium_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
@property
def TakeProfitOffset(self):
return self._take_profit_offset.Value
@TakeProfitOffset.setter
def TakeProfitOffset(self, value):
self._take_profit_offset.Value = value
@property
def StopLossOffset(self):
return self._stop_loss_offset.Value
@StopLossOffset.setter
def StopLossOffset(self, value):
self._stop_loss_offset.Value = value
@property
def TrailingStopOffset(self):
return self._trailing_stop_offset.Value
@TrailingStopOffset.setter
def TrailingStopOffset(self, value):
self._trailing_stop_offset.Value = value
@property
def BaseVolume(self):
return self._base_volume.Value
@BaseVolume.setter
def BaseVolume(self, value):
self._base_volume.Value = value
@property
def RiskFraction(self):
return self._risk_fraction.Value
@RiskFraction.setter
def RiskFraction(self, value):
self._risk_fraction.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(up3x1_dynamic_sizing_strategy, self).OnStarted2(time)
self._has_prev_values = False
self._prev_fast = 0.0
self._prev_medium = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 0.0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
medium_ema = ExponentialMovingAverage()
medium_ema.Length = self.MediumPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
self._fast_ema = fast_ema
self._medium_ema = medium_ema
self._slow_ema = slow_ema
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, medium_ema, slow_ema, self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle, fast_value, medium_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
medium_val = float(medium_value)
slow_val = float(slow_value)
if not self._fast_ema.IsFormed or not self._medium_ema.IsFormed or not self._slow_ema.IsFormed:
return
if not self._has_prev_values:
self._prev_fast = fast_val
self._prev_medium = medium_val
self._prev_slow = slow_val
self._has_prev_values = True
return
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
tp_offset = float(self.TakeProfitOffset)
sl_offset = float(self.StopLossOffset)
trail_offset = float(self.TrailingStopOffset)
if self.Position > 0:
if self._try_handle_long_exit(candle, fast_val, medium_val, slow_val):
self._prev_fast = fast_val
self._prev_medium = medium_val
self._prev_slow = slow_val
return
elif self.Position < 0:
if self._try_handle_short_exit(candle, fast_val, medium_val, slow_val):
self._prev_fast = fast_val
self._prev_medium = medium_val
self._prev_slow = slow_val
return
else:
bullish_setup = (self._prev_fast < self._prev_medium and
self._prev_medium < self._prev_slow and
medium_val < fast_val and fast_val < slow_val)
bearish_setup = (self._prev_fast > self._prev_medium and
self._prev_medium > self._prev_slow and
medium_val > fast_val and fast_val > slow_val)
if bullish_setup:
self.BuyMarket()
self._entry_price = close
self._highest_price = high
self._lowest_price = low
elif bearish_setup:
self.SellMarket()
self._entry_price = close
self._highest_price = high
self._lowest_price = low
self._prev_fast = fast_val
self._prev_medium = medium_val
self._prev_slow = slow_val
def _try_handle_long_exit(self, candle, fast_val, medium_val, slow_val):
if self._entry_price <= 0.0:
return False
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
tp_offset = float(self.TakeProfitOffset)
sl_offset = float(self.StopLossOffset)
trail_offset = float(self.TrailingStopOffset)
exit_price = 0.0
if tp_offset > 0.0:
target = self._entry_price + tp_offset
if high >= target:
exit_price = target
if exit_price == 0.0 and sl_offset > 0.0:
stop = self._entry_price - sl_offset
if low <= stop:
exit_price = stop
if high > self._highest_price:
self._highest_price = high
if exit_price == 0.0 and trail_offset > 0.0 and self._highest_price - self._entry_price > trail_offset:
trail = self._highest_price - trail_offset
if low <= trail:
exit_price = trail
if exit_price == 0.0:
reversal = (self._prev_fast > self._prev_medium and
self._prev_medium > self._prev_slow and
slow_val < fast_val and fast_val < medium_val)
if reversal:
exit_price = close
if exit_price == 0.0:
return False
self._exit_position(exit_price)
return True
def _try_handle_short_exit(self, candle, fast_val, medium_val, slow_val):
if self._entry_price <= 0.0:
return False
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
tp_offset = float(self.TakeProfitOffset)
sl_offset = float(self.StopLossOffset)
trail_offset = float(self.TrailingStopOffset)
exit_price = 0.0
if tp_offset > 0.0:
target = self._entry_price - tp_offset
if low <= target:
exit_price = target
if exit_price == 0.0 and sl_offset > 0.0:
stop = self._entry_price + sl_offset
if high >= stop:
exit_price = stop
if self._lowest_price == 0.0 or low < self._lowest_price:
self._lowest_price = low
if exit_price == 0.0 and trail_offset > 0.0 and self._entry_price - self._lowest_price > trail_offset:
trail = self._lowest_price + trail_offset
if high >= trail:
exit_price = trail
if exit_price == 0.0:
reversal = (self._prev_fast > self._prev_medium and
self._prev_medium > self._prev_slow and
slow_val < fast_val and fast_val < medium_val)
if reversal:
exit_price = close
if exit_price == 0.0:
return False
self._exit_position(exit_price)
return True
def _exit_position(self, exit_price):
is_long = self.Position > 0
if is_long:
pnl = exit_price - self._entry_price
self.SellMarket()
else:
pnl = self._entry_price - exit_price
self.BuyMarket()
if pnl < 0.0:
self._losses += 1
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 0.0
def OnReseted(self):
super(up3x1_dynamic_sizing_strategy, self).OnReseted()
self._has_prev_values = False
self._prev_fast = 0.0
self._prev_medium = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 0.0
self._losses = 0
def CreateClone(self):
return up3x1_dynamic_sizing_strategy()