N Candles v6 策略
概述
N Candles v6 策略跟踪最近完成的蜡烛,寻找方向完全相同的连续序列。当市场连续出现 N 根阳线时,策略建立多头仓位;连续出现 N 根阴线时则开立空头。该方案源自 MetaTrader 专家顾问 N Candles v6.mq5,并针对 StockSharp 高级 API 重新实现。
该算法适用于任何提供常规时间周期蜡烛的品种。可以通过交易时间窗口控制进场时段,而已经建立的仓位在窗口关闭时仍然受到止损、止盈与追踪止损的保护。
交易逻辑
- 订阅设定的蜡烛类型,只处理状态为
Finished的蜡烛。 - 统计连续的阳线(
Close > Open)与阴线(Close < Open),十字线会重置计数。 - 当检测到
CandlesCount根阳线:- 预测加仓后的净头寸不超过
MaxPositionVolume。 - 发送市价买单;若当前持有空头,会自动增加手数以一次性反向至多头。
- 预测加仓后的净头寸不超过
- 当检测到
CandlesCount根阴线:- 确认新的空头不会突破
MaxPositionVolume。 - 发送市价卖单;若当前持有多头,同样会扩大手数以完成反向。
- 确认新的空头不会突破
- 若最新蜡烛破坏了连阳/连阴结构(“黑羊”):
- 根据
ClosingMode仅执行一次平仓操作:全部平仓、只平反向仓,或只平顺势仓。
- 根据
- 每根蜡烛都会执行风控:
- 止损与止盈按点数和
PriceStep计算为绝对价差。 - 价格在有利方向移动
TrailingStopPips + TrailingStepPips后启动追踪止损,并且只沿盈利方向推进。 - 一旦价格触及止损、止盈或追踪止损,立即平掉全部仓位。
- 止损与止盈按点数和
风险控制
- Stop Loss (pips):将点数转换为绝对价格距离;对于 5 位或 3 位小数报价,会自动放大 10 倍以符合传统“pip”定义。
- Take Profit (pips):达到设定盈利点数后平仓,设置为
0则关闭该功能。 - Trailing Stop / Step (pips):启用追踪止损后,价格至少移动指定的点数才会更新止损位置;当
TrailingStopPips > 0时,TrailingStepPips必须大于 0。 - Max Position Volume:限制净头寸的绝对值,超出限制的信号会被忽略。
- Closing Mode:决定出现“黑羊”时的处理方式:
All– 平掉全部仓位。Opposite– 仅平掉与连阳/连阴方向相反的仓位。Unidirectional– 仅平掉与连阳/连阴方向相同的仓位。
- 交易时间窗口:只有当蜡烛的开盘时间处于
StartHour与EndHour(包含端点)之间时才允许开仓;保护性平仓逻辑始终有效。
参数
| 名称 | 默认值 | 说明 |
|---|---|---|
CandlesCount |
3 | 触发信号所需的连续同向蜡烛数量。 |
OrderVolume |
0.01 | 基础下单手数;若存在反向仓位,会附加相应数量以完成反向。 |
TakeProfitPips |
50 | 止盈点数,0 为关闭。 |
StopLossPips |
50 | 止损点数,0 为关闭。 |
TrailingStopPips |
10 | 追踪止损的距离,0 为关闭。 |
TrailingStepPips |
4 | 每次更新追踪止损所需的最小价格改进;启用追踪止损时必须 > 0。 |
MaxPositionVolume |
2 | 净头寸的最大绝对值。 |
UseTradingHours |
true | 是否启用交易时间过滤。 |
StartHour |
11 | 允许开仓的起始小时(0-23)。 |
EndHour |
18 | 允许开仓的结束小时(0-23)。 |
ClosingMode |
All | 出现“黑羊”时的平仓策略。 |
CandleType |
1 小时蜡烛 | 用于信号计算的数据类型。 |
其他说明
- Pip 换算基于
PriceStep,对 5 位或 3 位小数报价会自动乘以 10。 - 策略启动时调用
StartProtection(),以启用 StockSharp 提供的安全保护机制(异常断线、撤单等)。 - 策略依据净头寸 (
Strategy.Position) 运作,适合净额账户;通过提高MaxPositionVolume可近似实现多头/空头同时持仓的效果。
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>
/// Detects runs of identical candles and trades in the direction of the streak.
/// </summary>
public class NCandlesV6Strategy : Strategy
{
/// <summary>
/// Defines how positions are closed when a candle breaks the streak.
/// </summary>
public enum BlackSheepCloseModes
{
/// <summary>
/// Close every open position regardless of direction.
/// </summary>
All,
/// <summary>
/// Close only positions that oppose the detected streak.
/// </summary>
Opposite,
/// <summary>
/// Close only positions that follow the detected streak.
/// </summary>
Unidirectional,
}
private readonly StrategyParam<int> _candlesCount;
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<decimal> _trailingStepPips;
private readonly StrategyParam<decimal> _maxPositionVolume;
private readonly StrategyParam<bool> _useTradingHours;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _endHour;
private readonly StrategyParam<BlackSheepCloseModes> _blackSheepMode;
private readonly StrategyParam<DataType> _candleType;
private decimal _pipSize;
private decimal _entryPrice;
private decimal? _stopLossPrice;
private decimal? _takeProfitPrice;
private decimal? _trailingLong;
private decimal? _trailingShort;
private int _streakDirection;
private int _bullCount;
private int _bearCount;
private bool _blackSheepTriggered;
public int CandlesCount { get => _candlesCount.Value; set => _candlesCount.Value = value; }
public decimal OrderVolume { get => _orderVolume.Value; set => _orderVolume.Value = value; }
public decimal TakeProfitPips { get => _takeProfitPips.Value; set => _takeProfitPips.Value = value; }
public decimal StopLossPips { get => _stopLossPips.Value; set => _stopLossPips.Value = value; }
public decimal TrailingStopPips { get => _trailingStopPips.Value; set => _trailingStopPips.Value = value; }
public decimal TrailingStepPips { get => _trailingStepPips.Value; set => _trailingStepPips.Value = value; }
public decimal MaxPositionVolume { get => _maxPositionVolume.Value; set => _maxPositionVolume.Value = value; }
public bool UseTradingHours { get => _useTradingHours.Value; set => _useTradingHours.Value = value; }
public int StartHour { get => _startHour.Value; set => _startHour.Value = value; }
public int EndHour { get => _endHour.Value; set => _endHour.Value = value; }
public BlackSheepCloseModes ClosingMode { get => _blackSheepMode.Value; set => _blackSheepMode.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public NCandlesV6Strategy()
{
_candlesCount = Param(nameof(CandlesCount), 4)
.SetGreaterThanZero()
.SetDisplay("Candles", "Number of identical candles", "Pattern");
_orderVolume = Param(nameof(OrderVolume), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Order Volume", "Base order size", "Orders");
_takeProfitPips = Param(nameof(TakeProfitPips), 50m)
.SetDisplay("Take Profit (pips)", "Take profit distance in pips", "Risk");
_stopLossPips = Param(nameof(StopLossPips), 50m)
.SetDisplay("Stop Loss (pips)", "Stop loss distance in pips", "Risk");
_trailingStopPips = Param(nameof(TrailingStopPips), 10m)
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance in pips", "Risk");
_trailingStepPips = Param(nameof(TrailingStepPips), 4m)
.SetDisplay("Trailing Step (pips)", "Minimum move before trailing updates", "Risk");
_maxPositionVolume = Param(nameof(MaxPositionVolume), 2m)
.SetGreaterThanZero()
.SetDisplay("Max Position Volume", "Maximum absolute net position", "Risk");
_useTradingHours = Param(nameof(UseTradingHours), false)
.SetDisplay("Use Trading Hours", "Enable trading window", "Timing");
_startHour = Param(nameof(StartHour), 11)
.SetDisplay("Start Hour", "Hour when trading can start", "Timing");
_endHour = Param(nameof(EndHour), 18)
.SetDisplay("End Hour", "Hour when trading stops", "Timing");
_blackSheepMode = Param(nameof(ClosingMode), BlackSheepCloseModes.All)
.SetDisplay("Closing Mode", "Reaction to a black sheep candle", "Pattern");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "Pattern");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (UseTradingHours && StartHour >= EndHour)
throw new InvalidOperationException("Start hour must be less than end hour when trading window is enabled.");
if (TrailingStopPips > 0m && TrailingStepPips <= 0m)
throw new InvalidOperationException("Trailing step must be greater than zero when trailing stop is enabled.");
Volume = OrderVolume;
_pipSize = CalculatePipSize();
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pipSize = 0m;
ResetCounters();
ResetPositionState();
_blackSheepTriggered = false;
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
UpdateTrailingLevels(candle);
if (ApplyRiskManagement(candle))
return;
var direction = GetDirection(candle);
if (direction == 0)
{
if (_streakDirection != 0 && !_blackSheepTriggered)
HandleBlackSheep(_streakDirection);
ResetCounters();
return;
}
if (_streakDirection == direction)
{
if (direction == 1)
{
_bullCount = Math.Min(CandlesCount, _bullCount + 1);
_bearCount = 0;
}
else
{
_bearCount = Math.Min(CandlesCount, _bearCount + 1);
_bullCount = 0;
}
}
else
{
if (_streakDirection != 0 && !_blackSheepTriggered)
HandleBlackSheep(_streakDirection);
_streakDirection = direction;
_bullCount = direction == 1 ? 1 : 0;
_bearCount = direction == -1 ? 1 : 0;
}
var allowTrading = !UseTradingHours || IsWithinTradingHours(candle.OpenTime);
if (_bullCount >= CandlesCount && allowTrading)
{
EnterLong(candle.ClosePrice);
}
else if (_bearCount >= CandlesCount && allowTrading)
{
EnterShort(candle.ClosePrice);
}
}
private void EnterLong(decimal price)
{
if (OrderVolume <= 0m)
return;
var volume = OrderVolume;
if (Position < 0m)
volume += Math.Abs(Position);
var projected = Position + volume;
if (projected > MaxPositionVolume)
return;
BuyMarket(volume);
_entryPrice = price;
_stopLossPrice = StopLossPips > 0m ? price - GetPriceOffset(StopLossPips) : null;
_takeProfitPrice = TakeProfitPips > 0m ? price + GetPriceOffset(TakeProfitPips) : null;
_trailingLong = null;
_trailingShort = null;
_blackSheepTriggered = false;
}
private void EnterShort(decimal price)
{
if (OrderVolume <= 0m)
return;
var volume = OrderVolume;
if (Position > 0m)
volume += Math.Abs(Position);
var projected = Position - volume;
if (Math.Abs(projected) > MaxPositionVolume)
return;
SellMarket(volume);
_entryPrice = price;
_stopLossPrice = StopLossPips > 0m ? price + GetPriceOffset(StopLossPips) : null;
_takeProfitPrice = TakeProfitPips > 0m ? price - GetPriceOffset(TakeProfitPips) : null;
_trailingLong = null;
_trailingShort = null;
_blackSheepTriggered = false;
}
private void HandleBlackSheep(int direction)
{
if (direction == 0 || _blackSheepTriggered)
return;
switch (ClosingMode)
{
case BlackSheepCloseModes.All:
{
ClosePosition();
break;
}
case BlackSheepCloseModes.Opposite:
{
if (direction == 1 && Position < 0m)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
}
else if (direction == -1 && Position > 0m)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
}
break;
}
case BlackSheepCloseModes.Unidirectional:
{
if (direction == 1 && Position > 0m)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
}
else if (direction == -1 && Position < 0m)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
}
break;
}
}
_blackSheepTriggered = true;
}
private void ClosePosition()
{
if (Position > 0m)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
}
else if (Position < 0m)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
}
}
private void UpdateTrailingLevels(ICandleMessage candle)
{
var trailingStop = GetPriceOffset(TrailingStopPips);
if (trailingStop <= 0m)
return;
var trailingStep = GetPriceOffset(TrailingStepPips);
if (Position > 0m)
{
var profit = candle.ClosePrice - _entryPrice;
if (profit > trailingStop + trailingStep)
{
var candidate = candle.ClosePrice - trailingStop;
if (_trailingLong == null || candidate > _trailingLong.Value + trailingStep)
_trailingLong = candidate;
}
}
else if (Position < 0m)
{
var profit = _entryPrice - candle.ClosePrice;
if (profit > trailingStop + trailingStep)
{
var candidate = candle.ClosePrice + trailingStop;
if (_trailingShort == null || candidate < _trailingShort.Value - trailingStep)
_trailingShort = candidate;
}
}
}
private bool ApplyRiskManagement(ICandleMessage candle)
{
if (Position > 0m)
{
if (_stopLossPrice is decimal longSl && candle.LowPrice <= longSl)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
return true;
}
if (_takeProfitPrice is decimal longTp && candle.HighPrice >= longTp)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
return true;
}
if (_trailingLong is decimal trail && candle.LowPrice <= trail)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
return true;
}
}
else if (Position < 0m)
{
var absPosition = Math.Abs(Position);
if (_stopLossPrice is decimal shortSl && candle.HighPrice >= shortSl)
{
BuyMarket(absPosition);
ResetPositionState();
return true;
}
if (_takeProfitPrice is decimal shortTp && candle.LowPrice <= shortTp)
{
BuyMarket(absPosition);
ResetPositionState();
return true;
}
if (_trailingShort is decimal trail && candle.HighPrice >= trail)
{
BuyMarket(absPosition);
ResetPositionState();
return true;
}
}
return false;
}
private void ResetCounters()
{
_streakDirection = 0;
_bullCount = 0;
_bearCount = 0;
}
private void ResetPositionState()
{
_entryPrice = 0m;
_stopLossPrice = null;
_takeProfitPrice = null;
_trailingLong = null;
_trailingShort = null;
}
private bool IsWithinTradingHours(DateTimeOffset time)
{
var hour = time.TimeOfDay.Hours;
return hour >= StartHour && hour <= EndHour;
}
private decimal GetPriceOffset(decimal pips)
{
if (pips <= 0m)
return 0m;
return pips * _pipSize;
}
private static int GetDirection(ICandleMessage candle)
{
if (candle.ClosePrice > candle.OpenPrice)
return 1;
if (candle.ClosePrice < candle.OpenPrice)
return -1;
return 0;
}
private decimal CalculatePipSize()
{
var step = Security?.PriceStep ?? 1m;
if (step <= 0m)
return 1m;
var decimals = CountDecimals(step);
return decimals == 3 || decimals == 5
? step * 10m
: step;
}
private static int CountDecimals(decimal value)
{
value = Math.Abs(value);
var count = 0;
while (value != Math.Truncate(value) && count < 10)
{
value *= 10m;
count++;
}
return count;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class n_candles_v6_strategy(Strategy):
CLOSE_ALL = 0
CLOSE_OPPOSITE = 1
CLOSE_UNIDIRECTIONAL = 2
def __init__(self):
super(n_candles_v6_strategy, self).__init__()
self._candles_count = self.Param("CandlesCount", 4)
self._order_volume = self.Param("OrderVolume", 0.01)
self._take_profit_pips = self.Param("TakeProfitPips", 50.0)
self._stop_loss_pips = self.Param("StopLossPips", 50.0)
self._trailing_stop_pips = self.Param("TrailingStopPips", 10.0)
self._trailing_step_pips = self.Param("TrailingStepPips", 4.0)
self._max_position_volume = self.Param("MaxPositionVolume", 2.0)
self._use_trading_hours = self.Param("UseTradingHours", False)
self._start_hour = self.Param("StartHour", 11)
self._end_hour = self.Param("EndHour", 18)
self._closing_mode = self.Param("ClosingMode", self.CLOSE_ALL)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._pip_size = 0.0
self._entry_price = 0.0
self._stop_loss_price = None
self._take_profit_price = None
self._trailing_long = None
self._trailing_short = None
self._streak_direction = 0
self._bull_count = 0
self._bear_count = 0
self._black_sheep_triggered = False
@property
def CandlesCount(self):
return self._candles_count.Value
@property
def OrderVolume(self):
return self._order_volume.Value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@property
def TrailingStopPips(self):
return self._trailing_stop_pips.Value
@property
def TrailingStepPips(self):
return self._trailing_step_pips.Value
@property
def MaxPositionVolume(self):
return self._max_position_volume.Value
@property
def UseTradingHours(self):
return self._use_trading_hours.Value
@property
def StartHour(self):
return self._start_hour.Value
@property
def EndHour(self):
return self._end_hour.Value
@property
def ClosingMode(self):
return self._closing_mode.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(n_candles_v6_strategy, self).OnStarted2(time)
self.Volume = float(self.OrderVolume)
self._pip_size = self._calculate_pip_size()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._update_trailing_levels(candle)
if self._apply_risk_management(candle):
return
direction = self._get_direction(candle)
if direction == 0:
if self._streak_direction != 0 and not self._black_sheep_triggered:
self._handle_black_sheep(self._streak_direction)
self._reset_counters()
return
if self._streak_direction == direction:
if direction == 1:
self._bull_count = min(self.CandlesCount, self._bull_count + 1)
self._bear_count = 0
else:
self._bear_count = min(self.CandlesCount, self._bear_count + 1)
self._bull_count = 0
else:
if self._streak_direction != 0 and not self._black_sheep_triggered:
self._handle_black_sheep(self._streak_direction)
self._streak_direction = direction
self._bull_count = 1 if direction == 1 else 0
self._bear_count = 1 if direction == -1 else 0
allow_trading = not self.UseTradingHours or self._is_within_trading_hours(candle.OpenTime)
if self._bull_count >= self.CandlesCount and allow_trading:
self._enter_long(float(candle.ClosePrice))
elif self._bear_count >= self.CandlesCount and allow_trading:
self._enter_short(float(candle.ClosePrice))
def _enter_long(self, price):
vol = float(self.OrderVolume)
if vol <= 0:
return
pos = float(self.Position)
if pos < 0:
vol += abs(pos)
projected = pos + vol
if projected > float(self.MaxPositionVolume):
return
self.BuyMarket(vol)
self._entry_price = price
self._stop_loss_price = price - self._get_price_offset(float(self.StopLossPips)) if float(self.StopLossPips) > 0 else None
self._take_profit_price = price + self._get_price_offset(float(self.TakeProfitPips)) if float(self.TakeProfitPips) > 0 else None
self._trailing_long = None
self._trailing_short = None
self._black_sheep_triggered = False
def _enter_short(self, price):
vol = float(self.OrderVolume)
if vol <= 0:
return
pos = float(self.Position)
if pos > 0:
vol += abs(pos)
projected = pos - vol
if abs(projected) > float(self.MaxPositionVolume):
return
self.SellMarket(vol)
self._entry_price = price
self._stop_loss_price = price + self._get_price_offset(float(self.StopLossPips)) if float(self.StopLossPips) > 0 else None
self._take_profit_price = price - self._get_price_offset(float(self.TakeProfitPips)) if float(self.TakeProfitPips) > 0 else None
self._trailing_long = None
self._trailing_short = None
self._black_sheep_triggered = False
def _handle_black_sheep(self, direction):
if direction == 0 or self._black_sheep_triggered:
return
pos = float(self.Position)
mode = self.ClosingMode
if mode == self.CLOSE_ALL:
self._close_position()
elif mode == self.CLOSE_OPPOSITE:
if direction == 1 and pos < 0:
self.BuyMarket(abs(pos))
self._reset_position_state()
elif direction == -1 and pos > 0:
self.SellMarket(abs(pos))
self._reset_position_state()
elif mode == self.CLOSE_UNIDIRECTIONAL:
if direction == 1 and pos > 0:
self.SellMarket(abs(pos))
self._reset_position_state()
elif direction == -1 and pos < 0:
self.BuyMarket(abs(pos))
self._reset_position_state()
self._black_sheep_triggered = True
def _close_position(self):
pos = float(self.Position)
if pos > 0:
self.SellMarket(abs(pos))
self._reset_position_state()
elif pos < 0:
self.BuyMarket(abs(pos))
self._reset_position_state()
def _update_trailing_levels(self, candle):
trailing_stop = self._get_price_offset(float(self.TrailingStopPips))
if trailing_stop <= 0:
return
trailing_step = self._get_price_offset(float(self.TrailingStepPips))
pos = float(self.Position)
if pos > 0:
profit = float(candle.ClosePrice) - self._entry_price
if profit > trailing_stop + trailing_step:
candidate = float(candle.ClosePrice) - trailing_stop
if self._trailing_long is None or candidate > self._trailing_long + trailing_step:
self._trailing_long = candidate
elif pos < 0:
profit = self._entry_price - float(candle.ClosePrice)
if profit > trailing_stop + trailing_step:
candidate = float(candle.ClosePrice) + trailing_stop
if self._trailing_short is None or candidate < self._trailing_short - trailing_step:
self._trailing_short = candidate
def _apply_risk_management(self, candle):
pos = float(self.Position)
if pos > 0:
if self._stop_loss_price is not None and float(candle.LowPrice) <= self._stop_loss_price:
self.SellMarket(abs(pos))
self._reset_position_state()
return True
if self._take_profit_price is not None and float(candle.HighPrice) >= self._take_profit_price:
self.SellMarket(abs(pos))
self._reset_position_state()
return True
if self._trailing_long is not None and float(candle.LowPrice) <= self._trailing_long:
self.SellMarket(abs(pos))
self._reset_position_state()
return True
elif pos < 0:
ap = abs(pos)
if self._stop_loss_price is not None and float(candle.HighPrice) >= self._stop_loss_price:
self.BuyMarket(ap)
self._reset_position_state()
return True
if self._take_profit_price is not None and float(candle.LowPrice) <= self._take_profit_price:
self.BuyMarket(ap)
self._reset_position_state()
return True
if self._trailing_short is not None and float(candle.HighPrice) >= self._trailing_short:
self.BuyMarket(ap)
self._reset_position_state()
return True
return False
def _reset_counters(self):
self._streak_direction = 0
self._bull_count = 0
self._bear_count = 0
def _reset_position_state(self):
self._entry_price = 0.0
self._stop_loss_price = None
self._take_profit_price = None
self._trailing_long = None
self._trailing_short = None
def _is_within_trading_hours(self, time):
hour = time.TimeOfDay.Hours
return hour >= self.StartHour and hour <= self.EndHour
def _get_price_offset(self, pips):
if pips <= 0:
return 0.0
return pips * self._pip_size
def _get_direction(self, candle):
if float(candle.ClosePrice) > float(candle.OpenPrice):
return 1
if float(candle.ClosePrice) < float(candle.OpenPrice):
return -1
return 0
def _calculate_pip_size(self):
sec = self.Security
if sec is None:
return 1.0
step = float(sec.PriceStep) if sec.PriceStep is not None else 1.0
if step <= 0:
return 1.0
decimals = self._count_decimals(step)
if decimals == 3 or decimals == 5:
return step * 10.0
return step
def _count_decimals(self, value):
value = abs(value)
count = 0
while value != int(value) and count < 10:
value *= 10.0
count += 1
return count
def OnReseted(self):
super(n_candles_v6_strategy, self).OnReseted()
self._pip_size = 0.0
self._reset_counters()
self._reset_position_state()
self._black_sheep_triggered = False
def CreateClone(self):
return n_candles_v6_strategy()