Day Trading Impulse Strategy
Overview
The DayTrading Strategy is a faithful C# conversion of the classic MetaTrader 4 expert advisor "DayTrading" released by NazFunds in 2005. The original robot was designed for 5-minute Forex charts and combines multiple momentum and trend-following indicators to capture short-term directional moves with a modest fixed target and optional trailing stop. This StockSharp implementation reproduces the core decision logic while exposing every important threshold as a strategy parameter so that it can be optimized or adapted to different instruments.
Indicator Stack
The strategy evaluates four indicators on the selected candle series:
- Parabolic SAR (
ParabolicSar) with configurable acceleration, increment, and cap. It defines the baseline trend direction and has to flip below/above price to enable new entries.
- MACD (12, 26, 9) (
MovingAverageConvergenceDivergenceSignal). The MACD line must be below the signal line for longs and above it for shorts, mirroring the original histogram/signal comparison in MQL.
- Stochastic Oscillator (5, 3, 3) (
StochasticOscillator). The %K line must stay under 35 for longs and above 60 for shorts to ensure the market is coming out of an oversold/overbought zone.
- Momentum (14) (
Momentum). A value below 100 unlocks long trades, whereas a value above 100 authorizes shorts, exactly as in the MT4 script.
All indicators are processed through the high-level BindEx pipeline, so no manual buffer management or historical indexing is required.
Trading Rules
Entry Conditions
A long position is opened when all of the following are true on the latest finished candle:
- The Parabolic SAR dot prints at or below the current ask price and the previous dot was above the current dot (fresh SAR flip to bullish).
- Momentum is below 100.
- The MACD line is below its signal line.
- Stochastic %K is below 35.
A short position is opened when the symmetric conditions are satisfied:
- The Parabolic SAR dot prints at or above the current bid price and the previous dot was below the current dot (bearish flip).
- Momentum is above 100.
- The MACD line is above its signal line.
- Stochastic %K is above 60.
Only one position can be open at a time. Whenever an opposite signal appears, the existing position is closed and no re-entry happens on the same candle—just like in the MetaTrader implementation where the OrdersTotal scan prevents immediate reloading.
Exit Management
- Stop Loss / Take Profit: Optional fixed distances (in points) are converted to absolute prices using the instrument's tick size. They are re-evaluated on every candle and close the position if breached intrabar.
- Trailing Stop: Once price advances by the configured number of points, a trailing stop is activated. For long trades the stop trails below the close; for short trades it trails above the close. The stop never steps backwards, so profit is locked progressively.
- Opposite Signal: A valid opposite setup immediately liquidates the current position before any new entry is considered.
No additional grid, scaling, or hedging logic is added; the strategy stays as lightweight and deterministic as the original EA.
Parameters
| Parameter |
Default |
Description |
LotSize |
1 |
Volume of each market order. The Strategy.Volume property is synchronized to this value during start-up. |
TrailingStopPoints |
15 |
Trailing distance in points. Set to zero to disable trailing. |
TakeProfitPoints |
20 |
Fixed take-profit distance in points. Set to zero to remove the target. |
StopLossPoints |
0 |
Protective stop distance in points. Zero reproduces the original "no stop" behaviour. |
SlippagePoints |
3 |
Maximum execution slippage placeholder (for compatibility with the MT4 input). Not enforced automatically but kept for completeness. |
CandleType |
5-minute time frame |
Candle series used by all indicators. Keep at M5 to match the EA's original recommendation. |
MacdFastPeriod |
12 |
Fast EMA length in the MACD calculation. |
MacdSlowPeriod |
26 |
Slow EMA length in the MACD calculation. |
MacdSignalPeriod |
9 |
Signal EMA length in the MACD calculation. |
StochasticLength |
5 |
%K look-back length for the Stochastic Oscillator. |
StochasticSignal |
3 |
%D smoothing length. |
StochasticSlow |
3 |
Additional slowing applied to the %K line. |
MomentumPeriod |
14 |
Momentum look-back length. |
SarAcceleration |
0.02 |
Initial acceleration factor for Parabolic SAR. |
SarStep |
0.02 |
Increment applied to the acceleration factor after each new extreme. |
SarMaximum |
0.2 |
Maximum acceleration factor for Parabolic SAR. |
All numeric parameters can be optimized through StockSharp's optimization workflow thanks to the SetCanOptimize(true) hints.
Implementation Notes
- Bid/ask prices are derived from live Level1 data when available; otherwise the candle close acts as a fallback so that the logic remains robust in historical testing.
- Point conversion relies on the instrument's
Step/PriceStep. If none is provided a conservative 0.0001 fallback is used, which matches a standard Forex pip.
- Position management mirrors the MT4 EA: the strategy never pyramids and never holds both directions simultaneously.
- Comments inside the code are in English per project guidelines, while this README includes extended documentation for easier onboarding.
Usage Tips
- Assign the desired Forex pair to the strategy, leave the candle type at 5 minutes, and start the strategy. The indicators will warm up automatically.
- Consider enabling a non-zero stop loss when running on live data—the original script recommended trading without it, but trailing stops alone may not be sufficient for risk control.
- For algorithmic portfolios you can add this strategy to a
BasketStrategy and manage capital allocation externally while still benefiting from the exposed parameters for optimization.
This documentation, along with the Russian and Chinese translations in the same folder, provides full transparency of the converted logic.
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()