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根已收盘 K 线的最高价/最低价。 - 分批止盈:价格相对入场价突破
HalfCloseRatio × (入场价-止损价)后,平掉一半头寸,并把止损移动到入场价。 - 最终退出:
- 多头:止损已经移动到保本后,若价格跌破
FastSMA + Indentation则清仓。 - 空头:止损已经移动到保本后,若价格升破
FastSMA + Indentation则清仓。
- 多头:止损已经移动到保本后,若价格跌破
- 多空方向:支持双向交易。
- 过滤器:可选的 ADX 趋势过滤器,
UseAdxFilter = false时不做强度检查。 - 仓位管理:新的反向信号会下单
Volume + |Position|,从而直接反手而无需手动平仓。
参数
| 名称 | 默认值 | 说明 |
|---|---|---|
MacdFast |
12 | MACD 差值线的快速 EMA 周期。 |
MacdSlow |
24 | MACD 差值线的慢速 EMA 周期。 |
MacdSignal |
9 | MACD 信号线的 EMA 周期。 |
MacdLookback |
6 | 检查 MACD 符号变化的最近 K 线数量。 |
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会按照标的价格最小变动转换成实际价差;对于 3 或 5 位小数的报价,会额外乘以 10 以近似“点”这一单位。- 当
UseAdxFilter为false时,策略不会检查 ADX 值即可触发信号。 - 分批止盈与保本逻辑在每根收盘 K 线上都会执行,以便在暂停开仓时仍能管理已有头寸。
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()