Estratégia de Impulso de Day Trading
Visão geral
A Estratégia DayTrading é uma conversão C# fiel do clássico MetaTrader 4 consultor especialista "DayTrading" lançado pela NazFunds em 2005. O robô original foi projetado para gráficos Forex de 5 minutos e combina vários indicadores de impulso e acompanhamento de tendências para capturar movimentos direcionais de curto prazo com um alvo fixo modesto e um trailing stop opcional. Esta implementação StockSharp reproduz a lógica de decisão central enquanto expõe cada limite importante como um parâmetro de estratégia para que possa ser otimizado ou adaptado a diferentes instrumentos.
Pilha de Indicadores
A estratégia avalia quatro indicadores na série de velas selecionada:
- Parabolic SAR (
ParabolicSar) com aceleração, incremento e limite configuráveis. Ele define a direção da tendência da linha de base e precisa virar abaixo/acima do preço para permitir novas entradas.
- MACD (12, 26, 9) (
MovingAverageConvergenceDivergenceSignal). A linha MACD deve estar abaixo da linha de sinal para posições compradas e acima dela para posições vendidas, refletindo a comparação original do histograma/sinal em MQL.
- Stochastic Oscilador (5, 3, 3) (
StochasticOscillator). A linha %K deve permanecer abaixo de 35 para posições longas e acima de 60 para posições curtas para garantir que o mercado esteja saindo de uma zona de sobrevenda/sobrecompra.
- Momentum (14) (
Momentum). Um valor abaixo de 100 desbloqueia negociações longas, enquanto um valor acima de 100 autoriza operações curtas, exatamente como no script MT4.
Todos os indicadores são processados por meio do pipeline BindEx de alto nível, portanto, nenhum gerenciamento manual de buffer ou indexação histórica é necessário.
Regras de negociação
Condições de Entrada
Uma posição longa é aberta quando todas as afirmações a seguir são verdadeiras na última vela finalizada:
- O ponto Parabolic SAR é impresso no preço de venda atual ou abaixo dele e o ponto anterior estava acima do ponto atual (nova SAR mudança para alta).
- O impulso está abaixo de 100.
- A linha MACD está abaixo de sua linha de sinal.
- Stochastic %K está abaixo de 35.
Uma posição curta é aberta quando as condições simétricas são satisfeitas:
- O ponto Parabolic SAR é impresso no preço de oferta atual ou acima dele e o ponto anterior estava abaixo do ponto atual (inversão de baixa).
- O impulso está acima de 100.
- A linha MACD está acima de sua linha de sinal.
- Stochastic %K está acima de 60.
Apenas uma posição pode ser aberta por vez. Sempre que um sinal oposto aparece, a posição existente é fechada e nenhuma reentrada acontece na mesma vela - assim como na implementação MetaTrader onde a varredura OrdersTotal impede a recarga imediata.
Gerenciamento de saída
- Stop Loss/Take Profit: Distâncias fixas opcionais (em pontos) são convertidas em preços absolutos usando o tamanho do tick do instrumento. Eles são reavaliados em cada vela e fecham a posição se a intrabar for violada.
- Trailing Stop: Quando o preço avança pelo número de pontos configurado, um trailing stop é ativado. Para negociações longas, o stop fica abaixo do fechamento; para negociações curtas, ele fica acima do fechamento. O stop nunca recua, portanto o lucro é bloqueado progressivamente.
- Sinal Oposto: Uma configuração oposta válida liquida imediatamente a posição atual antes que qualquer nova entrada seja considerada.
Nenhuma lógica adicional de grade, escala ou cobertura é adicionada; a estratégia permanece tão leve e determinística quanto o EA original.
Parâmetros
| Parâmetro |
Padrão |
Descrição |
LotSize |
1 |
Volume de cada ordem de mercado. A propriedade Strategy.Volume é sincronizada com este valor durante a inicialização. |
TrailingStopPoints |
15 |
Distância final em pontos. Defina como zero para desativar o rastreamento. |
TakeProfitPoints |
20 |
Distância fixa de lucro em pontos. Defina como zero para remover o alvo. |
StopLossPoints |
0 |
Distância de parada protetora em pontos. Zero reproduz o comportamento original de "sem parada". |
SlippagePoints |
3 |
Espaço reservado para deslizamento máximo de execução (para compatibilidade com a entrada MT4). Não aplicado automaticamente, mas mantido para fins de integridade. |
CandleType |
Período de 5 minutos |
Série de velas usada por todos os indicadores. Mantenha-se em M5 para corresponder à recomendação original do EA. |
MacdFastPeriod |
12 |
Comprimento EMA rápido no cálculo de MACD. |
MacdSlowPeriod |
26 |
Comprimento EMA lento no cálculo MACD. |
MacdSignalPeriod |
9 |
Comprimento do sinal EMA no cálculo MACD. |
StochasticLength |
5 |
%K comprimento de lookback para o oscilador Stochastic. |
StochasticSignal |
3 |
%D comprimento de suavização. |
StochasticSlow |
3 |
Desaceleração adicional aplicada à linha %K. |
MomentumPeriod |
14 |
Comprimento retrospectivo do momento. |
SarAcceleration |
0,02 |
Fator de aceleração inicial para Parabolic SAR. |
SarStep |
0,02 |
Incremento aplicado ao fator de aceleração após cada novo extremo. |
SarMaximum |
0,2 |
Fator de aceleração máximo para Parabolic SAR. |
Todos os parâmetros numéricos podem ser otimizados por meio do fluxo de trabalho de otimização do StockSharp graças às dicas do SetCanOptimize(true).
Notas de implementação
- Os preços de compra/venda são derivados de dados em tempo real do Nível 1, quando disponíveis; caso contrário, o fechamento da vela atua como um substituto para que a lógica permaneça robusta nos testes históricos.
- A conversão de pontos depende do
Step/PriceStep do instrumento. Se nenhum for fornecido, um substituto conservador 0.0001 será usado, que corresponde a um pip Forex padrão.
- O gerenciamento de posição reflete o MT4 EA: a estratégia nunca forma pirâmide e nunca mantém as duas direções simultaneamente.
- Os comentários dentro do código estão em inglês de acordo com as diretrizes do projeto, enquanto este README inclui documentação estendida para facilitar a integração.
Dicas de uso
- Atribua o par Forex desejado à estratégia, deixe o tipo de vela em 5 minutos e inicie a estratégia. Os indicadores aquecerão automaticamente.
- Considere ativar um stop loss diferente de zero ao executar dados em tempo real – o script original recomendava negociar sem ele, mas os trailing stops por si só podem não ser suficientes para o controle de risco.
- Para portfólios algorítmicos, você pode adicionar esta estratégia a um
BasketStrategy e gerenciar a alocação de capital externamente enquanto ainda se beneficia dos parâmetros expostos para otimização.
Esta documentação, juntamente com as traduções para russo e chinês na mesma pasta, fornece total transparência da lógica convertida.
namespace StockSharp.Samples.Strategies;
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;
/// <summary>
/// Intraday trend strategy converted from the MetaTrader "DayTrading" expert advisor.
/// Combines Parabolic SAR, MACD, Stochastic and Momentum filters with trailing exits.
/// </summary>
public class DayTradingImpulseStrategy : Strategy
{
private readonly StrategyParam<decimal> _lotSize;
private readonly StrategyParam<decimal> _trailingStopPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _slippagePoints;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _macdFastPeriod;
private readonly StrategyParam<int> _macdSlowPeriod;
private readonly StrategyParam<int> _macdSignalPeriod;
private readonly StrategyParam<int> _stochasticLength;
private readonly StrategyParam<int> _stochasticSignal;
private readonly StrategyParam<int> _stochasticSlow;
private readonly StrategyParam<decimal> _stochasticBuyThreshold;
private readonly StrategyParam<decimal> _stochasticSellThreshold;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<decimal> _momentumNeutralLevel;
private readonly StrategyParam<decimal> _sarAcceleration;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMaximum;
private ParabolicSar _parabolicSar = null!;
private MovingAverageConvergenceDivergenceSignal _macd = null!;
private StochasticOscillator _stochastic = null!;
private Momentum _momentum = null!;
private decimal? _previousSar;
private decimal? _longStopPrice;
private decimal? _shortStopPrice;
private decimal? _longTakeProfit;
private decimal? _shortTakeProfit;
private decimal? _longEntryPrice;
private decimal? _shortEntryPrice;
private decimal _pointSize;
/// <summary>
/// Initializes a new instance of <see cref="DayTradingImpulseStrategy"/>.
/// </summary>
public DayTradingImpulseStrategy()
{
_lotSize = Param(nameof(LotSize), 1m)
.SetGreaterThanZero()
.SetDisplay("Order Volume", "Trade volume used for each market entry", "Trading")
;
_trailingStopPoints = Param(nameof(TrailingStopPoints), 15m)
.SetNotNegative()
.SetDisplay("Trailing Stop (points)", "Distance used to trail profitable positions", "Risk")
;
_takeProfitPoints = Param(nameof(TakeProfitPoints), 20m)
.SetNotNegative()
.SetDisplay("Take Profit (points)", "Fixed profit target measured in points", "Risk")
;
_stopLossPoints = Param(nameof(StopLossPoints), 0m)
.SetNotNegative()
.SetDisplay("Stop Loss (points)", "Protective stop distance measured in points", "Risk")
;
_slippagePoints = Param(nameof(SlippagePoints), 3m)
.SetNotNegative()
.SetDisplay("Slippage (points)", "Maximum acceptable execution slippage", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Time frame used for indicator calculations", "Data");
_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Length of the fast EMA in MACD", "Indicators")
;
_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Length of the slow EMA in MACD", "Indicators")
;
_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Length of the MACD signal EMA", "Indicators")
;
_stochasticLength = Param(nameof(StochasticLength), 5)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
;
_stochasticSignal = Param(nameof(StochasticSignal), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "Period of the %D smoothing", "Indicators")
;
_stochasticSlow = Param(nameof(StochasticSlow), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic Slowing", "Final smoothing applied to %K", "Indicators")
;
_stochasticBuyThreshold = Param(nameof(StochasticBuyThreshold), 35m)
.SetDisplay("Stochastic Buy", "Oversold %K threshold for long entries", "Indicators")
;
_stochasticSellThreshold = Param(nameof(StochasticSellThreshold), 60m)
.SetDisplay("Stochastic Sell", "Overbought %K threshold for short entries", "Indicators")
;
_momentumPeriod = Param(nameof(MomentumPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Number of candles used for Momentum", "Indicators")
;
_momentumNeutralLevel = Param(nameof(MomentumNeutralLevel), 100m)
.SetDisplay("Momentum Neutral", "Neutral momentum value used for signal confirmation", "Indicators")
;
_sarAcceleration = Param(nameof(SarAcceleration), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Acceleration", "Initial acceleration factor of Parabolic SAR", "Indicators")
;
_sarStep = Param(nameof(SarStep), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Step", "Increment applied to the acceleration factor", "Indicators")
;
_sarMaximum = Param(nameof(SarMaximum), 0.2m)
.SetGreaterThanZero()
.SetDisplay("SAR Maximum", "Maximum acceleration factor of Parabolic SAR", "Indicators")
;
}
/// <summary>
/// Trade volume used for each market entry.
/// </summary>
public decimal LotSize
{
get => _lotSize.Value;
set => _lotSize.Value = value;
}
/// <summary>
/// Distance used to trail profitable positions.
/// </summary>
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
/// <summary>
/// Fixed profit target measured in points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Protective stop distance measured in points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Maximum acceptable execution slippage.
/// </summary>
public decimal SlippagePoints
{
get => _slippagePoints.Value;
set => _slippagePoints.Value = value;
}
/// <summary>
/// Time frame used for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Length of the fast EMA in MACD.
/// </summary>
public int MacdFastPeriod
{
get => _macdFastPeriod.Value;
set => _macdFastPeriod.Value = value;
}
/// <summary>
/// Length of the slow EMA in MACD.
/// </summary>
public int MacdSlowPeriod
{
get => _macdSlowPeriod.Value;
set => _macdSlowPeriod.Value = value;
}
/// <summary>
/// Length of the MACD signal EMA.
/// </summary>
public int MacdSignalPeriod
{
get => _macdSignalPeriod.Value;
set => _macdSignalPeriod.Value = value;
}
/// <summary>
/// Period of the %K line.
/// </summary>
public int StochasticLength
{
get => _stochasticLength.Value;
set => _stochasticLength.Value = value;
}
/// <summary>
/// Period of the %D smoothing.
/// </summary>
public int StochasticSignal
{
get => _stochasticSignal.Value;
set => _stochasticSignal.Value = value;
}
/// <summary>
/// Final smoothing applied to %K.
/// </summary>
public int StochasticSlow
{
get => _stochasticSlow.Value;
set => _stochasticSlow.Value = value;
}
/// <summary>
/// Stochastic %K level that qualifies oversold conditions.
/// </summary>
public decimal StochasticBuyThreshold
{
get => _stochasticBuyThreshold.Value;
set => _stochasticBuyThreshold.Value = value;
}
/// <summary>
/// Stochastic %K level that qualifies overbought conditions.
/// </summary>
public decimal StochasticSellThreshold
{
get => _stochasticSellThreshold.Value;
set => _stochasticSellThreshold.Value = value;
}
/// <summary>
/// Number of candles used for Momentum.
/// </summary>
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
/// <summary>
/// Momentum value considered neutral for trend confirmation.
/// </summary>
public decimal MomentumNeutralLevel
{
get => _momentumNeutralLevel.Value;
set => _momentumNeutralLevel.Value = value;
}
/// <summary>
/// Initial acceleration factor of Parabolic SAR.
/// </summary>
public decimal SarAcceleration
{
get => _sarAcceleration.Value;
set => _sarAcceleration.Value = value;
}
/// <summary>
/// Increment applied to the acceleration factor.
/// </summary>
public decimal SarStep
{
get => _sarStep.Value;
set => _sarStep.Value = value;
}
/// <summary>
/// Maximum acceleration factor of Parabolic SAR.
/// </summary>
public decimal SarMaximum
{
get => _sarMaximum.Value;
set => _sarMaximum.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousSar = null;
_longStopPrice = null;
_shortStopPrice = null;
_longTakeProfit = null;
_shortTakeProfit = null;
_longEntryPrice = null;
_shortEntryPrice = null;
_pointSize = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = LotSize;
_pointSize = CalculatePointSize();
_parabolicSar = new ParabolicSar
{
Acceleration = SarAcceleration,
AccelerationStep = SarStep,
AccelerationMax = SarMaximum,
};
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFastPeriod },
LongMa = { Length = MacdSlowPeriod },
},
SignalMa = { Length = MacdSignalPeriod },
};
_stochastic = new StochasticOscillator();
_stochastic.K.Length = StochasticLength;
_stochastic.D.Length = StochasticSignal;
_momentum = new Momentum
{
Length = MomentumPeriod,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_parabolicSar, _macd, _stochastic, _momentum, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _parabolicSar);
DrawIndicator(area, _macd);
DrawIndicator(area, _stochastic);
DrawIndicator(area, _momentum);
DrawOwnTrades(area);
}
}
private void ProcessCandle(
ICandleMessage candle,
IIndicatorValue sarValue,
IIndicatorValue macdValue,
IIndicatorValue stochasticValue,
IIndicatorValue momentumValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!sarValue.IsFinal || !macdValue.IsFinal || !stochasticValue.IsFinal || !momentumValue.IsFinal)
return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macd)
return;
if (stochasticValue is not StochasticOscillatorValue stochastic)
return;
var sar = sarValue.ToDecimal();
var previousSar = _previousSar;
_previousSar = sar;
if (previousSar is null)
return;
var momentum = momentumValue.ToDecimal();
var ask = GetAskPrice(candle);
var bid = GetBidPrice(candle);
var buySignal = sar <= ask && previousSar.Value > sar && momentum < MomentumNeutralLevel &&
macd.Macd < macd.Signal && stochastic.K < StochasticBuyThreshold;
var sellSignal = sar >= bid && previousSar.Value < sar && momentum > MomentumNeutralLevel &&
macd.Macd > macd.Signal && stochastic.K > StochasticSellThreshold;
var closedPosition = false;
if (Position > 0)
{
if (sellSignal)
{
SellMarket(Math.Abs(Position));
ResetLongState();
closedPosition = true;
}
else if (HandleLongRisk(candle))
{
closedPosition = true;
}
}
else if (Position < 0)
{
if (buySignal)
{
BuyMarket(Math.Abs(Position));
ResetShortState();
closedPosition = true;
}
else if (HandleShortRisk(candle))
{
closedPosition = true;
}
}
if (closedPosition)
return;
if (Position == 0)
{
if (buySignal)
{
var entryPrice = ask;
BuyMarket(Volume);
_longEntryPrice = entryPrice;
_longStopPrice = StopLossPoints > 0m ? entryPrice - ConvertPoints(StopLossPoints) : null;
_longTakeProfit = TakeProfitPoints > 0m ? entryPrice + ConvertPoints(TakeProfitPoints) : null;
}
else if (sellSignal)
{
var entryPrice = bid;
SellMarket(Volume);
_shortEntryPrice = entryPrice;
_shortStopPrice = StopLossPoints > 0m ? entryPrice + ConvertPoints(StopLossPoints) : null;
_shortTakeProfit = TakeProfitPoints > 0m ? entryPrice - ConvertPoints(TakeProfitPoints) : null;
}
}
}
private bool HandleLongRisk(ICandleMessage candle)
{
if (Math.Abs(Position) <= 0m)
return false;
if (_longTakeProfit is decimal takeProfit && candle.HighPrice >= takeProfit)
{
SellMarket(Math.Abs(Position));
ResetLongState();
return true;
}
if (_longStopPrice is decimal stop && candle.LowPrice <= stop)
{
SellMarket(Math.Abs(Position));
ResetLongState();
return true;
}
var trailingDistance = ConvertPoints(TrailingStopPoints);
if (trailingDistance > 0m && _longEntryPrice is decimal entry)
{
var progressed = candle.HighPrice - entry;
if (progressed >= trailingDistance)
{
var candidate = candle.ClosePrice - trailingDistance;
if (!_longStopPrice.HasValue || candidate > _longStopPrice.Value)
_longStopPrice = candidate;
}
}
return false;
}
private bool HandleShortRisk(ICandleMessage candle)
{
if (Math.Abs(Position) <= 0m)
return false;
if (_shortTakeProfit is decimal takeProfit && candle.LowPrice <= takeProfit)
{
BuyMarket(Math.Abs(Position));
ResetShortState();
return true;
}
if (_shortStopPrice is decimal stop && candle.HighPrice >= stop)
{
BuyMarket(Math.Abs(Position));
ResetShortState();
return true;
}
var trailingDistance = ConvertPoints(TrailingStopPoints);
if (trailingDistance > 0m && _shortEntryPrice is decimal entry)
{
var progressed = entry - candle.LowPrice;
if (progressed >= trailingDistance)
{
var candidate = candle.ClosePrice + trailingDistance;
if (!_shortStopPrice.HasValue || candidate < _shortStopPrice.Value)
_shortStopPrice = candidate;
}
}
return false;
}
private void ResetLongState()
{
_longEntryPrice = null;
_longStopPrice = null;
_longTakeProfit = null;
}
private void ResetShortState()
{
_shortEntryPrice = null;
_shortStopPrice = null;
_shortTakeProfit = null;
}
private decimal GetBidPrice(ICandleMessage candle)
{
return candle.ClosePrice;
}
private decimal GetAskPrice(ICandleMessage candle)
{
return candle.ClosePrice;
}
private decimal ConvertPoints(decimal points)
{
if (points <= 0m)
return 0m;
if (_pointSize > 0m)
return points * _pointSize;
var step = Security?.PriceStep ?? 0m;
return step > 0m ? points * step : points;
}
private decimal CalculatePointSize()
{
var step = Security?.PriceStep ?? 0m;
return step > 0m ? step : 0.0001m;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import (
ParabolicSar,
MovingAverageConvergenceDivergenceSignal,
StochasticOscillator,
Momentum,
)
class day_trading_impulse_strategy(Strategy):
def __init__(self):
super(day_trading_impulse_strategy, self).__init__()
self._lot_size = self.Param("LotSize", 1.0) \
.SetDisplay("Order Volume", "Trade volume used for each market entry", "Trading")
self._trailing_stop_points = self.Param("TrailingStopPoints", 15.0) \
.SetDisplay("Trailing Stop (points)", "Distance used to trail profitable positions", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 20.0) \
.SetDisplay("Take Profit (points)", "Fixed profit target measured in points", "Risk")
self._stop_loss_points = self.Param("StopLossPoints", 0.0) \
.SetDisplay("Stop Loss (points)", "Protective stop distance measured in points", "Risk")
self._slippage_points = self.Param("SlippagePoints", 3.0) \
.SetDisplay("Slippage (points)", "Maximum acceptable execution slippage", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Time frame used for indicator calculations", "Data")
self._macd_fast_period = self.Param("MacdFastPeriod", 12) \
.SetDisplay("MACD Fast", "Length of the fast EMA in MACD", "Indicators")
self._macd_slow_period = self.Param("MacdSlowPeriod", 26) \
.SetDisplay("MACD Slow", "Length of the slow EMA in MACD", "Indicators")
self._macd_signal_period = self.Param("MacdSignalPeriod", 9) \
.SetDisplay("MACD Signal", "Length of the MACD signal EMA", "Indicators")
self._stochastic_length = self.Param("StochasticLength", 5) \
.SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
self._stochastic_signal = self.Param("StochasticSignal", 3) \
.SetDisplay("Stochastic %D", "Period of the %D smoothing", "Indicators")
self._stochastic_slow = self.Param("StochasticSlow", 3) \
.SetDisplay("Stochastic Slowing", "Final smoothing applied to %K", "Indicators")
self._stochastic_buy_threshold = self.Param("StochasticBuyThreshold", 35.0) \
.SetDisplay("Stochastic Buy", "Oversold %K threshold for long entries", "Indicators")
self._stochastic_sell_threshold = self.Param("StochasticSellThreshold", 60.0) \
.SetDisplay("Stochastic Sell", "Overbought %K threshold for short entries", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetDisplay("Momentum Period", "Number of candles used for Momentum", "Indicators")
self._momentum_neutral_level = self.Param("MomentumNeutralLevel", 100.0) \
.SetDisplay("Momentum Neutral", "Neutral momentum value used for signal confirmation", "Indicators")
self._sar_acceleration = self.Param("SarAcceleration", 0.02) \
.SetDisplay("SAR Acceleration", "Initial acceleration factor of Parabolic SAR", "Indicators")
self._sar_step = self.Param("SarStep", 0.02) \
.SetDisplay("SAR Step", "Increment applied to the acceleration factor", "Indicators")
self._sar_maximum = self.Param("SarMaximum", 0.2) \
.SetDisplay("SAR Maximum", "Maximum acceleration factor of Parabolic SAR", "Indicators")
self._parabolic_sar = None
self._macd = None
self._stochastic = None
self._momentum = None
self._previous_sar = None
self._long_stop_price = None
self._short_stop_price = None
self._long_take_profit = None
self._short_take_profit = None
self._long_entry_price = None
self._short_entry_price = None
self._point_size = 0.0
@property
def LotSize(self):
return self._lot_size.Value
@property
def TrailingStopPoints(self):
return self._trailing_stop_points.Value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@property
def SlippagePoints(self):
return self._slippage_points.Value
@property
def CandleType(self):
return self._candle_type.Value
@property
def MacdFastPeriod(self):
return self._macd_fast_period.Value
@property
def MacdSlowPeriod(self):
return self._macd_slow_period.Value
@property
def MacdSignalPeriod(self):
return self._macd_signal_period.Value
@property
def StochasticLength(self):
return self._stochastic_length.Value
@property
def StochasticSignal(self):
return self._stochastic_signal.Value
@property
def StochasticSlow(self):
return self._stochastic_slow.Value
@property
def StochasticBuyThreshold(self):
return self._stochastic_buy_threshold.Value
@property
def StochasticSellThreshold(self):
return self._stochastic_sell_threshold.Value
@property
def MomentumPeriod(self):
return self._momentum_period.Value
@property
def MomentumNeutralLevel(self):
return self._momentum_neutral_level.Value
@property
def SarAcceleration(self):
return self._sar_acceleration.Value
@property
def SarStep(self):
return self._sar_step.Value
@property
def SarMaximum(self):
return self._sar_maximum.Value
def _calculate_point_size(self):
step = 0.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
return step if step > 0 else 0.0001
def _convert_points(self, points):
pts = float(points)
if pts <= 0:
return 0.0
if self._point_size > 0:
return pts * self._point_size
step = 0.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
return pts * step if step > 0 else pts
def OnStarted2(self, time):
super(day_trading_impulse_strategy, self).OnStarted2(time)
self.Volume = float(self.LotSize)
self._point_size = self._calculate_point_size()
self._parabolic_sar = ParabolicSar()
self._parabolic_sar.Acceleration = float(self.SarAcceleration)
self._parabolic_sar.AccelerationStep = float(self.SarStep)
self._parabolic_sar.AccelerationMax = float(self.SarMaximum)
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = self.MacdFastPeriod
self._macd.Macd.LongMa.Length = self.MacdSlowPeriod
self._macd.SignalMa.Length = self.MacdSignalPeriod
self._stochastic = StochasticOscillator()
self._stochastic.K.Length = self.StochasticLength
self._stochastic.D.Length = self.StochasticSignal
self._momentum = Momentum()
self._momentum.Length = self.MomentumPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._parabolic_sar, self._macd, self._stochastic, self._momentum, self.ProcessCandle).Start()
def ProcessCandle(self, candle, sar_value, macd_value, stochastic_value, momentum_value):
if candle.State != CandleStates.Finished:
return
if not sar_value.IsFinal or not macd_value.IsFinal or not stochastic_value.IsFinal or not momentum_value.IsFinal:
return
try:
sar = float(sar_value)
except:
sar = float(sar_value.Value)
previous_sar = self._previous_sar
self._previous_sar = sar
if previous_sar is None:
return
try:
mom = float(momentum_value)
except:
mom = float(momentum_value.Value)
close_price = float(candle.ClosePrice)
macd_raw = macd_value.Macd if hasattr(macd_value, 'Macd') else None
signal_raw = macd_value.Signal if hasattr(macd_value, 'Signal') else None
macd_val = float(macd_raw) if macd_raw is not None else 0.0
signal_val = float(signal_raw) if signal_raw is not None else 0.0
stoch_k_raw = stochastic_value.K if hasattr(stochastic_value, 'K') else None
stoch_k = float(stoch_k_raw) if stoch_k_raw is not None else 0.0
mom_neutral = float(self.MomentumNeutralLevel)
stoch_buy = float(self.StochasticBuyThreshold)
stoch_sell = float(self.StochasticSellThreshold)
buy_signal = sar <= close_price and previous_sar > sar and mom < mom_neutral and macd_val < signal_val and stoch_k < stoch_buy
sell_signal = sar >= close_price and previous_sar < sar and mom > mom_neutral and macd_val > signal_val and stoch_k > stoch_sell
closed_position = False
if self.Position > 0:
if sell_signal:
self.SellMarket(Math.Abs(self.Position))
self._reset_long_state()
closed_position = True
elif self._handle_long_risk(candle):
closed_position = True
elif self.Position < 0:
if buy_signal:
self.BuyMarket(Math.Abs(self.Position))
self._reset_short_state()
closed_position = True
elif self._handle_short_risk(candle):
closed_position = True
if closed_position:
return
if self.Position == 0:
if buy_signal:
entry_price = close_price
self.BuyMarket(self.Volume)
self._long_entry_price = entry_price
sl = float(self.StopLossPoints)
tp = float(self.TakeProfitPoints)
self._long_stop_price = entry_price - self._convert_points(sl) if sl > 0 else None
self._long_take_profit = entry_price + self._convert_points(tp) if tp > 0 else None
elif sell_signal:
entry_price = close_price
self.SellMarket(self.Volume)
self._short_entry_price = entry_price
sl = float(self.StopLossPoints)
tp = float(self.TakeProfitPoints)
self._short_stop_price = entry_price + self._convert_points(sl) if sl > 0 else None
self._short_take_profit = entry_price - self._convert_points(tp) if tp > 0 else None
def _handle_long_risk(self, candle):
if Math.Abs(self.Position) <= 0:
return False
if self._long_take_profit is not None and float(candle.HighPrice) >= self._long_take_profit:
self.SellMarket(Math.Abs(self.Position))
self._reset_long_state()
return True
if self._long_stop_price is not None and float(candle.LowPrice) <= self._long_stop_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_long_state()
return True
trailing_distance = self._convert_points(self.TrailingStopPoints)
if trailing_distance > 0 and self._long_entry_price is not None:
progressed = float(candle.HighPrice) - self._long_entry_price
if progressed >= trailing_distance:
candidate = float(candle.ClosePrice) - trailing_distance
if self._long_stop_price is None or candidate > self._long_stop_price:
self._long_stop_price = candidate
return False
def _handle_short_risk(self, candle):
if Math.Abs(self.Position) <= 0:
return False
if self._short_take_profit is not None and float(candle.LowPrice) <= self._short_take_profit:
self.BuyMarket(Math.Abs(self.Position))
self._reset_short_state()
return True
if self._short_stop_price is not None and float(candle.HighPrice) >= self._short_stop_price:
self.BuyMarket(Math.Abs(self.Position))
self._reset_short_state()
return True
trailing_distance = self._convert_points(self.TrailingStopPoints)
if trailing_distance > 0 and self._short_entry_price is not None:
progressed = self._short_entry_price - float(candle.LowPrice)
if progressed >= trailing_distance:
candidate = float(candle.ClosePrice) + trailing_distance
if self._short_stop_price is None or candidate < self._short_stop_price:
self._short_stop_price = candidate
return False
def _reset_long_state(self):
self._long_entry_price = None
self._long_stop_price = None
self._long_take_profit = None
def _reset_short_state(self):
self._short_entry_price = None
self._short_stop_price = None
self._short_take_profit = None
def OnReseted(self):
super(day_trading_impulse_strategy, self).OnReseted()
self._parabolic_sar = None
self._macd = None
self._stochastic = None
self._momentum = None
self._previous_sar = None
self._long_stop_price = None
self._short_stop_price = None
self._long_take_profit = None
self._short_take_profit = None
self._long_entry_price = None
self._short_entry_price = None
self._point_size = 0.0
def CreateClone(self):
return day_trading_impulse_strategy()