JS Sistem 2 策略
概述
JS Sistem 2 最初是为 MetaTrader 5 编写的趋势策略。移植到 StockSharp 后,仍然保留了原始智能交易系统中的多指标确认模块,只在所选周期的收盘 K 线后进行计算。策略使用固定下单量,当投资组合净值低于阈值 MinBalance 时可以停止开仓。风险控制通过以点数表示的止损、止盈距离以及跟随 K 线影线的自适应追踪止损共同完成。
指标与过滤器
- EMA(55)、EMA(89)、EMA(144):构成方向性过滤。做多要求快线在上、慢线在下,并且 EMA55 与 EMA144 的距离小于
MinDifferencePips指定的阈值。 - MACD 柱状图(OsMA):使用与 MQL 版本一致的快、慢、信号周期。做多要求柱状图为正,做空要求柱状图为负。
- 相对活力指数 (RVI):以
RviPeriod为周期计算,并通过长度为RviSignalLength的简单移动平均生成信号线。做多时 RVI 需高于信号线且信号线不低于RviMax;做空时条件相反并使用RviMin阈值。 - 最高价/最低价通道:利用
VolatilityPeriod根 K 线的最高价与最低价来复制原策略的“影线追踪”逻辑,为追踪止损提供参考。
交易逻辑
- 策略仅处理
CandleType指定类型的完整 K 线。 - 在评估新信号之前,会根据最新的高低点更新追踪止损,然后检测当前 K 线是否触及止损或止盈。
- 做多条件:
- 投资组合净值高于
MinBalance。 - EMA55 > EMA89 > EMA144,且 EMA55 与 EMA144 的差值(按品种点值换算)小于
MinDifferencePips。 - MACD 柱状图
macdLine大于 0。 - RVI 位于信号线上方,且信号线达到或超过
RviMax。 - 当前无多单(
Position <= 0);若存在空单,会先平仓再开多。
- 投资组合净值高于
- 做空条件与做多完全对称,并使用
RviMin作为阈值。 - 开仓后以 K 线收盘价为基准,根据
StopLossPips和TakeProfitPips计算虚拟止损和止盈价格,同时重置追踪状态。
仓位管理与追踪
- 固定止损 / 止盈:当 K 线区间触及保存的止损或止盈价位时,立即平掉全部仓位。
- 追踪止损:当
TrailingEnabled为 true 时,止损会顺着盈利方向移动。多单在最近VolatilityPeriod根 K 线的最低价高于入场价和上一止损至少TrailingIndentPips时,将止损提升至该最低价;空单则使用最高价并采用对称规则,从而复现原策略的“影线追踪”效果,避免过早被震荡扫出。 - 余额保护:如果投资组合净值跌破
MinBalance,策略将暂停新的下单,但仍会管理已有仓位和追踪止损。
参数
| 参数 | 说明 | 默认值 |
|---|---|---|
MinBalance |
允许开仓的最低账户净值。 | 100 |
Volume |
每次下单的固定数量。 | 1 |
StopLossPips |
止损距离(点数,0 表示关闭)。 | 35 |
TakeProfitPips |
止盈距离(点数,0 表示关闭)。 | 40 |
MinDifferencePips |
快、慢 EMA 之间允许的最大点差。 | 28 |
VolatilityPeriod |
计算高低点通道的 K 线数量。 | 15 |
TrailingEnabled |
是否启用追踪止损。 | true |
TrailingIndentPips |
更新追踪止损时价格、入场价与止损之间需要保持的最小间隔。 | 1 |
MaFastPeriod |
快速 EMA 的周期。 | 55 |
MaMediumPeriod |
中速 EMA 的周期。 | 89 |
MaSlowPeriod |
慢速 EMA 的周期。 | 144 |
OsmaFastPeriod |
MACD 柱状图的快速 EMA 周期。 | 13 |
OsmaSlowPeriod |
MACD 柱状图的慢速 EMA 周期。 | 55 |
OsmaSignalPeriod |
MACD 柱状图的信号线周期。 | 21 |
RviPeriod |
RVI 的计算周期。 | 44 |
RviSignalLength |
对 RVI 进行平滑的 SMA 周期。 | 4 |
RviMax |
做多前信号线需要达到的上限阈值。 | 0.04 |
RviMin |
做空前信号线需要达到的下限阈值。 | -0.04 |
CandleType |
用于计算的 K 线周期。 | 5 分钟 |
实现说明
- 点值根据品种的最小价格步长推导,报价保留 3 或 5 位小数的品种会将 1 点视为 10 个最小步长,完全复刻原 MQL 策略的处理方式。
- 止损和止盈在策略内部检测,并不会在交易所创建真实挂单。
- 策略启动时调用
StartProtection(),便于基类监控断线或持仓异常的情况。
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>
/// JS Sistem 2 trend-following strategy converted from MetaTrader 5.
/// Combines exponential moving averages, MACD histogram (OsMA), and Relative Vigor Index filters.
/// Includes trailing stop based on recent candle shadows and configurable stop/target distances.
/// </summary>
public class JsSistem2Strategy : Strategy
{
private readonly StrategyParam<decimal> _minBalance;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _minDifferencePips;
private readonly StrategyParam<int> _volatilityPeriod;
private readonly StrategyParam<bool> _trailingEnabled;
private readonly StrategyParam<int> _trailingIndentPips;
private readonly StrategyParam<int> _maFastPeriod;
private readonly StrategyParam<int> _maMediumPeriod;
private readonly StrategyParam<int> _maSlowPeriod;
private readonly StrategyParam<int> _osmaFastPeriod;
private readonly StrategyParam<int> _osmaSlowPeriod;
private readonly StrategyParam<int> _osmaSignalPeriod;
private readonly StrategyParam<int> _rviPeriod;
private readonly StrategyParam<int> _rviSignalLength;
private readonly StrategyParam<decimal> _rviMax;
private readonly StrategyParam<decimal> _rviMin;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _emaFast = null!;
private ExponentialMovingAverage _emaMedium = null!;
private ExponentialMovingAverage _emaSlow = null!;
private MovingAverageConvergenceDivergence _macd = null!;
private Highest _highest = null!;
private Lowest _lowest = null!;
private RelativeVigorIndex _rvi = null!;
private decimal? _stopPrice;
private decimal? _takePrice;
private decimal _entryPrice;
/// <summary>
/// Minimum account balance required to allow new entries.
/// </summary>
public decimal MinBalance
{
get => _minBalance.Value;
set => _minBalance.Value = value;
}
/// <summary>
/// Stop-loss distance in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance in pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Maximum allowed spread between fast and slow EMA in pips.
/// </summary>
public int MinDifferencePips
{
get => _minDifferencePips.Value;
set => _minDifferencePips.Value = value;
}
/// <summary>
/// Lookback for trailing stop based on candle shadows.
/// </summary>
public int VolatilityPeriod
{
get => _volatilityPeriod.Value;
set => _volatilityPeriod.Value = value;
}
/// <summary>
/// Enables trailing stop management.
/// </summary>
public bool TrailingEnabled
{
get => _trailingEnabled.Value;
set => _trailingEnabled.Value = value;
}
/// <summary>
/// Offset applied when updating trailing stop levels.
/// </summary>
public int TrailingIndentPips
{
get => _trailingIndentPips.Value;
set => _trailingIndentPips.Value = value;
}
/// <summary>
/// Fast EMA period.
/// </summary>
public int MaFastPeriod
{
get => _maFastPeriod.Value;
set => _maFastPeriod.Value = value;
}
/// <summary>
/// Medium EMA period.
/// </summary>
public int MaMediumPeriod
{
get => _maMediumPeriod.Value;
set => _maMediumPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int MaSlowPeriod
{
get => _maSlowPeriod.Value;
set => _maSlowPeriod.Value = value;
}
/// <summary>
/// Fast EMA length for the MACD/OsMA filter.
/// </summary>
public int OsmaFastPeriod
{
get => _osmaFastPeriod.Value;
set => _osmaFastPeriod.Value = value;
}
/// <summary>
/// Slow EMA length for the MACD/OsMA filter.
/// </summary>
public int OsmaSlowPeriod
{
get => _osmaSlowPeriod.Value;
set => _osmaSlowPeriod.Value = value;
}
/// <summary>
/// Signal length for the MACD/OsMA filter.
/// </summary>
public int OsmaSignalPeriod
{
get => _osmaSignalPeriod.Value;
set => _osmaSignalPeriod.Value = value;
}
/// <summary>
/// Relative Vigor Index period.
/// </summary>
public int RviPeriod
{
get => _rviPeriod.Value;
set => _rviPeriod.Value = value;
}
/// <summary>
/// Smoothing length for the RVI signal line.
/// </summary>
public int RviSignalLength
{
get => _rviSignalLength.Value;
set => _rviSignalLength.Value = value;
}
/// <summary>
/// Upper threshold for the RVI signal line.
/// </summary>
public decimal RviMax
{
get => _rviMax.Value;
set => _rviMax.Value = value;
}
/// <summary>
/// Lower threshold for the RVI signal line.
/// </summary>
public decimal RviMin
{
get => _rviMin.Value;
set => _rviMin.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="JsSistem2Strategy"/> class.
/// </summary>
public JsSistem2Strategy()
{
_minBalance = Param(nameof(MinBalance), 100m)
.SetDisplay("Min Balance", "Minimum balance to allow trading", "Risk")
;
_stopLossPips = Param(nameof(StopLossPips), 200)
.SetDisplay("Stop Loss", "Stop-loss distance in pips", "Risk")
;
_takeProfitPips = Param(nameof(TakeProfitPips), 300)
.SetDisplay("Take Profit", "Take-profit distance in pips", "Risk")
;
_minDifferencePips = Param(nameof(MinDifferencePips), 5000)
.SetGreaterThanZero()
.SetDisplay("EMA Spread", "Maximum fast-slow EMA spread", "Filters")
;
_volatilityPeriod = Param(nameof(VolatilityPeriod), 15)
.SetGreaterThanZero()
.SetDisplay("Trailing Range", "Number of candles for trailing", "Risk")
;
_trailingEnabled = Param(nameof(TrailingEnabled), true)
.SetDisplay("Trailing", "Enable trailing stop", "Risk");
_trailingIndentPips = Param(nameof(TrailingIndentPips), 1)
.SetGreaterThanZero()
.SetDisplay("Trailing Offset", "Indent from candle shadows", "Risk")
;
_maFastPeriod = Param(nameof(MaFastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
;
_maMediumPeriod = Param(nameof(MaMediumPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Medium EMA", "Medium EMA period", "Indicators")
;
_maSlowPeriod = Param(nameof(MaSlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
;
_osmaFastPeriod = Param(nameof(OsmaFastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("OsMA Fast", "Fast EMA for MACD", "Indicators")
;
_osmaSlowPeriod = Param(nameof(OsmaSlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("OsMA Slow", "Slow EMA for MACD", "Indicators")
;
_osmaSignalPeriod = Param(nameof(OsmaSignalPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("OsMA Signal", "Signal period for MACD", "Indicators")
;
_rviPeriod = Param(nameof(RviPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("RVI Period", "Relative Vigor Index period", "Indicators")
;
_rviSignalLength = Param(nameof(RviSignalLength), 4)
.SetGreaterThanZero()
.SetDisplay("RVI Signal", "Smoothing for RVI signal", "Indicators")
;
_rviMax = Param(nameof(RviMax), 0.02m)
.SetDisplay("RVI Max", "Upper threshold for RVI signal", "Filters")
;
_rviMin = Param(nameof(RviMin), -0.02m)
.SetDisplay("RVI Min", "Lower threshold for RVI signal", "Filters")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles used for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stopPrice = null;
_takePrice = null;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_emaFast = new ExponentialMovingAverage { Length = MaFastPeriod };
_emaMedium = new ExponentialMovingAverage { Length = MaMediumPeriod };
_emaSlow = new ExponentialMovingAverage { Length = MaSlowPeriod };
_macd = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = OsmaFastPeriod },
LongMa = { Length = OsmaSlowPeriod },
};
_highest = new Highest { Length = VolatilityPeriod };
_lowest = new Lowest { Length = VolatilityPeriod };
_rvi = new RelativeVigorIndex();
_rvi.Average.Length = RviPeriod;
_rvi.Signal.Length = RviSignalLength;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_emaFast, _emaMedium, _emaSlow, _macd, _highest, _lowest, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaFast, decimal emaMedium, decimal emaSlow, decimal macdLine, decimal highestValue, decimal lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_emaFast.IsFormed || !_emaMedium.IsFormed || !_emaSlow.IsFormed || !_macd.IsFormed || !_highest.IsFormed || !_lowest.IsFormed)
return;
var step = CalculatePipSize();
if (step == 0m)
{
step = Security.PriceStep ?? 0m;
}
if (step == 0m)
step = 1m;
var stopDistance = StopLossPips > 0 ? StopLossPips * step : 0m;
var takeDistance = TakeProfitPips > 0 ? TakeProfitPips * step : 0m;
var minDifference = MinDifferencePips * step;
var indent = TrailingIndentPips * step;
UpdateTrailingStops(candle, highestValue, lowestValue, indent);
HandleStopsAndTargets(candle);
var canTrade = (Portfolio?.CurrentValue ?? decimal.MaxValue) >= MinBalance;
var emaOrderLong = emaFast > emaMedium && emaMedium > emaSlow;
var emaOrderShort = emaFast < emaMedium && emaMedium < emaSlow;
var emaSpreadLong = Math.Abs(emaFast - emaSlow) < minDifference;
var emaSpreadShort = Math.Abs(emaSlow - emaFast) < minDifference;
var longCondition = canTrade && emaOrderLong && emaSpreadLong && macdLine > 0m;
var shortCondition = canTrade && emaOrderShort && emaSpreadShort && macdLine < 0m;
if (longCondition && Position <= 0)
{
if (Position < 0)
{
BuyMarket(Math.Abs(Position));
ResetOrders();
}
if (Volume > 0m)
{
BuyMarket(Volume);
_entryPrice = candle.ClosePrice;
_stopPrice = stopDistance > 0m ? _entryPrice - stopDistance : null;
_takePrice = takeDistance > 0m ? _entryPrice + takeDistance : null;
}
}
else if (shortCondition && Position >= 0)
{
if (Position > 0)
{
SellMarket(Math.Abs(Position));
ResetOrders();
}
if (Volume > 0m)
{
SellMarket(Volume);
_entryPrice = candle.ClosePrice;
_stopPrice = stopDistance > 0m ? _entryPrice + stopDistance : null;
_takePrice = takeDistance > 0m ? _entryPrice - takeDistance : null;
}
}
}
private void UpdateTrailingStops(ICandleMessage candle, decimal highestValue, decimal lowestValue, decimal indent)
{
if (!TrailingEnabled)
return;
if (Position > 0)
{
var newStop = lowestValue;
if (newStop > 0m && candle.ClosePrice - newStop > indent && newStop - _entryPrice > indent)
{
if (!_stopPrice.HasValue || newStop - _stopPrice.Value > indent)
{
_stopPrice = newStop;
}
}
}
else if (Position < 0)
{
var newStop = highestValue;
if (newStop > 0m && newStop - candle.ClosePrice > indent && _entryPrice - newStop > indent)
{
if (!_stopPrice.HasValue || _stopPrice.Value - newStop > indent)
{
_stopPrice = newStop;
}
}
}
}
private void HandleStopsAndTargets(ICandleMessage candle)
{
if (Position > 0)
{
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
{
SellMarket(Math.Abs(Position));
ResetOrders();
return;
}
if (_takePrice.HasValue && candle.HighPrice >= _takePrice.Value)
{
SellMarket(Math.Abs(Position));
ResetOrders();
}
}
else if (Position < 0)
{
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetOrders();
return;
}
if (_takePrice.HasValue && candle.LowPrice <= _takePrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetOrders();
}
}
}
private void ResetOrders()
{
_stopPrice = null;
_takePrice = null;
_entryPrice = 0m;
}
private decimal CalculatePipSize()
{
var security = Security;
if (security is null)
return 0m;
var step = security.PriceStep ?? 0m;
if (step == 0m)
return 0m;
var decimals = security.Decimals;
return decimals == 3 || decimals == 5 ? step * 10m : step;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (
ExponentialMovingAverage, MovingAverageConvergenceDivergence,
Highest, Lowest
)
from StockSharp.Algo.Strategies import Strategy
class js_sistem2_strategy(Strategy):
def __init__(self):
super(js_sistem2_strategy, self).__init__()
self._min_balance = self.Param("MinBalance", 100.0)
self._stop_loss_pips = self.Param("StopLossPips", 200)
self._take_profit_pips = self.Param("TakeProfitPips", 300)
self._min_difference_pips = self.Param("MinDifferencePips", 5000)
self._volatility_period = self.Param("VolatilityPeriod", 15)
self._trailing_enabled = self.Param("TrailingEnabled", True)
self._trailing_indent_pips = self.Param("TrailingIndentPips", 1)
self._ma_fast_period = self.Param("MaFastPeriod", 12)
self._ma_medium_period = self.Param("MaMediumPeriod", 26)
self._ma_slow_period = self.Param("MaSlowPeriod", 50)
self._osma_fast_period = self.Param("OsmaFastPeriod", 12)
self._osma_slow_period = self.Param("OsmaSlowPeriod", 26)
self._osma_signal_period = self.Param("OsmaSignalPeriod", 9)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._ema_fast = None
self._ema_medium = None
self._ema_slow = None
self._macd = None
self._highest = None
self._lowest = None
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MinBalance(self):
return self._min_balance.Value
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@property
def MinDifferencePips(self):
return self._min_difference_pips.Value
@property
def VolatilityPeriod(self):
return self._volatility_period.Value
@property
def TrailingEnabled(self):
return self._trailing_enabled.Value
@property
def TrailingIndentPips(self):
return self._trailing_indent_pips.Value
@property
def MaFastPeriod(self):
return self._ma_fast_period.Value
@property
def MaMediumPeriod(self):
return self._ma_medium_period.Value
@property
def MaSlowPeriod(self):
return self._ma_slow_period.Value
@property
def OsmaFastPeriod(self):
return self._osma_fast_period.Value
@property
def OsmaSlowPeriod(self):
return self._osma_slow_period.Value
@property
def OsmaSignalPeriod(self):
return self._osma_signal_period.Value
def OnStarted2(self, time):
super(js_sistem2_strategy, self).OnStarted2(time)
self._ema_fast = ExponentialMovingAverage()
self._ema_fast.Length = self.MaFastPeriod
self._ema_medium = ExponentialMovingAverage()
self._ema_medium.Length = self.MaMediumPeriod
self._ema_slow = ExponentialMovingAverage()
self._ema_slow.Length = self.MaSlowPeriod
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self.OsmaSlowPeriod
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self.OsmaFastPeriod
self._macd = MovingAverageConvergenceDivergence(slow_ma, fast_ma)
self._highest = Highest()
self._highest.Length = self.VolatilityPeriod
self._lowest = Lowest()
self._lowest.Length = self.VolatilityPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(
self._ema_fast, self._ema_medium, self._ema_slow,
self._macd, self._highest, self._lowest,
self._process_candle
).Start()
def _process_candle(self, candle, ema_fast_v, ema_medium_v, ema_slow_v, macd_v, highest_v, lowest_v):
if candle.State != CandleStates.Finished:
return
if (not self._ema_fast.IsFormed or not self._ema_medium.IsFormed or
not self._ema_slow.IsFormed or not self._macd.IsFormed or
not self._highest.IsFormed or not self._lowest.IsFormed):
return
step = self._calculate_pip_size()
if step == 0:
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 0.0
if step == 0:
step = 1.0
ef = float(ema_fast_v)
em = float(ema_medium_v)
es = float(ema_slow_v)
macd_line = float(macd_v)
hv = float(highest_v)
lv = float(lowest_v)
stop_dist = self.StopLossPips * step if self.StopLossPips > 0 else 0.0
take_dist = self.TakeProfitPips * step if self.TakeProfitPips > 0 else 0.0
min_diff = self.MinDifferencePips * step
indent = self.TrailingIndentPips * step
self._update_trailing(candle, hv, lv, indent)
self._handle_stops(candle)
pf = self.Portfolio
can_trade = True
if pf is not None and pf.CurrentValue is not None:
can_trade = float(pf.CurrentValue) >= self.MinBalance
ema_order_long = ef > em and em > es
ema_order_short = ef < em and em < es
ema_spread_long = abs(ef - es) < min_diff
ema_spread_short = abs(es - ef) < min_diff
long_cond = can_trade and ema_order_long and ema_spread_long and macd_line > 0
short_cond = can_trade and ema_order_short and ema_spread_short and macd_line < 0
close_price = float(candle.ClosePrice)
if long_cond and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self._reset_orders()
if self.Volume > 0:
self.BuyMarket()
self._entry_price = close_price
self._stop_price = self._entry_price - stop_dist if stop_dist > 0 else None
self._take_price = self._entry_price + take_dist if take_dist > 0 else None
elif short_cond and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self._reset_orders()
if self.Volume > 0:
self.SellMarket()
self._entry_price = close_price
self._stop_price = self._entry_price + stop_dist if stop_dist > 0 else None
self._take_price = self._entry_price - take_dist if take_dist > 0 else None
def _update_trailing(self, candle, highest_v, lowest_v, indent):
if not self.TrailingEnabled:
return
close = float(candle.ClosePrice)
if self.Position > 0:
new_stop = lowest_v
if new_stop > 0 and close - new_stop > indent and new_stop - self._entry_price > indent:
if self._stop_price is None or new_stop - self._stop_price > indent:
self._stop_price = new_stop
elif self.Position < 0:
new_stop = highest_v
if new_stop > 0 and new_stop - close > indent and self._entry_price - new_stop > indent:
if self._stop_price is None or self._stop_price - new_stop > indent:
self._stop_price = new_stop
def _handle_stops(self, candle):
if self.Position > 0:
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket(); self._reset_orders(); return
if self._take_price is not None and float(candle.HighPrice) >= self._take_price:
self.SellMarket(); self._reset_orders()
elif self.Position < 0:
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket(); self._reset_orders(); return
if self._take_price is not None and float(candle.LowPrice) <= self._take_price:
self.BuyMarket(); self._reset_orders()
def _reset_orders(self):
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
def _calculate_pip_size(self):
sec = self.Security
if sec is None:
return 0.0
step = float(sec.PriceStep) if sec.PriceStep is not None else 0.0
if step == 0:
return 0.0
decimals = sec.Decimals if sec.Decimals is not None else 0
return step * 10.0 if (decimals == 3 or decimals == 5) else step
def OnReseted(self):
super(js_sistem2_strategy, self).OnReseted()
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
def CreateClone(self):
return js_sistem2_strategy()