Стратегия HarVesteR
Стратегия HarVesteR сочетает импульс MACD с двумя простыми скользящими средними и опциональным фильтром по силе тренда ADX. Она ищет ситуации, когда цена «прилипает» к средним, а MACD недавно пересёк нулевую линию, что указывает на возможный выход из консолидации. Стопы ставятся по ближайшим локальным экстремумам, половина позиции фиксируется при достижении заданного соотношения риск/прибыль, а остаток защищается выходом в безубыток, управляемым быстрой средней.
Подробности
- Условия входа:
- Лонг:
MACD > 0 && в истории MACD есть отрицательное значение && Close < SlowSMA && Close + Indentation > FastSMA && Close + Indentation > SlowSMA && ADX ≥ AdxBuyLevel (если фильтр включён) - Шорт:
MACD < 0 && в истории MACD есть положительное значение && Close > SlowSMA && Close - Indentation < FastSMA && Close - Indentation < SlowSMA && ADX ≥ AdxSellLevel (если фильтр включён)
- Лонг:
- Стоп-лосс: последний локальный минимум/максимум за
StopLookbackзакрытых свечей. - Частичное закрытие: половина объёма закрывается, когда цена проходит
HalfCloseRatioрасстояний между входом и стопом, после чего стоп переносится в безубыток. - Окончательный выход:
- Лонг: полное закрытие, если цена опускается ниже
FastSMA + Indentationпосле переноса стопа в безубыток. - Шорт: полное закрытие, если цена поднимается выше
FastSMA + Indentationпосле переноса стопа в безубыток.
- Лонг: полное закрытие, если цена опускается ниже
- Направления: торгует в обе стороны.
- Фильтры: опциональный фильтр по силе тренда ADX; отключается параметром
UseAdxFilter = false. - Управление позицией: при смене сигнала объём сделки включает текущую позицию для разворота без отдельного закрытия.
Параметры
| Имя | Значение по умолчанию | Описание |
|---|---|---|
MacdFast |
12 | Быстрый период EMA для разностной линии MACD. |
MacdSlow |
24 | Медленный период EMA для разностной линии MACD. |
MacdSignal |
9 | Период EMA сигнальной линии MACD. |
MacdLookback |
6 | Число последних свечей, анализируемых на смену знака MACD. |
SmaFastLength |
50 | Период быстрой простой скользящей средней. |
SmaSlowLength |
100 | Период медленной простой скользящей средней. |
MinIndentation |
10 | Отступ в пунктах вокруг средних для условий входа и выхода. |
StopLookback |
6 | Глубина поиска локального максимума/минимума для постановки стопа. |
UseAdxFilter |
false | Включает фильтр по силе тренда ADX для обоих направлений. |
AdxBuyLevel |
50 | Минимальное значение ADX для разрешения лонгов при активном фильтре. |
AdxSellLevel |
50 | Минимальное значение ADX для разрешения шортов при активном фильтре. |
AdxPeriod |
14 | Период расчёта индикатора ADX. |
HalfCloseRatio |
2 | Множитель к расстоянию «вход-стоп» перед частичной фиксацией прибыли. |
Volume |
1 | Объём заявки для новых входов (с учётом текущей позиции). |
CandleType |
1 час | Таймфрейм, по которому строятся свечи и индикаторы. |
Примечания
MinIndentationпереводится в ценовое расстояние через шаг цены инструмента. Для котировок с тремя или пятью знаками используется дополнительный множитель ×10, чтобы приблизить величину к пунктам.- При
UseAdxFilter = falseстратегия принимает сигналы без проверки значения ADX. - Логика частичного выхода и перехода в безубыток выполняется на каждой закрытой свече, чтобы защищать позиции даже при временном запрете новых сделок.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trend strategy that combines MACD momentum, moving average proximity and ADX filter with partial profit taking.
/// </summary>
public class HarVesteRStrategy : Strategy
{
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<int> _macdLookback;
private readonly StrategyParam<int> _smaFastLength;
private readonly StrategyParam<int> _smaSlowLength;
private readonly StrategyParam<decimal> _minIndentation;
private readonly StrategyParam<int> _stopLookback;
private readonly StrategyParam<bool> _useAdx;
private readonly StrategyParam<decimal> _adxBuyLevel;
private readonly StrategyParam<decimal> _adxSellLevel;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _halfCloseRatio;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergenceSignal _macd = null!;
private SimpleMovingAverage _smaFast = null!;
private SimpleMovingAverage _smaSlow = null!;
private AverageDirectionalIndex _adx = null!;
private Lowest _lowest = null!;
private Highest _highest = null!;
private readonly List<decimal> _macdHistory = new();
private decimal? _lastLowest;
private decimal? _lastHighest;
private decimal? _longEntry;
private decimal? _longStop;
private bool _longStopMoved;
private decimal? _shortEntry;
private decimal? _shortStop;
private bool _shortStopMoved;
/// <summary>
/// Fast period for MACD.
/// </summary>
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
/// <summary>
/// Slow period for MACD.
/// </summary>
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
/// <summary>
/// Signal line period for MACD.
/// </summary>
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
/// <summary>
/// Number of bars used to confirm MACD sign change.
/// </summary>
public int MacdLookback
{
get => _macdLookback.Value;
set => _macdLookback.Value = value;
}
/// <summary>
/// Fast simple moving average length.
/// </summary>
public int SmaFastLength
{
get => _smaFastLength.Value;
set => _smaFastLength.Value = value;
}
/// <summary>
/// Slow simple moving average length.
/// </summary>
public int SmaSlowLength
{
get => _smaSlowLength.Value;
set => _smaSlowLength.Value = value;
}
/// <summary>
/// Minimum indentation measured in pips.
/// </summary>
public decimal MinIndentation
{
get => _minIndentation.Value;
set => _minIndentation.Value = value;
}
/// <summary>
/// Bars used to compute stop loss levels.
/// </summary>
public int StopLookback
{
get => _stopLookback.Value;
set => _stopLookback.Value = value;
}
/// <summary>
/// Enable ADX filter for entries.
/// </summary>
public bool UseAdxFilter
{
get => _useAdx.Value;
set => _useAdx.Value = value;
}
/// <summary>
/// Minimum ADX strength required to buy.
/// </summary>
public decimal AdxBuyLevel
{
get => _adxBuyLevel.Value;
set => _adxBuyLevel.Value = value;
}
/// <summary>
/// Minimum ADX strength required to sell.
/// </summary>
public decimal AdxSellLevel
{
get => _adxSellLevel.Value;
set => _adxSellLevel.Value = value;
}
/// <summary>
/// ADX indicator period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Ratio used to trigger half position exit.
/// </summary>
public int HalfCloseRatio
{
get => _halfCloseRatio.Value;
set => _halfCloseRatio.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes <see cref="HarVesteRStrategy"/>.
/// </summary>
public HarVesteRStrategy()
{
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast EMA", "Short EMA period for MACD", "MACD")
;
_macdSlow = Param(nameof(MacdSlow), 24)
.SetGreaterThanZero()
.SetDisplay("MACD Slow EMA", "Long EMA period for MACD", "MACD")
;
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal averaging period", "MACD")
;
_macdLookback = Param(nameof(MacdLookback), 6)
.SetGreaterThanZero()
.SetDisplay("MACD Lookback", "Bars to confirm MACD sign change", "MACD")
;
_smaFastLength = Param(nameof(SmaFastLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fast SMA", "First moving average length", "Moving Averages")
;
_smaSlowLength = Param(nameof(SmaSlowLength), 20)
.SetGreaterThanZero()
.SetDisplay("Slow SMA", "Second moving average length", "Moving Averages")
;
_minIndentation = Param(nameof(MinIndentation), 500m)
.SetGreaterThanZero()
.SetDisplay("Indentation", "Distance from moving averages in pips", "Trading")
;
_stopLookback = Param(nameof(StopLookback), 6)
.SetGreaterThanZero()
.SetDisplay("Stop Lookback", "Bars for stop loss calculation", "Risk")
;
_useAdx = Param(nameof(UseAdxFilter), false)
.SetDisplay("Use ADX", "Enable ADX trend filter", "ADX");
_adxBuyLevel = Param(nameof(AdxBuyLevel), 50m)
.SetGreaterThanZero()
.SetDisplay("ADX Buy Level", "Minimum ADX strength for longs", "ADX");
_adxSellLevel = Param(nameof(AdxSellLevel), 50m)
.SetGreaterThanZero()
.SetDisplay("ADX Sell Level", "Minimum ADX strength for shorts", "ADX");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "ADX calculation length", "ADX")
;
_halfCloseRatio = Param(nameof(HalfCloseRatio), 2)
.SetGreaterThanZero()
.SetDisplay("Half Close Ratio", "Multiplier applied to stop distance", "Risk")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macdHistory.Clear();
_lastLowest = null;
_lastHighest = null;
ResetLongState();
ResetShortState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Configure indicators used by the strategy.
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow },
},
SignalMa = { Length = MacdSignal }
};
_smaFast = new SMA { Length = SmaFastLength };
_smaSlow = new SMA { Length = SmaSlowLength };
_adx = new AverageDirectionalIndex { Length = AdxPeriod };
_lowest = new Lowest { Length = StopLookback };
_highest = new Highest { Length = StopLookback };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_macd, _smaFast, _smaSlow, _adx, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _smaFast);
DrawIndicator(area, _smaSlow);
DrawIndicator(area, _macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue smaFastValue, IIndicatorValue smaSlowValue, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished)
return;
// Update trailing stop helpers from recent highs and lows.
var lowValue = _lowest.Process(new DecimalIndicatorValue(_lowest, candle.LowPrice, candle.ServerTime) { IsFinal = true });
if (lowValue.IsFormed)
_lastLowest = lowValue.ToDecimal();
var highValue = _highest.Process(new DecimalIndicatorValue(_highest, candle.HighPrice, candle.ServerTime) { IsFinal = true });
if (highValue.IsFormed)
_lastHighest = highValue.ToDecimal();
if (!macdValue.IsFinal || !smaFastValue.IsFinal || !smaSlowValue.IsFinal)
return;
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdTyped.Macd is not decimal macdMain)
return;
var smaFast = smaFastValue.ToDecimal();
var smaSlow = smaSlowValue.ToDecimal();
decimal? adxStrength = null;
if (UseAdxFilter)
{
if (!adxValue.IsFinal)
return;
var adxTyped = (AverageDirectionalIndexValue)adxValue;
adxStrength = adxTyped.MovingAverage;
if (adxStrength is not decimal)
return;
}
_macdHistory.Add(macdMain);
while (_macdHistory.Count > MacdLookback)
try { _macdHistory.RemoveAt(0); } catch { break; }
var indentation = GetIndentation();
var close = candle.ClosePrice;
if (macdMain == 0m || smaFast == 0m || smaSlow == 0m || close <= 0m)
return;
// Manage partial exits and break-even logic for open positions.
ManageOpenPositions(close, smaFast, indentation);
if (!_macd.IsFormed || !_smaFast.IsFormed || !_smaSlow.IsFormed)
return;
if (_macdHistory.Count < MacdLookback)
return;
var hadNegative = HasNegativeMacd();
var hadPositive = HasPositiveMacd();
var adxBuyOk = !UseAdxFilter;
var adxSellOk = !UseAdxFilter;
if (UseAdxFilter && adxStrength is decimal adxValueDecimal)
{
adxBuyOk = adxValueDecimal >= AdxBuyLevel;
adxSellOk = adxValueDecimal >= AdxSellLevel;
}
var okBuy = close < smaSlow;
var okSell = close > smaSlow;
if (macdMain > 0m && hadNegative && adxBuyOk && okBuy && close + indentation > smaFast && close + indentation > smaSlow && Position <= 0m && _lastLowest is decimal longStop)
{
var volume = Volume + Math.Abs(Position);
if (volume > 0m)
{
BuyMarket();
_longEntry = close;
_longStop = longStop;
_longStopMoved = false;
ResetShortState();
}
}
else if (macdMain < 0m && hadPositive && adxSellOk && okSell && close - indentation < smaFast && close - indentation < smaSlow && Position >= 0m && _lastHighest is decimal shortStop)
{
var volume = Volume + Math.Abs(Position);
if (volume > 0m)
{
SellMarket();
_shortEntry = close;
_shortStop = shortStop;
_shortStopMoved = false;
ResetLongState();
}
}
}
private void ManageOpenPositions(decimal close, decimal smaFast, decimal indentation)
{
if (Position > 0m && _longEntry is decimal entry && _longStop is decimal stop)
{
var distance = Math.Abs(entry - stop);
if (distance > 0m)
{
var target = entry + distance * HalfCloseRatio;
if (!_longStopMoved && close > target)
{
var half = Position / 2m;
if (half > 0m)
{
SellMarket();
_longStop = entry;
_longStopMoved = true;
}
}
else if (_longStopMoved && smaFast > close - indentation)
{
SellMarket();
ResetLongState();
}
}
}
else if (Position <= 0m)
{
ResetLongState();
}
if (Position < 0m && _shortEntry is decimal entryShort && _shortStop is decimal stopShort)
{
var distance = Math.Abs(entryShort - stopShort);
if (distance > 0m)
{
var target = entryShort - distance * HalfCloseRatio;
if (!_shortStopMoved && close < target)
{
var half = -Position / 2m;
if (half > 0m)
{
BuyMarket();
_shortStop = entryShort;
_shortStopMoved = true;
}
}
else if (_shortStopMoved && smaFast < close - indentation)
{
BuyMarket();
ResetShortState();
}
}
}
else if (Position >= 0m)
{
ResetShortState();
}
}
private bool HasNegativeMacd()
{
foreach (var value in _macdHistory)
{
if (value < 0m)
return true;
}
return false;
}
private bool HasPositiveMacd()
{
foreach (var value in _macdHistory)
{
if (value > 0m)
return true;
}
return false;
}
private decimal GetIndentation()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
return MinIndentation;
var decimals = Security?.Decimals ?? 0;
var factor = (decimals == 3 || decimals == 5) ? 10m : 1m;
return MinIndentation * step * factor;
}
private void ResetLongState()
{
_longEntry = null;
_longStop = null;
_longStopMoved = false;
}
private void ResetShortState()
{
_shortEntry = null;
_shortStop = null;
_shortStopMoved = false;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import (
MovingAverageConvergenceDivergenceSignal,
SimpleMovingAverage,
AverageDirectionalIndex,
Lowest,
Highest,
)
from indicator_extensions import *
class har_veste_r_strategy(Strategy):
"""Trend strategy combining MACD momentum, MA proximity, ADX filter with partial profit taking."""
def __init__(self):
super(har_veste_r_strategy, self).__init__()
self._macd_fast = self.Param("MacdFast", 12) \
.SetGreaterThanZero() \
.SetDisplay("MACD Fast EMA", "Short EMA period for MACD", "MACD")
self._macd_slow = self.Param("MacdSlow", 24) \
.SetGreaterThanZero() \
.SetDisplay("MACD Slow EMA", "Long EMA period for MACD", "MACD")
self._macd_signal_param = self.Param("MacdSignal", 9) \
.SetGreaterThanZero() \
.SetDisplay("MACD Signal", "Signal averaging period", "MACD")
self._macd_lookback = self.Param("MacdLookback", 6) \
.SetGreaterThanZero() \
.SetDisplay("MACD Lookback", "Bars to confirm MACD sign change", "MACD")
self._sma_fast_length = self.Param("SmaFastLength", 10) \
.SetGreaterThanZero() \
.SetDisplay("Fast SMA", "First moving average length", "Moving Averages")
self._sma_slow_length = self.Param("SmaSlowLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Slow SMA", "Second moving average length", "Moving Averages")
self._min_indentation = self.Param("MinIndentation", 500.0) \
.SetGreaterThanZero() \
.SetDisplay("Indentation", "Distance from moving averages in pips", "Trading")
self._stop_lookback = self.Param("StopLookback", 6) \
.SetGreaterThanZero() \
.SetDisplay("Stop Lookback", "Bars for stop loss calculation", "Risk")
self._use_adx = self.Param("UseAdxFilter", False) \
.SetDisplay("Use ADX", "Enable ADX trend filter", "ADX")
self._adx_buy_level = self.Param("AdxBuyLevel", 50.0) \
.SetGreaterThanZero() \
.SetDisplay("ADX Buy Level", "Minimum ADX strength for longs", "ADX")
self._adx_sell_level = self.Param("AdxSellLevel", 50.0) \
.SetGreaterThanZero() \
.SetDisplay("ADX Sell Level", "Minimum ADX strength for shorts", "ADX")
self._adx_period = self.Param("AdxPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ADX Period", "ADX calculation length", "ADX")
self._half_close_ratio = self.Param("HalfCloseRatio", 2) \
.SetGreaterThanZero() \
.SetDisplay("Half Close Ratio", "Multiplier applied to stop distance", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Primary timeframe", "General")
self._macd_history = []
self._last_lowest = None
self._last_highest = None
self._long_entry = None
self._long_stop = None
self._long_stop_moved = False
self._short_entry = None
self._short_stop = None
self._short_stop_moved = False
@property
def MacdFast(self):
return int(self._macd_fast.Value)
@property
def MacdSlow(self):
return int(self._macd_slow.Value)
@property
def MacdSignal(self):
return int(self._macd_signal_param.Value)
@property
def MacdLookback(self):
return int(self._macd_lookback.Value)
@property
def SmaFastLength(self):
return int(self._sma_fast_length.Value)
@property
def SmaSlowLength(self):
return int(self._sma_slow_length.Value)
@property
def MinIndentation(self):
return float(self._min_indentation.Value)
@property
def StopLookback(self):
return int(self._stop_lookback.Value)
@property
def UseAdxFilter(self):
return self._use_adx.Value
@property
def AdxBuyLevel(self):
return float(self._adx_buy_level.Value)
@property
def AdxSellLevel(self):
return float(self._adx_sell_level.Value)
@property
def AdxPeriod(self):
return int(self._adx_period.Value)
@property
def HalfCloseRatio(self):
return int(self._half_close_ratio.Value)
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(har_veste_r_strategy, self).OnStarted2(time)
self._macd_history = []
self._last_lowest = None
self._last_highest = None
self._reset_long_state()
self._reset_short_state()
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = self.MacdFast
self._macd.Macd.LongMa.Length = self.MacdSlow
self._macd.SignalMa.Length = self.MacdSignal
self._sma_fast_ind = SimpleMovingAverage()
self._sma_fast_ind.Length = self.SmaFastLength
self._sma_slow_ind = SimpleMovingAverage()
self._sma_slow_ind.Length = self.SmaSlowLength
self._adx_ind = AverageDirectionalIndex()
self._adx_ind.Length = self.AdxPeriod
self._lowest_ind = Lowest()
self._lowest_ind.Length = self.StopLookback
self._highest_ind = Highest()
self._highest_ind.Length = self.StopLookback
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._macd, self._sma_fast_ind, self._sma_slow_ind, self._adx_ind, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._sma_fast_ind)
self.DrawIndicator(area, self._sma_slow_ind)
self.DrawIndicator(area, self._macd)
self.DrawOwnTrades(area)
def process_candle(self, candle, macd_value, sma_fast_value, sma_slow_value, adx_value):
if candle.State != CandleStates.Finished:
return
# Update lowest/highest for stop calculation
low_val = process_float(self._lowest_ind, candle.LowPrice, candle.ServerTime, True)
if low_val.IsFormed:
self._last_lowest = float(low_val.Value)
high_val = process_float(self._highest_ind, candle.HighPrice, candle.ServerTime, True)
if high_val.IsFormed:
self._last_highest = float(high_val.Value)
if not macd_value.IsFinal or not sma_fast_value.IsFinal or not sma_slow_value.IsFinal:
return
macd_main_n = macd_value.Macd
if macd_main_n is None:
return
macd_main = float(macd_main_n)
sma_fast = float(sma_fast_value.Value)
sma_slow = float(sma_slow_value.Value)
adx_strength = None
if self.UseAdxFilter:
if not adx_value.IsFinal:
return
adx_ma = adx_value.MovingAverage
if adx_ma is None:
return
adx_strength = float(adx_ma)
self._macd_history.append(macd_main)
while len(self._macd_history) > self.MacdLookback:
self._macd_history.pop(0)
indentation = self._get_indentation()
close = float(candle.ClosePrice)
if macd_main == 0.0 or sma_fast == 0.0 or sma_slow == 0.0 or close <= 0.0:
return
# Manage partial exits and break-even logic
self._manage_open_positions(close, sma_fast, indentation)
if not self._macd.IsFormed or not self._sma_fast_ind.IsFormed or not self._sma_slow_ind.IsFormed:
return
if len(self._macd_history) < self.MacdLookback:
return
had_negative = self._has_negative_macd()
had_positive = self._has_positive_macd()
adx_buy_ok = not self.UseAdxFilter
adx_sell_ok = not self.UseAdxFilter
if self.UseAdxFilter and adx_strength is not None:
adx_buy_ok = adx_strength >= self.AdxBuyLevel
adx_sell_ok = adx_strength >= self.AdxSellLevel
ok_buy = close < sma_slow
ok_sell = close > sma_slow
if (macd_main > 0 and had_negative and adx_buy_ok and ok_buy
and close + indentation > sma_fast and close + indentation > sma_slow
and self.Position <= 0 and self._last_lowest is not None):
self.BuyMarket()
self._long_entry = close
self._long_stop = self._last_lowest
self._long_stop_moved = False
self._reset_short_state()
elif (macd_main < 0 and had_positive and adx_sell_ok and ok_sell
and close - indentation < sma_fast and close - indentation < sma_slow
and self.Position >= 0 and self._last_highest is not None):
self.SellMarket()
self._short_entry = close
self._short_stop = self._last_highest
self._short_stop_moved = False
self._reset_long_state()
def _manage_open_positions(self, close, sma_fast, indentation):
if self.Position > 0 and self._long_entry is not None and self._long_stop is not None:
distance = abs(self._long_entry - self._long_stop)
if distance > 0:
target = self._long_entry + distance * self.HalfCloseRatio
if not self._long_stop_moved and close > target:
self.SellMarket()
self._long_stop = self._long_entry
self._long_stop_moved = True
elif self._long_stop_moved and sma_fast > close - indentation:
self.SellMarket()
self._reset_long_state()
elif self.Position <= 0:
self._reset_long_state()
if self.Position < 0 and self._short_entry is not None and self._short_stop is not None:
distance = abs(self._short_entry - self._short_stop)
if distance > 0:
target = self._short_entry - distance * self.HalfCloseRatio
if not self._short_stop_moved and close < target:
self.BuyMarket()
self._short_stop = self._short_entry
self._short_stop_moved = True
elif self._short_stop_moved and sma_fast < close - indentation:
self.BuyMarket()
self._reset_short_state()
elif self.Position >= 0:
self._reset_short_state()
def _has_negative_macd(self):
for v in self._macd_history:
if v < 0:
return True
return False
def _has_positive_macd(self):
for v in self._macd_history:
if v > 0:
return True
return False
def _get_indentation(self):
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 0.0
if step <= 0:
return self.MinIndentation
decimals = int(sec.Decimals) if sec is not None and sec.Decimals is not None else 0
factor = 10.0 if (decimals == 3 or decimals == 5) else 1.0
return self.MinIndentation * step * factor
def _reset_long_state(self):
self._long_entry = None
self._long_stop = None
self._long_stop_moved = False
def _reset_short_state(self):
self._short_entry = None
self._short_stop = None
self._short_stop_moved = False
def OnReseted(self):
super(har_veste_r_strategy, self).OnReseted()
self._macd_history = []
self._last_lowest = None
self._last_highest = None
self._reset_long_state()
self._reset_short_state()
def CreateClone(self):
return har_veste_r_strategy()