HarVesteR Strategy
The HarVesteR strategy blends MACD momentum with two simple moving averages and an optional ADX strength filter. It looks for situations where price hugs the moving averages while MACD has recently crossed the zero line, signalling a potential breakout from consolidation. Stops are attached to swing highs or lows, half of the position is booked at a fixed reward multiple, and the remainder is protected with a break-even exit driven by the fast moving average.
Details
- Entry Criteria:
- Long:
MACD > 0 && MACD history contains negative value && Close < SlowSMA && Close + Indentation > FastSMA && Close + Indentation > SlowSMA && ADX ≥ AdxBuyLevel (if enabled) - Short:
MACD < 0 && MACD history contains positive value && Close > SlowSMA && Close - Indentation < FastSMA && Close - Indentation < SlowSMA && ADX ≥ AdxSellLevel (if enabled)
- Long:
- Stop Loss: Latest swing low/high over
StopLookbackcompleted candles. - Partial Exit: Closes half the position once price moves
HalfCloseRatiotimes the distance between entry and stop, then moves the stop to break-even. - Final Exit:
- Long: closes the rest if price dips below
FastSMA + Indentationafter the stop is at break-even. - Short: closes the rest if price rises above
FastSMA + Indentationafter the stop is at break-even.
- Long: closes the rest if price dips below
- Long/Short: Both directions supported.
- Filters: Optional ADX trend-strength filter; set
UseAdxFiltertofalseto disable it. - Position Management: Reverses the position by netting the opposite signal volume plus the current exposure.
Parameters
| Name | Default | Description |
|---|---|---|
MacdFast |
12 | Fast EMA period for the MACD difference line. |
MacdSlow |
24 | Slow EMA period for the MACD difference line. |
MacdSignal |
9 | Signal EMA period for MACD smoothing. |
MacdLookback |
6 | Number of recently finished candles checked for a MACD sign change. |
SmaFastLength |
50 | Length of the fast simple moving average. |
SmaSlowLength |
100 | Length of the slow simple moving average. |
MinIndentation |
10 | Offset in pips applied around the moving averages before entering or exiting. |
StopLookback |
6 | Swing-high/low lookback used to seed the initial stop level. |
UseAdxFilter |
false | Enables the ADX strength filter for both directions. |
AdxBuyLevel |
50 | Minimum ADX level required to allow long entries when the filter is enabled. |
AdxSellLevel |
50 | Minimum ADX level required to allow short entries when the filter is enabled. |
AdxPeriod |
14 | Period used for the ADX calculation. |
HalfCloseRatio |
2 | Multiplier applied to the entry-to-stop distance before taking partial profits. |
Volume |
1 | Order volume for new entries (netting against any opposite exposure). |
CandleType |
1 hour | Primary timeframe used to build candles and indicators. |
Notes
MinIndentationis converted to price distance using the instrument tick size. Instruments quoted with three or five decimals receive a tenfold adjustment to approximate pip units.- When
UseAdxFilteris disabled the strategy accepts signals in both directions without checking the ADX value. - Partial profit taking and break-even exits run on every finished candle to protect open positions even when no new trades are allowed.
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()