Estrategia Momo Trades
Conversión del asesor experto original de MetaTrader "Momo_trades" que opera rupturas de momentum filtradas por una media móvil y la estructura del MACD.
Lógica de la estrategia
- Trabaja con velas completadas del marco temporal configurado y procesa solo una posición neta a la vez.
- Usa una media móvil simple con un desplazamiento de barra configurable para medir cuánto cerró el precio alejado del promedio. Las operaciones largas requieren que el cierre desplazado esté por encima de la SMA en más del umbral de desplazamiento de precio; los cortos requieren lo contrario.
- Evalúa un patrón de momentum MACD en cascada que refleja las reglas MQL: varios valores pasados de la línea principal MACD deben aumentar a través de cero para largos o disminuir a través de cero para cortos. Esto evita operaciones mientras el momentum se debilita.
- Abre una orden de mercado con el volumen de la estrategia una vez que tanto el filtro de distancia SMA como el patrón MACD se alinean para la misma dirección.
Gestión de riesgo
- El stop-loss, take-profit, trailing stop, paso de trailing, break-even y los inputs de desplazamiento de precio se definen en pips y se convierten automáticamente a unidades de precio usando el paso del instrumento.
- Cuando se proporcionan valores de take-profit y trailing, el stop solo se arrastra después de que el precio avance por la distancia de trailing más el paso de trailing, reproduciendo el comportamiento MQL.
- Cuando no hay take-profit configurado pero sí una distancia de break-even, el stop se mueve al precio de entrada una vez que se alcanza el disparador de break-even.
- Todos los niveles de stop y take se recalculan en cada vela completada y se cierran mediante órdenes de mercado cuando son cruzados por los extremos de la vela.
Gestión de sesión
- La bandera
CloseEndDaycoincide con el asesor experto original y cierra cualquier posición activa a las 23:00 hora de la plataforma (21:00 los viernes). Después del corte, la estrategia omite nuevas entradas hasta el día siguiente.
Parámetros
- SMA Period / MA Bar Shift – longitud de la media móvil y el índice de barra usado para obtener valores de SMA y precio.
- MACD Fast / Slow / Signal / Bar Shift – configuración de MACD y el desplazamiento aplicado a los valores almacenados para las verificaciones de patrón.
- Stop Loss / Take Profit / Trailing Stop / Trailing Step / Breakeven / Price Shift – distancias en pips que controlan la salida, el trailing y los filtros de SMA.
- Close End Of Day – cierra posiciones después del fin de sesión configurado.
- Candle Type – marco temporal usado para velas y cálculos de indicadores.
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>
/// Momentum strategy based on MACD momentum and distance from SMA similar to the original MQL logic.
/// </summary>
public class MomoTradesStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _maBarShift;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<int> _macdBarShift;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<decimal> _trailingStepPips;
private readonly StrategyParam<decimal> _breakevenPips;
private readonly StrategyParam<decimal> _priceShiftPips;
private readonly StrategyParam<bool> _closeEndDay;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private MovingAverageConvergenceDivergence _macd;
// Indicators follow the same configuration as in the MQL script.
private readonly decimal[] _macdHistory = new decimal[64];
private readonly decimal[] _maHistory = new decimal[64];
private readonly decimal[] _closeHistory = new decimal[64];
// Buffers store the recent history required for shifted indicator access.
private int _macdCount;
private int _maCount;
private int _closeCount;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private decimal? _breakevenTrigger;
private decimal? _trailingDistance;
private decimal? _trailingStep;
private bool _isLongPosition;
private int _cooldownCounter;
// Position management state persists between candles.
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
public int MaBarShift
{
get => _maBarShift.Value;
set => _maBarShift.Value = value;
}
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
public int MacdBarShift
{
get => _macdBarShift.Value;
set => _macdBarShift.Value = value;
}
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
public decimal TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
public decimal BreakevenPips
{
get => _breakevenPips.Value;
set => _breakevenPips.Value = value;
}
public decimal PriceShiftPips
{
get => _priceShiftPips.Value;
set => _priceShiftPips.Value = value;
}
public bool CloseEndDay
{
get => _closeEndDay.Value;
set => _closeEndDay.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public MomoTradesStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 22).SetGreaterThanZero().SetDisplay("SMA Period", "Period of the moving average", "Indicators");
_maBarShift = Param(nameof(MaBarShift), 6).SetNotNegative().SetDisplay("MA Bar Shift", "Bar shift used for SMA comparison", "Indicators");
_macdFast = Param(nameof(MacdFast), 12).SetGreaterThanZero().SetDisplay("MACD Fast", "Fast EMA period for MACD", "Indicators");
_macdSlow = Param(nameof(MacdSlow), 26).SetGreaterThanZero().SetDisplay("MACD Slow", "Slow EMA period for MACD", "Indicators");
_macdSignal = Param(nameof(MacdSignal), 9).SetGreaterThanZero().SetDisplay("MACD Signal", "Signal SMA period for MACD", "Indicators");
_macdBarShift = Param(nameof(MacdBarShift), 2).SetNotNegative().SetDisplay("MACD Bar Shift", "Offset applied to MACD values", "Indicators");
_stopLossPips = Param(nameof(StopLossPips), 25m).SetNotNegative().SetDisplay("Stop Loss", "Stop loss distance in pips", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 0m).SetNotNegative().SetDisplay("Take Profit", "Take profit distance in pips", "Risk");
_trailingStopPips = Param(nameof(TrailingStopPips), 0m).SetNotNegative().SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Risk");
_trailingStepPips = Param(nameof(TrailingStepPips), 5m).SetNotNegative().SetDisplay("Trailing Step", "Trailing step distance in pips", "Risk");
_breakevenPips = Param(nameof(BreakevenPips), 10m).SetNotNegative().SetDisplay("Breakeven", "Distance to move stop to breakeven", "Risk");
_priceShiftPips = Param(nameof(PriceShiftPips), 5m).SetNotNegative().SetDisplay("Price Shift", "Required price distance from SMA", "Filters");
_closeEndDay = Param(nameof(CloseEndDay), true).SetDisplay("Close End Of Day", "Close positions near session end", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Source candles for calculations", "General");
Volume = 1m;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
Array.Clear(_macdHistory, 0, _macdHistory.Length);
Array.Clear(_maHistory, 0, _maHistory.Length);
Array.Clear(_closeHistory, 0, _closeHistory.Length);
_macdCount = 0;
_maCount = 0;
_closeCount = 0;
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
_breakevenTrigger = null;
_trailingDistance = null;
_trailingStep = null;
_isLongPosition = false;
_cooldownCounter = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new SimpleMovingAverage { Length = SmaPeriod };
_macd = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow },
};
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_sma, _macd, ProcessCandle).Start();
}
// Process each finished candle to evaluate entries with indicator filters.
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_sma.IsFormed || !_macd.IsFormed)
return;
PushValue(_closeHistory, ref _closeCount, candle.ClosePrice);
PushValue(_maHistory, ref _maCount, smaValue);
PushValue(_macdHistory, ref _macdCount, macdValue);
// Cache the latest values so shifted lookups mimic the MQL buffer usage.
ManageActivePosition(candle);
if (_cooldownCounter > 0)
{
_cooldownCounter--;
return;
}
if (Position != 0)
return;
if (CloseEndDay && ShouldCloseForDay(candle))
return;
if (!TryGetHistoryValue(_closeHistory, _closeCount, MaBarShift, out var shiftedClose))
return;
if (!TryGetHistoryValue(_maHistory, _maCount, MaBarShift, out var shiftedMa))
return;
var priceShift = GetPipValue(PriceShiftPips);
var emaBuy = shiftedClose - shiftedMa > priceShift;
var emaSell = shiftedMa - shiftedClose > priceShift;
var macdBuy = CheckMacdPattern(true);
var macdSell = CheckMacdPattern(false);
if (macdBuy && emaBuy)
{
EnterLong(candle.ClosePrice);
_cooldownCounter = 5;
}
else if (macdSell && emaSell)
{
EnterShort(candle.ClosePrice);
_cooldownCounter = 5;
}
}
private void ManageActivePosition(ICandleMessage candle)
{
if (Position == 0)
{
return;
}
if (CloseEndDay && ShouldCloseForDay(candle))
{
ClosePosition();
return;
}
var close = candle.ClosePrice;
// Adjust stop levels according to trailing or breakeven rules before exits.
if (_trailingDistance.HasValue && _trailingStep.HasValue)
{
if (_isLongPosition)
{
if (close - _entryPrice > _trailingDistance.Value + _trailingStep.Value)
{
var newStop = close - _trailingDistance.Value;
if (!_stopPrice.HasValue || newStop > _stopPrice.Value)
_stopPrice = newStop;
}
}
else
{
if (_entryPrice - close > _trailingDistance.Value + _trailingStep.Value)
{
var newStop = close + _trailingDistance.Value;
if (!_stopPrice.HasValue || newStop < _stopPrice.Value)
_stopPrice = newStop;
}
}
}
else if (_breakevenTrigger.HasValue)
{
if (_isLongPosition)
{
if (close > _breakevenTrigger.Value)
{
_stopPrice = _entryPrice;
_breakevenTrigger = null;
}
}
else
{
if (close < _breakevenTrigger.Value)
{
_stopPrice = _entryPrice;
_breakevenTrigger = null;
}
}
}
if (_isLongPosition)
{
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
return;
}
if (_takePrice.HasValue && candle.HighPrice >= _takePrice.Value)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
}
}
else
{
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
return;
}
if (_takePrice.HasValue && candle.LowPrice <= _takePrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
}
}
}
private void EnterLong(decimal price)
// Configure protective levels immediately after a long entry.
{
BuyMarket(Volume);
_entryPrice = price;
_isLongPosition = true;
var stop = GetPipValue(StopLossPips);
var take = GetPipValue(TakeProfitPips);
var trail = GetPipValue(TrailingStopPips);
var step = GetPipValue(TrailingStepPips);
var breakeven = GetPipValue(BreakevenPips);
_stopPrice = StopLossPips > 0m ? price - stop : null;
_takePrice = TakeProfitPips > 0m ? price + take : null;
if (TakeProfitPips <= 0m && BreakevenPips > 0m)
_breakevenTrigger = price + breakeven;
else
_breakevenTrigger = null;
if (TakeProfitPips > 0m && TrailingStopPips > 0m && TrailingStepPips > 0m)
{
_trailingDistance = trail;
_trailingStep = step;
}
else
{
_trailingDistance = null;
_trailingStep = null;
}
}
private void EnterShort(decimal price)
// Configure protective levels immediately after a short entry.
{
SellMarket(Volume);
_entryPrice = price;
_isLongPosition = false;
var stop = GetPipValue(StopLossPips);
var take = GetPipValue(TakeProfitPips);
var trail = GetPipValue(TrailingStopPips);
var step = GetPipValue(TrailingStepPips);
var breakeven = GetPipValue(BreakevenPips);
_stopPrice = StopLossPips > 0m ? price + stop : null;
_takePrice = TakeProfitPips > 0m ? price - take : null;
if (TakeProfitPips <= 0m && BreakevenPips > 0m)
_breakevenTrigger = price - breakeven;
else
_breakevenTrigger = null;
if (TakeProfitPips > 0m && TrailingStopPips > 0m && TrailingStepPips > 0m)
{
_trailingDistance = trail;
_trailingStep = step;
}
else
{
_trailingDistance = null;
_trailingStep = null;
}
}
private bool CheckMacdPattern(bool isLong)
// MACD momentum pattern replicates the original conditional cascade.
{
var baseIndex = MacdBarShift;
var required = baseIndex + 8;
if (_macdCount <= required)
return false;
var v3 = _macdHistory[baseIndex + 3];
var v4 = _macdHistory[baseIndex + 4];
var v5 = _macdHistory[baseIndex + 5];
var v6 = _macdHistory[baseIndex + 6];
var v7 = _macdHistory[baseIndex + 7];
var v8 = _macdHistory[baseIndex + 8];
if (isLong)
return v3 > v4 && v4 > v5 && v5 >= 0m && v6 <= 0m && v6 > v7 && v7 > v8;
return v3 < v4 && v4 < v5 && v5 <= 0m && v6 >= 0m && v6 < v7 && v7 < v8;
}
private void PushValue(decimal[] buffer, ref int count, decimal value)
{
if (count < buffer.Length)
count += 1;
for (var i = count - 1; i > 0; i--)
{
buffer[i] = buffer[i - 1];
}
buffer[0] = value;
}
private bool TryGetHistoryValue(decimal[] buffer, int count, int index, out decimal value)
{
if (index < 0 || index >= count)
{
value = 0m;
return false;
}
value = buffer[index];
return true;
}
private decimal GetPipValue(decimal pips)
// Convert pip-based settings into price units using the instrument step.
{
var step = Security?.PriceStep ?? 0.0001m;
return pips * step * 10m;
}
private bool ShouldCloseForDay(ICandleMessage candle)
{
var time = candle.CloseTime;
var endHour = time.DayOfWeek == DayOfWeek.Friday ? 21 : 23;
return time.Hour >= endHour;
}
private void ClosePosition()
{
if (Position > 0)
{
SellMarket(Position);
}
else if (Position < 0)
{
BuyMarket(Math.Abs(Position));
}
ResetPositionState();
}
private void ResetPositionState()
// Reset cached trading state once the position has been closed.
{
_stopPrice = null;
_takePrice = null;
_breakevenTrigger = null;
_trailingDistance = null;
_trailingStep = null;
_isLongPosition = false;
_entryPrice = 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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (
SimpleMovingAverage, MovingAverageConvergenceDivergence, ExponentialMovingAverage
)
from StockSharp.Algo.Strategies import Strategy
class momo_trades_strategy(Strategy):
def __init__(self):
super(momo_trades_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 22)
self._ma_bar_shift = self.Param("MaBarShift", 6)
self._macd_fast = self.Param("MacdFast", 12)
self._macd_slow = self.Param("MacdSlow", 26)
self._macd_signal = self.Param("MacdSignal", 9)
self._macd_bar_shift = self.Param("MacdBarShift", 2)
self._stop_loss_pips = self.Param("StopLossPips", 25.0)
self._take_profit_pips = self.Param("TakeProfitPips", 0.0)
self._trailing_stop_pips = self.Param("TrailingStopPips", 0.0)
self._trailing_step_pips = self.Param("TrailingStepPips", 5.0)
self._breakeven_pips = self.Param("BreakevenPips", 10.0)
self._price_shift_pips = self.Param("PriceShiftPips", 5.0)
self._close_end_day = self.Param("CloseEndDay", True)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._sma = None
self._macd = None
self._macd_history = [0.0] * 64
self._ma_history = [0.0] * 64
self._close_history = [0.0] * 64
self._macd_count = 0
self._ma_count = 0
self._close_count = 0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._breakeven_trigger = None
self._trailing_distance = None
self._trailing_step = None
self._is_long = False
self._cooldown = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def SmaPeriod(self):
return self._sma_period.Value
@property
def MaBarShift(self):
return self._ma_bar_shift.Value
@property
def MacdFast(self):
return self._macd_fast.Value
@property
def MacdSlow(self):
return self._macd_slow.Value
@property
def MacdSignal(self):
return self._macd_signal.Value
@property
def MacdBarShift(self):
return self._macd_bar_shift.Value
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@property
def TrailingStopPips(self):
return self._trailing_stop_pips.Value
@property
def TrailingStepPips(self):
return self._trailing_step_pips.Value
@property
def BreakevenPips(self):
return self._breakeven_pips.Value
@property
def PriceShiftPips(self):
return self._price_shift_pips.Value
@property
def CloseEndDay(self):
return self._close_end_day.Value
def OnStarted2(self, time):
super(momo_trades_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = self.SmaPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.MacdSlow
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.MacdFast
self._macd = MovingAverageConvergenceDivergence(slow_ema, fast_ema)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._sma, self._macd, self._process_candle).Start()
def _process_candle(self, candle, sma_value, macd_value):
if candle.State != CandleStates.Finished:
return
if not self._sma.IsFormed or not self._macd.IsFormed:
return
self._push_value(self._close_history, float(candle.ClosePrice), "close")
self._push_value(self._ma_history, float(sma_value), "ma")
self._push_value(self._macd_history, float(macd_value), "macd")
self._manage_active_position(candle)
if self._cooldown > 0:
self._cooldown -= 1
return
if self.Position != 0:
return
if self.CloseEndDay and self._should_close_for_day(candle):
return
shift = self.MaBarShift
if self._close_count <= shift or self._ma_count <= shift:
return
shifted_close = self._close_history[shift]
shifted_ma = self._ma_history[shift]
price_shift = self._get_pip_value(self.PriceShiftPips)
ema_buy = shifted_close - shifted_ma > price_shift
ema_sell = shifted_ma - shifted_close > price_shift
macd_buy = self._check_macd_pattern(True)
macd_sell = self._check_macd_pattern(False)
if macd_buy and ema_buy:
self._enter_long(float(candle.ClosePrice))
self._cooldown = 5
elif macd_sell and ema_sell:
self._enter_short(float(candle.ClosePrice))
self._cooldown = 5
def _manage_active_position(self, candle):
if self.Position == 0:
return
if self.CloseEndDay and self._should_close_for_day(candle):
self._close_position()
return
close = float(candle.ClosePrice)
if self._trailing_distance is not None and self._trailing_step is not None:
if self._is_long:
if close - self._entry_price > self._trailing_distance + self._trailing_step:
new_stop = close - self._trailing_distance
if self._stop_price is None or new_stop > self._stop_price:
self._stop_price = new_stop
else:
if self._entry_price - close > self._trailing_distance + self._trailing_step:
new_stop = close + self._trailing_distance
if self._stop_price is None or new_stop < self._stop_price:
self._stop_price = new_stop
elif self._breakeven_trigger is not None:
if self._is_long:
if close > self._breakeven_trigger:
self._stop_price = self._entry_price
self._breakeven_trigger = None
else:
if close < self._breakeven_trigger:
self._stop_price = self._entry_price
self._breakeven_trigger = None
if self._is_long:
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
self._reset_position_state()
return
if self._take_price is not None and float(candle.HighPrice) >= self._take_price:
self.SellMarket()
self._reset_position_state()
else:
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
self._reset_position_state()
return
if self._take_price is not None and float(candle.LowPrice) <= self._take_price:
self.BuyMarket()
self._reset_position_state()
def _enter_long(self, price):
self.BuyMarket()
self._entry_price = price
self._is_long = True
stop = self._get_pip_value(self.StopLossPips)
take = self._get_pip_value(self.TakeProfitPips)
trail = self._get_pip_value(self.TrailingStopPips)
step = self._get_pip_value(self.TrailingStepPips)
breakeven = self._get_pip_value(self.BreakevenPips)
self._stop_price = price - stop if self.StopLossPips > 0 else None
self._take_price = price + take if self.TakeProfitPips > 0 else None
if self.TakeProfitPips <= 0 and self.BreakevenPips > 0:
self._breakeven_trigger = price + breakeven
else:
self._breakeven_trigger = None
if self.TakeProfitPips > 0 and self.TrailingStopPips > 0 and self.TrailingStepPips > 0:
self._trailing_distance = trail
self._trailing_step = step
else:
self._trailing_distance = None
self._trailing_step = None
def _enter_short(self, price):
self.SellMarket()
self._entry_price = price
self._is_long = False
stop = self._get_pip_value(self.StopLossPips)
take = self._get_pip_value(self.TakeProfitPips)
trail = self._get_pip_value(self.TrailingStopPips)
step = self._get_pip_value(self.TrailingStepPips)
breakeven = self._get_pip_value(self.BreakevenPips)
self._stop_price = price + stop if self.StopLossPips > 0 else None
self._take_price = price - take if self.TakeProfitPips > 0 else None
if self.TakeProfitPips <= 0 and self.BreakevenPips > 0:
self._breakeven_trigger = price - breakeven
else:
self._breakeven_trigger = None
if self.TakeProfitPips > 0 and self.TrailingStopPips > 0 and self.TrailingStepPips > 0:
self._trailing_distance = trail
self._trailing_step = step
else:
self._trailing_distance = None
self._trailing_step = None
def _check_macd_pattern(self, is_long):
base = self.MacdBarShift
required = base + 8
if self._macd_count <= required:
return False
v3 = self._macd_history[base + 3]
v4 = self._macd_history[base + 4]
v5 = self._macd_history[base + 5]
v6 = self._macd_history[base + 6]
v7 = self._macd_history[base + 7]
v8 = self._macd_history[base + 8]
if is_long:
return v3 > v4 and v4 > v5 and v5 >= 0 and v6 <= 0 and v6 > v7 and v7 > v8
return v3 < v4 and v4 < v5 and v5 <= 0 and v6 >= 0 and v6 < v7 and v7 < v8
def _push_value(self, buf, value, which):
length = len(buf)
if which == "close":
cnt = self._close_count
elif which == "ma":
cnt = self._ma_count
else:
cnt = self._macd_count
if cnt < length:
cnt += 1
for i in range(cnt - 1, 0, -1):
buf[i] = buf[i - 1]
buf[0] = value
if which == "close":
self._close_count = cnt
elif which == "ma":
self._ma_count = cnt
else:
self._macd_count = cnt
def _get_pip_value(self, pips):
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 0.0001
return pips * step * 10.0
def _should_close_for_day(self, candle):
t = candle.CloseTime
day = t.DayOfWeek
end_hour = 21 if day == 5 else 23
return t.Hour >= end_hour
def _close_position(self):
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._reset_position_state()
def _reset_position_state(self):
self._stop_price = None
self._take_price = None
self._breakeven_trigger = None
self._trailing_distance = None
self._trailing_step = None
self._is_long = False
self._entry_price = 0.0
def OnReseted(self):
super(momo_trades_strategy, self).OnReseted()
self._macd_history = [0.0] * 64
self._ma_history = [0.0] * 64
self._close_history = [0.0] * 64
self._macd_count = 0
self._ma_count = 0
self._close_count = 0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._breakeven_trigger = None
self._trailing_distance = None
self._trailing_step = None
self._is_long = False
self._cooldown = 0
def CreateClone(self):
return momo_trades_strategy()