Стратегия Day Trading Impulse
Общее описание
DayTrading Strategy — это точная конвертация советника MetaTrader 4 «DayTrading», опубликованного компанией NazFunds в 2005 году. Алгоритм рассчитан на пятиминутные графики Forex и объединяет несколько трендовых и моментум-индикаторов, чтобы брать краткосрочные импульсы с небольшим тейк-профитом и при необходимости сопровождать сделку трейлинг-стопом. Версия на StockSharp полностью повторяет логику MQL-скрипта и одновременно делает ключевые параметры доступными для оптимизации.
Набор индикаторов
Для выбранной свечной серии рассчитываются четыре индикатора:
- Parabolic SAR (
ParabolicSar) с настраиваемыми коэффициентами ускорения, шага и максимума. Индикатор определяет базовый тренд и должен сменить сторону относительно цены, чтобы разрешить новую сделку.
- MACD (12, 26, 9) (
MovingAverageConvergenceDivergenceSignal). Линия MACD должна находиться ниже сигнальной для покупок и выше для продаж, что соответствует сравнению гистограммы и сигнальной линии в оригинальном советнике.
- Стохастик (5, 3, 3) (
StochasticOscillator). Линия %K должна быть ниже 35 для лонгов и выше 60 для шортов, подтверждая выход из зоны перепроданности/перекупленности.
- Momentum (14) (
Momentum). Значение ниже 100 разрешает покупки, выше 100 — продажи.
Все индикаторы подключены через высокоуровневый метод BindEx, поэтому нет необходимости вручную управлять буферами или обращаться к прошлым значениям.
Правила торговли
Условия входа
Лонг открывается на последней сформированной свече, если выполняются условия:
- Точка Parabolic SAR находится на уровне или ниже текущей цены ask, а предыдущая точка была выше текущей (свежий разворот SAR вверх).
- Momentum < 100.
- Линия MACD ниже сигнальной.
- Стохастик %K < 35.
Шорт открывается зеркально:
- Точка Parabolic SAR находится на уровне или выше текущей цены bid, а предыдущая точка была ниже текущей (разворот вниз).
- Momentum > 100.
- Линия MACD выше сигнальной.
- Стохастик %K > 60.
В позиции может находиться только одна сделка. При появлении противоположного сигнала активная позиция закрывается, и на той же свече новая сделка не открывается — именно так работает сканирование OrdersTotal в оригинале.
Управление выходом
- Стоп-лосс / тейк-профит: Необязательные фиксированные дистанции в пунктах переводятся в цену с учётом шага котировки и проверяются на каждой свече. При пробое позиция закрывается.
- Трейлинг-стоп: После прохождения ценой заданного числа пунктов активируется сопровождение. Для лонга стоп подтягивается под цену закрытия, для шорта — над ценой. Стоп никогда не откатывается назад, тем самым фиксируя прибыль.
- Противоположный сигнал: Валидный сигнал противоположного направления немедленно закрывает текущую позицию до рассмотрения новой сделки.
Стратегия не использует усреднение, сетки или хеджирование — логика остаётся минималистичной, как в исходном EA.
Параметры
| Параметр |
Значение по умолчанию |
Описание |
LotSize |
1 |
Объём каждой рыночной заявки. При запуске значение синхронизируется со свойством Strategy.Volume. |
TrailingStopPoints |
15 |
Дистанция трейлинг-стопа в пунктах. Ноль — отключить сопровождение. |
TakeProfitPoints |
20 |
Фиксированный тейк-профит в пунктах. Ноль — без цели. |
StopLossPoints |
0 |
Стоп-лосс в пунктах. Ноль воспроизводит оригинальную работу без стопа. |
SlippagePoints |
3 |
Допустимое проскальзывание (параметр сохранён для совместимости, но не используется напрямую). |
CandleType |
5-минутные свечи |
Свечная серия для расчёта индикаторов. Для повторения оригинала оставьте M5. |
MacdFastPeriod |
12 |
Период быстрой EMA в MACD. |
MacdSlowPeriod |
26 |
Период медленной EMA в MACD. |
MacdSignalPeriod |
9 |
Период сигнальной EMA MACD. |
StochasticLength |
5 |
Длина %K стохастика. |
StochasticSignal |
3 |
Период сглаживания %D. |
StochasticSlow |
3 |
Дополнительное сглаживание %K. |
MomentumPeriod |
14 |
Длина расчёта Momentum. |
SarAcceleration |
0.02 |
Начальный коэффициент ускорения Parabolic SAR. |
SarStep |
0.02 |
Шаг увеличения ускорения. |
SarMaximum |
0.2 |
Максимальное ускорение Parabolic SAR. |
Все числовые параметры помечены для оптимизации, поэтому их легко исследовать в оптимизаторе StockSharp.
Особенности реализации
- Цены bid/ask берутся из Level1, если данные доступны, либо подставляется цена закрытия свечи — это делает стратегию устойчивой при тестировании на исторических свечах.
- Перевод пунктов в цену выполняется через
Security.Step/PriceStep. При отсутствии шага используется запасное значение 0.0001, соответствующее стандартному форексному пункту.
- Позиция всегда одиночная: стратегия не переворачивается в обе стороны одновременно и не наращивает объём.
- Все комментарии в коде даны на английском в соответствии с требованиями репозитория, а расширенная документация приведена в README.
Рекомендации по применению
- Назначьте нужную валютную пару, оставьте таймфрейм 5 минут и запустите стратегию — индикаторы прогреются автоматически.
- Для реальной торговли рекомендуем задать ненулевой стоп-лосс: оригинальный советник предлагал торговать без него, но трейлинг-стоп может не защитить счёт от резкого разворота.
- Стратегию можно включать в корзину
BasketStrategy и управлять капиталом извне, пользуясь тем, что все ключевые пороги доступны как параметры.
Подробные описания на английском и китайском языках находятся в этой же папке.
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()