TradingLab 最佳 MACD 策略
该策略使用 StockSharp 高阶 API 复刻 MetaTrader 专家顾问 "TradingLab_Best_MACD_Strategy"。策略把移动平均趋势过滤、MACD 金叉死叉以及动态支撑/阻力检测组合在一起,以顺势参与价格动能并利用最近的价格反应。
核心逻辑
- K 线来源:通过可配置的
CandleType订阅完成的 K 线,仅在蜡烛收盘后作出决策。 - 趋势过滤:200 周期简单移动平均线定义趋势方向。做多需要收盘价在均线之上,做空需要收盘价在均线之下。
- 支撑阻力盒:20 周期的最高/最低窗口模拟自定义的 "Box" 指标。当上一根 K 线突破该区间的高点或低点时,分别触发做空或做多信号,并在
SignalValidity根 K 线内保持有效。 - MACD 交叉:标准 MACD(默认 12、26、9)必须在上一根 K 线上穿或下穿信号线,并位于零轴的指定一侧。每次交叉都会在倒计时结束前保持有效,逻辑与原 EA 相同。
- 入场时机:当 MACD 和相应的支撑/阻力触发仍然有效,并且至少一个条件在当前 K 线重新触发时,才允许开仓。
- 出场机制:开仓后根据均线与价格之间的距离计算动态止损与止盈。止盈距离等于
RiskRewardMultiplier乘以用于止损的调整距离。随后每根完成的 K 线都会检查高低点,一旦触及目标即调用ClosePosition()平仓。
参数
| 参数 | 说明 |
|---|---|
OrderVolume |
每次市价单的固定下单量。 |
SignalValidity |
MACD 交叉与支撑/阻力触发保持有效的蜡烛数量。 |
MaLength |
趋势过滤用的简单移动平均周期。 |
BoxPeriod |
构造最高/最低盒子的回溯长度。 |
MacdFastLength、MacdSlowLength、MacdSignalLength |
MACD 的快线、慢线与信号线周期。 |
StopDistancePoints |
止损距离,使用 MetaTrader 点数表示(乘以标的的价格步长)。 |
RiskRewardMultiplier |
用于生成止盈目标的风险收益倍数。 |
CandleType |
订阅的蜡烛数据类型,默认使用 1 小时周期。 |
说明
- 支撑与阻力的检测遵循原策略思路:观察上一根 K 线是否突破 20 周期盒子的高点或低点,一旦突破即重置倒计时。
- 每次进场都会重新计算止损与止盈,并在后续 K 线中根据高低点监控,实现可预测的出场行为。
- 保护管理依赖品种的
PriceStep。若品种未提供有效步长,将回退到 0.0001。
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;
public class TradingLabBestMacdStrategy : Strategy
{
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<int> _signalValidity;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<int> _boxPeriod;
private readonly StrategyParam<int> _macdFastLength;
private readonly StrategyParam<int> _macdSlowLength;
private readonly StrategyParam<int> _macdSignalLength;
private readonly StrategyParam<decimal> _stopDistancePoints;
private readonly StrategyParam<decimal> _riskRewardMultiplier;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private Highest _highest;
private Lowest _lowest;
private MovingAverageConvergenceDivergenceSignal _macd;
private int _resistanceCounter;
private int _supportCounter;
private int _macdDownCounter;
private int _macdUpCounter;
private decimal? _prevMacdMain;
private decimal? _prevMacdSignal;
private decimal? _plannedStop;
private decimal? _plannedTake;
private Sides? _plannedSide;
private decimal? _activeStop;
private decimal? _activeTake;
private Sides? _activeSide;
private decimal? _previousHigh;
private decimal? _previousLow;
private bool _hasPreviousCandle;
/// <summary>
/// Initializes a new instance of <see cref="TradingLabBestMacdStrategy"/>.
/// </summary>
public TradingLabBestMacdStrategy()
{
_orderVolume = Param(nameof(OrderVolume), 1m)
.SetDisplay("Order Volume", "Fixed volume sent with each market order", "Risk")
;
_signalValidity = Param(nameof(SignalValidity), 7)
.SetGreaterThanZero()
.SetDisplay("Signal Validity", "Number of candles a MACD or box trigger remains active", "Filters")
;
_maLength = Param(nameof(MaLength), 20)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Simple moving average period used as the trend filter", "Filters")
;
_boxPeriod = Param(nameof(BoxPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Box Period", "Lookback length for the support/resistance box", "Filters")
;
_macdFastLength = Param(nameof(MacdFastLength), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast Length", "Fast EMA length for MACD", "Indicators")
;
_macdSlowLength = Param(nameof(MacdSlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow Length", "Slow EMA length for MACD", "Indicators")
;
_macdSignalLength = Param(nameof(MacdSignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal Length", "Signal line length for MACD", "Indicators")
;
_stopDistancePoints = Param(nameof(StopDistancePoints), 50m)
.SetGreaterThanZero()
.SetDisplay("Stop Distance (points)", "Protective stop distance from the moving average expressed in points", "Risk")
;
_riskRewardMultiplier = Param(nameof(RiskRewardMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Risk-Reward Multiplier", "Multiplier applied to derive the take-profit distance", "Risk")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Data type used to subscribe for candles", "General")
;
}
/// <summary>
/// Fixed volume sent with every market order.
/// </summary>
public decimal OrderVolume
{
get => _orderVolume.Value;
set => _orderVolume.Value = value;
}
/// <summary>
/// Number of candles that keep MACD and support/resistance triggers active.
/// </summary>
public int SignalValidity
{
get => _signalValidity.Value;
set => _signalValidity.Value = value;
}
/// <summary>
/// Period for the simple moving average trend filter.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Lookback length for the support/resistance box.
/// </summary>
public int BoxPeriod
{
get => _boxPeriod.Value;
set => _boxPeriod.Value = value;
}
/// <summary>
/// Fast EMA length used by MACD.
/// </summary>
public int MacdFastLength
{
get => _macdFastLength.Value;
set => _macdFastLength.Value = value;
}
/// <summary>
/// Slow EMA length used by MACD.
/// </summary>
public int MacdSlowLength
{
get => _macdSlowLength.Value;
set => _macdSlowLength.Value = value;
}
/// <summary>
/// Signal line length used by MACD.
/// </summary>
public int MacdSignalLength
{
get => _macdSignalLength.Value;
set => _macdSignalLength.Value = value;
}
/// <summary>
/// Protective stop distance measured in MetaTrader points.
/// </summary>
public decimal StopDistancePoints
{
get => _stopDistancePoints.Value;
set => _stopDistancePoints.Value = value;
}
/// <summary>
/// Multiplier applied to the adjusted moving-average distance to build the take-profit target.
/// </summary>
public decimal RiskRewardMultiplier
{
get => _riskRewardMultiplier.Value;
set => _riskRewardMultiplier.Value = value;
}
/// <summary>
/// Candle data type used to subscribe for historical bars.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_highest = null;
_lowest = null;
_macd = null;
_resistanceCounter = 0;
_supportCounter = 0;
_macdDownCounter = 0;
_macdUpCounter = 0;
_prevMacdMain = null;
_prevMacdSignal = null;
_plannedStop = null;
_plannedTake = null;
_plannedSide = null;
_activeStop = null;
_activeTake = null;
_activeSide = null;
_previousHigh = null;
_previousLow = null;
_hasPreviousCandle = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Configure the indicators that replicate the MetaTrader calculations.
_sma = new SimpleMovingAverage { Length = MaLength };
_highest = new Highest { Length = BoxPeriod };
_lowest = new Lowest { Length = BoxPeriod };
_macd = new MovingAverageConvergenceDivergenceSignal { Macd = { ShortMa = { Length = MacdFastLength }, LongMa = { Length = MacdSlowLength } }, SignalMa = { Length = MacdSignalLength } };
// Subscribe to the configured candle stream and bind indicator outputs to the handler.
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(new IIndicator[] { _sma, _highest, _lowest, _macd }, ProcessCandle)
.Start();
// removed StartProtection
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished)
return;
// Manage trailing exits before analysing new signals.
CheckProtectiveLevels(candle);
if (values.Length != 4)
{
UpdatePreviousCandle(candle, null, null);
return;
}
var smaValue = values[0].ToDecimal();
var resistanceValue = values[1].ToDecimal();
var supportValue = values[2].ToDecimal();
if (values[3] is not IMovingAverageConvergenceDivergenceSignalValue macdValue)
{
UpdatePreviousCandle(candle, null, null);
return;
}
var macdMain = macdValue.Macd;
var macdSignal = macdValue.Signal;
if (!_sma.IsFormed || !_highest.IsFormed || !_lowest.IsFormed || !_macd.IsFormed)
{
UpdatePreviousCandle(candle, macdMain, macdSignal);
return;
}
// Decrease counters that track how many candles each signal remains active.
if (_resistanceCounter > 0)
_resistanceCounter--;
if (_supportCounter > 0)
_supportCounter--;
if (_macdDownCounter > 0)
_macdDownCounter--;
if (_macdUpCounter > 0)
_macdUpCounter--;
var point = GetPointValue();
if (_hasPreviousCandle)
{
// Detect fresh touches of the synthetic resistance/support levels.
if (_previousHigh.HasValue && resistanceValue > 0m && _previousHigh.Value > resistanceValue)
{
_resistanceCounter = SignalValidity;
}
if (_previousLow.HasValue && supportValue > 0m && _previousLow.Value < supportValue)
{
_supportCounter = SignalValidity;
}
}
if (_prevMacdMain.HasValue && _prevMacdSignal.HasValue)
{
// Track MACD crossovers relative to the zero line.
if (macdMain < macdSignal && _prevMacdMain.Value > _prevMacdSignal.Value && macdMain > 0m)
{
_macdDownCounter = SignalValidity;
}
if (macdMain > macdSignal && _prevMacdMain.Value < _prevMacdSignal.Value && macdMain < 0m)
{
_macdUpCounter = SignalValidity;
}
}
var volume = OrderVolume;
if (volume > 0m)
{
// Evaluate entry conditions once both the MACD and box counters are armed.
var longSignalActive = _macdUpCounter > 0 && candle.ClosePrice > smaValue;
var longTriggeredNow = true;
if (longSignalActive && longTriggeredNow && Position <= 0)
{
var stopOffset = StopDistancePoints * point;
var adjustedDistance = candle.ClosePrice - smaValue + stopOffset;
if (adjustedDistance > 0m)
{
_plannedStop = smaValue - stopOffset;
_plannedTake = candle.ClosePrice + adjustedDistance * RiskRewardMultiplier;
_plannedSide = Sides.Buy;
BuyMarket(volume);
}
}
var shortSignalActive = _macdDownCounter > 0 && candle.ClosePrice < smaValue;
var shortTriggeredNow = true;
if (shortSignalActive && shortTriggeredNow && Position >= 0)
{
var stopOffset = StopDistancePoints * point;
var adjustedDistance = smaValue - candle.ClosePrice + stopOffset;
if (adjustedDistance > 0m)
{
_plannedStop = smaValue + stopOffset;
_plannedTake = candle.ClosePrice - adjustedDistance * RiskRewardMultiplier;
_plannedSide = Sides.Sell;
SellMarket(volume);
}
}
}
UpdatePreviousCandle(candle, macdMain, macdSignal);
}
private void CheckProtectiveLevels(ICandleMessage candle)
{
if (_activeSide == null || Position == 0)
return;
// Close the position if price violates the stored stop-loss or take-profit.
if (_activeSide == Sides.Buy && Position > 0)
{
if (_activeStop.HasValue && candle.LowPrice <= _activeStop.Value)
{
ClearPlannedLevels();
ClearActiveLevels();
ClosePosition();
return;
}
if (_activeTake.HasValue && candle.HighPrice >= _activeTake.Value)
{
ClearPlannedLevels();
ClearActiveLevels();
ClosePosition();
return;
}
}
else if (_activeSide == Sides.Sell && Position < 0)
{
if (_activeStop.HasValue && candle.HighPrice >= _activeStop.Value)
{
ClearPlannedLevels();
ClearActiveLevels();
ClosePosition();
return;
}
if (_activeTake.HasValue && candle.LowPrice <= _activeTake.Value)
{
ClearPlannedLevels();
ClearActiveLevels();
ClosePosition();
}
}
}
private void UpdatePreviousCandle(ICandleMessage candle, decimal? macdMain, decimal? macdSignal)
{
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
_hasPreviousCandle = true;
_prevMacdMain = macdMain;
_prevMacdSignal = macdSignal;
}
private decimal GetPointValue()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
return 0.0001m;
return step;
}
private void ClosePosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(Math.Abs(Position));
}
private void ClearPlannedLevels()
{
_plannedStop = null;
_plannedTake = null;
_plannedSide = null;
}
private void ClearActiveLevels()
{
_activeStop = null;
_activeTake = null;
_activeSide = null;
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (trade.Order?.Security != Security)
return;
if (Position == 0)
{
ClearActiveLevels();
ClearPlannedLevels();
return;
}
if (Position > 0)
{
if (_plannedSide == Sides.Buy)
{
_activeStop = _plannedStop;
_activeTake = _plannedTake;
_activeSide = Sides.Buy;
ClearPlannedLevels();
}
else
{
_activeSide = Sides.Buy;
}
}
else if (Position < 0)
{
if (_plannedSide == Sides.Sell)
{
_activeStop = _plannedStop;
_activeTake = _plannedTake;
_activeSide = Sides.Sell;
ClearPlannedLevels();
}
else
{
_activeSide = Sides.Sell;
}
}
}
}
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
from StockSharp.Algo.Indicators import (
SimpleMovingAverage, Highest, Lowest, MovingAverageConvergenceDivergenceSignal
)
from StockSharp.Algo.Strategies import Strategy
class trading_lab_best_macd_strategy(Strategy):
"""MACD + SMA trend + Highest/Lowest box with signal validity counters and virtual SL/TP."""
def __init__(self):
super(trading_lab_best_macd_strategy, self).__init__()
self._order_volume = self.Param("OrderVolume", 1.0) \
.SetDisplay("Order Volume", "Fixed volume sent with each market order", "Risk")
self._signal_validity = self.Param("SignalValidity", 7) \
.SetGreaterThanZero() \
.SetDisplay("Signal Validity", "Number of candles a MACD or box trigger remains active", "Filters")
self._ma_length = self.Param("MaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("MA Length", "Simple moving average period used as the trend filter", "Filters")
self._box_period = self.Param("BoxPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Box Period", "Lookback length for the support/resistance box", "Filters")
self._macd_fast_length = self.Param("MacdFastLength", 12) \
.SetGreaterThanZero() \
.SetDisplay("MACD Fast Length", "Fast EMA length for MACD", "Indicators")
self._macd_slow_length = self.Param("MacdSlowLength", 26) \
.SetGreaterThanZero() \
.SetDisplay("MACD Slow Length", "Slow EMA length for MACD", "Indicators")
self._macd_signal_length = self.Param("MacdSignalLength", 9) \
.SetGreaterThanZero() \
.SetDisplay("MACD Signal Length", "Signal line length for MACD", "Indicators")
self._stop_distance_points = self.Param("StopDistancePoints", 50.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Distance (points)", "Protective stop distance from the moving average", "Risk")
self._risk_reward_multiplier = self.Param("RiskRewardMultiplier", 1.5) \
.SetGreaterThanZero() \
.SetDisplay("Risk-Reward Multiplier", "Multiplier applied to derive the take-profit distance", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Data type used to subscribe for candles", "General")
self._sma = None
self._highest = None
self._lowest = None
self._macd = None
self._resistance_counter = 0
self._support_counter = 0
self._macd_down_counter = 0
self._macd_up_counter = 0
self._prev_macd_main = None
self._prev_macd_signal = None
self._planned_stop = None
self._planned_take = None
self._planned_side = None
self._active_stop = None
self._active_take = None
self._active_side = None
self._previous_high = None
self._previous_low = None
self._has_previous_candle = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def OrderVolume(self):
return self._order_volume.Value
@property
def SignalValidity(self):
return self._signal_validity.Value
@property
def MaLength(self):
return self._ma_length.Value
@property
def BoxPeriod(self):
return self._box_period.Value
@property
def MacdFastLength(self):
return self._macd_fast_length.Value
@property
def MacdSlowLength(self):
return self._macd_slow_length.Value
@property
def MacdSignalLength(self):
return self._macd_signal_length.Value
@property
def StopDistancePoints(self):
return self._stop_distance_points.Value
@property
def RiskRewardMultiplier(self):
return self._risk_reward_multiplier.Value
def OnReseted(self):
super(trading_lab_best_macd_strategy, self).OnReseted()
self._sma = None
self._highest = None
self._lowest = None
self._macd = None
self._resistance_counter = 0
self._support_counter = 0
self._macd_down_counter = 0
self._macd_up_counter = 0
self._prev_macd_main = None
self._prev_macd_signal = None
self._planned_stop = None
self._planned_take = None
self._planned_side = None
self._active_stop = None
self._active_take = None
self._active_side = None
self._previous_high = None
self._previous_low = None
self._has_previous_candle = False
def OnStarted2(self, time):
super(trading_lab_best_macd_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = self.MaLength
self._highest = Highest()
self._highest.Length = self.BoxPeriod
self._lowest = Lowest()
self._lowest.Length = self.BoxPeriod
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = self.MacdFastLength
self._macd.Macd.LongMa.Length = self.MacdSlowLength
self._macd.SignalMa.Length = self.MacdSignalLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._sma, self._highest, self._lowest, self._macd, self._process_candle).Start()
def _process_candle(self, candle, sma_val, highest_val, lowest_val, macd_val):
if candle.State != CandleStates.Finished:
return
self._check_protective_levels(candle)
sma_value = float(sma_val)
resistance_value = float(highest_val)
support_value = float(lowest_val)
macd_main_raw = macd_val.Macd
macd_signal_raw = macd_val.Signal
if macd_main_raw is None or macd_signal_raw is None:
self._update_previous_candle(candle, None, None)
return
macd_main = float(macd_main_raw)
macd_signal = float(macd_signal_raw)
if not self._sma.IsFormed or not self._highest.IsFormed or \
not self._lowest.IsFormed or not self._macd.IsFormed:
self._update_previous_candle(candle, macd_main, macd_signal)
return
if self._resistance_counter > 0:
self._resistance_counter -= 1
if self._support_counter > 0:
self._support_counter -= 1
if self._macd_down_counter > 0:
self._macd_down_counter -= 1
if self._macd_up_counter > 0:
self._macd_up_counter -= 1
point = self._get_point_value()
if self._has_previous_candle:
if self._previous_high is not None and resistance_value > 0 and self._previous_high > resistance_value:
self._resistance_counter = self.SignalValidity
if self._previous_low is not None and support_value > 0 and self._previous_low < support_value:
self._support_counter = self.SignalValidity
if self._prev_macd_main is not None and self._prev_macd_signal is not None:
if macd_main < macd_signal and self._prev_macd_main > self._prev_macd_signal and macd_main > 0:
self._macd_down_counter = self.SignalValidity
if macd_main > macd_signal and self._prev_macd_main < self._prev_macd_signal and macd_main < 0:
self._macd_up_counter = self.SignalValidity
volume = float(self.OrderVolume)
close = float(candle.ClosePrice)
if volume > 0:
long_signal_active = self._macd_up_counter > 0 and close > sma_value
if long_signal_active and self.Position <= 0:
stop_offset = float(self.StopDistancePoints) * point
adjusted_distance = close - sma_value + stop_offset
if adjusted_distance > 0:
self._planned_stop = sma_value - stop_offset
self._planned_take = close + adjusted_distance * float(self.RiskRewardMultiplier)
self._planned_side = 1
self.BuyMarket(volume)
self._activate_planned_levels(1)
short_signal_active = self._macd_down_counter > 0 and close < sma_value
if short_signal_active and self.Position >= 0:
stop_offset = float(self.StopDistancePoints) * point
adjusted_distance = sma_value - close + stop_offset
if adjusted_distance > 0:
self._planned_stop = sma_value + stop_offset
self._planned_take = close - adjusted_distance * float(self.RiskRewardMultiplier)
self._planned_side = -1
self.SellMarket(volume)
self._activate_planned_levels(-1)
self._update_previous_candle(candle, macd_main, macd_signal)
def _activate_planned_levels(self, direction):
if direction == 1 and self._planned_side == 1:
self._active_stop = self._planned_stop
self._active_take = self._planned_take
self._active_side = 1
elif direction == -1 and self._planned_side == -1:
self._active_stop = self._planned_stop
self._active_take = self._planned_take
self._active_side = -1
self._planned_stop = None
self._planned_take = None
self._planned_side = None
def _check_protective_levels(self, candle):
if self._active_side is None or self.Position == 0:
return
if self._active_side == 1 and self.Position > 0:
if self._active_stop is not None and float(candle.LowPrice) <= self._active_stop:
self._clear_planned_levels()
self._clear_active_levels()
self._close_position()
return
if self._active_take is not None and float(candle.HighPrice) >= self._active_take:
self._clear_planned_levels()
self._clear_active_levels()
self._close_position()
return
elif self._active_side == -1 and self.Position < 0:
if self._active_stop is not None and float(candle.HighPrice) >= self._active_stop:
self._clear_planned_levels()
self._clear_active_levels()
self._close_position()
return
if self._active_take is not None and float(candle.LowPrice) <= self._active_take:
self._clear_planned_levels()
self._clear_active_levels()
self._close_position()
def _update_previous_candle(self, candle, macd_main, macd_signal):
self._previous_high = float(candle.HighPrice)
self._previous_low = float(candle.LowPrice)
self._has_previous_candle = True
self._prev_macd_main = macd_main
self._prev_macd_signal = macd_signal
def _get_point_value(self):
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step > 0:
return step
return 0.0001
def _close_position(self):
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(abs(self.Position))
def _clear_planned_levels(self):
self._planned_stop = None
self._planned_take = None
self._planned_side = None
def _clear_active_levels(self):
self._active_stop = None
self._active_take = None
self._active_side = None
def CreateClone(self):
return trading_lab_best_macd_strategy()