N本連続ローソク足シーケンス戦略
概要
N本連続ローソク足シーケンス戦略は、StockSharpの高レベルAPIを使用してオリジナルのMetaTraderエキスパート「N-_Candles_v7」の動作を再現します。完成したローソク足を監視し、設定可能な数の連続した強気または弱気のボディを探します。適格な連続が存在する場合、戦略は同じ方向にポジションを開き、設定可能なテイクプロフィット、ストップロス、トレーリングストップ、取引時間フィルター、浮動利益ロックで管理します。
取引ロジック
- 完成した各ローソク足を評価し、強気、弱気、またはニュートラル(ドージ)に分類します。ニュートラルなローソク足は連続カウンターをリセットし、「ブラックシープ」動作を引き起こす可能性があります。
- 同じボディ方向を持つ連続したローソク足の実行カウントを維持します。カウントが設定されたしきい値に達すると、現在の方向がアクティブなパターンになります。
- 強気の連続がアクティブな場合、戦略はロングポジションを開こうとします。弱気の連続がアクティブな場合は、ショートポジションを開こうとします。一度に1つのネットポジションのみが保持されます。
- ローソク足が統一した方向を破ると(「ブラックシープ」)、戦略は選択されたクローズモードに従って反応します:全てをクローズ、反対のポジションのみクローズ、または前の連続と整合したポジションのみクローズ。
- オプションで、開始時刻と終了時刻(両端含む)で定義される取引ウィンドウにエントリーを制限します。
- テイクプロフィット、ストップロス、トレーリングストップの更新、浮動利益しきい値のためにオープンポジションを継続的に監視します。
ポジションとリスク管理
- 最初の保護ストップと目標は、銘柄の価格ステップで変換されたpip距離から計算されます。これらのレベルは新しいポジションが開かれるたびに再計算されます。
- トレーリングストップロジックはオリジナルのエキスパートを模倣します:価格がトレーリング距離にステップを加えた分だけ進むと、トレーリング距離を維持するためにストップが移動されます。
- 浮動利益ガード(
MinProfit)は、オープン利益が設定値を超えるとポジション全体をクローズします。 MaxPositionVolumeパラメーターは、要求されたボリュームが許可された制限を超える場合にエントリーを防ぎます。MaxPositionsはポジションがすでにアクティブな場合の再エントリーに対するガードとして機能します。- 全ての退出は成行注文を呼び出してネットポジションをフラットにします。StockSharp戦略はネッティング環境で動作するためです。
パラメーター
| 名前 | 説明 |
|---|---|
ConsecutiveCandles |
シグナルをトリガーするために必要な同じ方向を持つローソク足の数。 |
OrderVolume |
エントリーと退出に使用される成行注文量。 |
TakeProfitPips |
pipsで表されるテイクプロフィット距離。無効にするにはゼロに設定。 |
StopLossPips |
pipsで表されるストップロス距離。無効にするにはゼロに設定。 |
TrailingStopPips |
トレーリングストップ距離。トレーリングを無効にするにはゼロに設定。 |
TrailingStepPips |
トレーリングストップが動く前に必要な追加距離。 |
MaxPositions |
パターンあたりの最大同時エントリー数(戦略は単一のネットポジションを維持する)。 |
MaxPositionVolume |
許可されたネットボリュームの上限。 |
UseTradeHours / StartHour / EndHour |
取引時間ウィンドウを有効化して設定する(両端含む)。 |
MinProfit |
完全退出をトリガーする浮動利益しきい値。 |
ClosingBehavior |
「ブラックシープ」ローソク足が現れた際の反応を定義する。 |
CandleType |
計算に使用されるローソク足シリーズ。 |
注意事項と前提条件
- 戦略はネットポジションで動作します。ヘッジスタイルの複数チケットは作成されません。これは、複数のヘッジされたポジションが共存できたオリジナルのエキスパートとは異なります。
- 浮動利益はロングポジションの場合
(現在価格 - エントリー価格) * ボリューム、ショートポジションの場合はその逆として近似されます。 - pip変換は銘柄の
PriceStepに依存します。最小ステップが提供されていないシンボルの場合、デフォルトのpip 0.0001が仮定されます。 - 要求に応じてPythonポートは提供されません。
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>
/// Strategy that searches for N identical candles in a row and trades in the direction of the streak.
/// Implements optional take profit, stop loss, trailing stop, trading hours filter and profit lock.
/// </summary>
public class NCandlesSequenceStreakStrategy : Strategy
{
private readonly StrategyParam<int> _consecutiveCandles;
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _trailingStopPips;
private readonly StrategyParam<int> _trailingStepPips;
private readonly StrategyParam<int> _maxPositions;
private readonly StrategyParam<decimal> _maxPositionVolume;
private readonly StrategyParam<bool> _useTradeHours;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _endHour;
private readonly StrategyParam<decimal> _minProfit;
private readonly StrategyParam<ClosingModes> _closingBehavior;
private readonly StrategyParam<DataType> _candleType;
private int _streakCount;
private int _lastDirection;
private int _patternDirection;
private int _entriesInDirection;
private bool _blackSheepTriggered;
private bool _hasPosition;
private decimal _entryPrice;
private decimal _pipSize;
private decimal? _longStop;
private decimal? _longTake;
private decimal? _shortStop;
private decimal? _shortTake;
/// <summary>
/// Defines how positions are closed when a "black sheep" candle appears.
/// </summary>
public enum ClosingModes
{
/// <summary>Close every open position.</summary>
All,
/// <summary>Close positions opposite to the previously detected streak.</summary>
Opposite,
/// <summary>Close positions that follow the previously detected streak.</summary>
SameDirection
}
/// <summary>
/// Initializes a new instance of the <see cref="NCandlesSequenceStreakStrategy"/> class.
/// </summary>
public NCandlesSequenceStreakStrategy()
{
_consecutiveCandles = Param(nameof(ConsecutiveCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Consecutive Candles", "Number of candles with identical direction", "Pattern")
.SetOptimize(2, 10, 1);
_orderVolume = Param(nameof(OrderVolume), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Order Volume", "Volume used for market orders", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 50)
.SetGreaterThanZero()
.SetDisplay("Take Profit (pips)", "Distance for the take profit target", "Risk")
.SetOptimize(10, 200, 10);
_stopLossPips = Param(nameof(StopLossPips), 50)
.SetGreaterThanZero()
.SetDisplay("Stop Loss (pips)", "Distance for the protective stop", "Risk")
.SetOptimize(10, 200, 10);
_trailingStopPips = Param(nameof(TrailingStopPips), 10)
.SetDisplay("Trailing Stop (pips)", "Distance used when trailing is active", "Risk");
_trailingStepPips = Param(nameof(TrailingStepPips), 4)
.SetDisplay("Trailing Step (pips)", "Additional distance before moving the trailing stop", "Risk");
_maxPositions = Param(nameof(MaxPositions), 2)
.SetGreaterThanZero()
.SetDisplay("Max Positions", "Maximum number of sequential entries in the same direction", "Risk");
_maxPositionVolume = Param(nameof(MaxPositionVolume), 2m)
.SetGreaterThanZero()
.SetDisplay("Max Position Volume", "Maximum volume allowed for an open position", "Risk");
_useTradeHours = Param(nameof(UseTradeHours), false)
.SetDisplay("Use Trade Hours", "Enable intraday trading window", "Timing");
_startHour = Param(nameof(StartHour), 11)
.SetDisplay("Start Hour", "First trading hour (0-23)", "Timing");
_endHour = Param(nameof(EndHour), 18)
.SetDisplay("End Hour", "Last trading hour (0-23)", "Timing");
_minProfit = Param(nameof(MinProfit), 3m)
.SetDisplay("Min Profit", "Close positions when floating profit exceeds this value", "Risk")
.SetOptimize(1m, 20m, 1m);
_closingBehavior = Param(nameof(ClosingBehavior), ClosingModes.All)
.SetDisplay("Black Sheep Closing", "Reaction when the streak is broken", "Pattern");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to analyze", "General");
}
/// <summary>
/// Required number of candles with the same direction.
/// </summary>
public int ConsecutiveCandles
{
get => _consecutiveCandles.Value;
set => _consecutiveCandles.Value = value;
}
/// <summary>
/// Volume for market orders.
/// </summary>
public decimal OrderVolume
{
get => _orderVolume.Value;
set => _orderVolume.Value = value;
}
/// <summary>
/// Take profit distance expressed in pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Stop loss distance expressed in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Trailing stop distance in pips.
/// </summary>
public int TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Additional step before the trailing stop is moved.
/// </summary>
public int TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
/// <summary>
/// Maximum number of consecutive entries in the same direction.
/// </summary>
public int MaxPositions
{
get => _maxPositions.Value;
set => _maxPositions.Value = value;
}
/// <summary>
/// Maximum allowed volume for an open position.
/// </summary>
public decimal MaxPositionVolume
{
get => _maxPositionVolume.Value;
set => _maxPositionVolume.Value = value;
}
/// <summary>
/// Enables the trading hours filter.
/// </summary>
public bool UseTradeHours
{
get => _useTradeHours.Value;
set => _useTradeHours.Value = value;
}
/// <summary>
/// First trading hour (inclusive).
/// </summary>
public int StartHour
{
get => _startHour.Value;
set => _startHour.Value = value;
}
/// <summary>
/// Last trading hour (inclusive).
/// </summary>
public int EndHour
{
get => _endHour.Value;
set => _endHour.Value = value;
}
/// <summary>
/// Minimum floating profit that forces the strategy to close all positions.
/// </summary>
public decimal MinProfit
{
get => _minProfit.Value;
set => _minProfit.Value = value;
}
/// <summary>
/// How to handle positions when the streak is broken.
/// </summary>
public ClosingModes ClosingBehavior
{
get => _closingBehavior.Value;
set => _closingBehavior.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_streakCount = 0;
_lastDirection = 0;
_patternDirection = 0;
_entriesInDirection = 0;
_blackSheepTriggered = false;
_hasPosition = false;
_entryPrice = 0m;
_pipSize = 0m;
_longStop = null;
_longTake = null;
_shortStop = null;
_shortTake = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (UseTradeHours && StartHour >= EndHour)
throw new InvalidOperationException("Start hour must be less than end hour when the trading window is enabled.");
_pipSize = CalculatePipSize();
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// no indicators to check formation
UpdateTrailingStops(candle);
ManageFloatingProfit(candle);
var direction = GetCandleDirection(candle);
if (direction == 0)
{
HandlePatternBreak();
_lastDirection = 0;
_streakCount = 0;
return;
}
if (_lastDirection == direction)
{
_streakCount++;
}
else
{
if (_lastDirection != 0)
HandlePatternBreak();
_lastDirection = direction;
_streakCount = 1;
}
if (_streakCount >= ConsecutiveCandles)
{
if (_patternDirection != direction)
{
_patternDirection = direction;
_entriesInDirection = 0;
_blackSheepTriggered = false;
}
if (direction > 0)
TryEnterLong(candle);
else
TryEnterShort(candle);
}
ManageExits(candle);
}
private void ManageFloatingProfit(ICandleMessage candle)
{
if (MinProfit <= 0m || Position == 0m || !_hasPosition)
return;
var floating = CalculateOpenProfit(candle.ClosePrice);
if (floating >= MinProfit)
ClosePosition();
}
private void TryEnterLong(ICandleMessage candle)
{
if (OrderVolume <= 0m || OrderVolume > MaxPositionVolume)
return;
if (_entriesInDirection >= MaxPositions)
return;
if (UseTradeHours && !IsWithinTradeHours(candle.CloseTime))
return;
if (Position < 0m)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
return;
}
if (Position != 0m)
return;
BuyMarket(OrderVolume);
InitializeLongState(candle.ClosePrice);
}
private void TryEnterShort(ICandleMessage candle)
{
if (OrderVolume <= 0m || OrderVolume > MaxPositionVolume)
return;
if (_entriesInDirection >= MaxPositions)
return;
if (UseTradeHours && !IsWithinTradeHours(candle.CloseTime))
return;
if (Position > 0m)
{
SellMarket(Position);
ResetPositionState();
return;
}
if (Position != 0m)
return;
SellMarket(OrderVolume);
InitializeShortState(candle.ClosePrice);
}
private void InitializeLongState(decimal entryPrice)
{
_hasPosition = true;
_entryPrice = entryPrice;
_entriesInDirection = 1;
_blackSheepTriggered = false;
var stopDistance = StopLossPips > 0 ? ToPrice(StopLossPips) : (decimal?)null;
var takeDistance = TakeProfitPips > 0 ? ToPrice(TakeProfitPips) : (decimal?)null;
_longStop = stopDistance.HasValue ? entryPrice - stopDistance : null;
_longTake = takeDistance.HasValue ? entryPrice + takeDistance : null;
_shortStop = null;
_shortTake = null;
}
private void InitializeShortState(decimal entryPrice)
{
_hasPosition = true;
_entryPrice = entryPrice;
_entriesInDirection = 1;
_blackSheepTriggered = false;
var stopDistance = StopLossPips > 0 ? ToPrice(StopLossPips) : (decimal?)null;
var takeDistance = TakeProfitPips > 0 ? ToPrice(TakeProfitPips) : (decimal?)null;
_shortStop = stopDistance.HasValue ? entryPrice + stopDistance : null;
_shortTake = takeDistance.HasValue ? entryPrice - takeDistance : null;
_longStop = null;
_longTake = null;
}
private void ManageExits(ICandleMessage candle)
{
if (!_hasPosition)
return;
if (Position > 0m)
{
if (_longStop.HasValue && candle.LowPrice <= _longStop.Value)
{
SellMarket(Position);
ResetPositionState();
return;
}
if (_longTake.HasValue && candle.HighPrice >= _longTake.Value)
{
SellMarket(Position);
ResetPositionState();
return;
}
}
else if (Position < 0m)
{
if (_shortStop.HasValue && candle.HighPrice >= _shortStop.Value)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
return;
}
if (_shortTake.HasValue && candle.LowPrice <= _shortTake.Value)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
return;
}
}
}
private void UpdateTrailingStops(ICandleMessage candle)
{
if (!_hasPosition || TrailingStopPips <= 0 || _pipSize <= 0m)
return;
var distance = ToPrice(TrailingStopPips);
var step = TrailingStepPips > 0 ? ToPrice(TrailingStepPips) : 0m;
if (Position > 0m)
{
var threshold = candle.ClosePrice - (distance + step);
if (candle.ClosePrice - _entryPrice > distance + step && (!_longStop.HasValue || _longStop.Value < threshold))
_longStop = candle.ClosePrice - distance;
}
else if (Position < 0m)
{
var threshold = candle.ClosePrice + (distance + step);
if (_entryPrice - candle.ClosePrice > distance + step && (!_shortStop.HasValue || _shortStop.Value > threshold))
_shortStop = candle.ClosePrice + distance;
}
}
private void HandlePatternBreak()
{
if (_patternDirection == 0 || _blackSheepTriggered)
return;
switch (ClosingBehavior)
{
case ClosingModes.All:
ClosePosition();
break;
case ClosingModes.Opposite:
if (_patternDirection > 0 && Position < 0m)
ClosePosition();
else if (_patternDirection < 0 && Position > 0m)
ClosePosition();
break;
case ClosingModes.SameDirection:
if (_patternDirection > 0 && Position > 0m)
ClosePosition();
else if (_patternDirection < 0 && Position < 0m)
ClosePosition();
break;
}
_blackSheepTriggered = true;
_entriesInDirection = 0;
_patternDirection = 0;
}
private void ClosePosition()
{
if (Position > 0m)
SellMarket(Position);
else if (Position < 0m)
BuyMarket(Math.Abs(Position));
ResetPositionState();
}
private void ResetPositionState()
{
_hasPosition = Position != 0m;
_entriesInDirection = _hasPosition ? 1 : 0;
if (!_hasPosition)
{
_entryPrice = 0m;
_longStop = null;
_longTake = null;
_shortStop = null;
_shortTake = null;
}
}
private decimal CalculateOpenProfit(decimal currentPrice)
{
if (!_hasPosition || Position == 0m)
return 0m;
var volume = Math.Abs(Position);
return Position > 0m ? (currentPrice - _entryPrice) * volume : (_entryPrice - currentPrice) * volume;
}
private static int GetCandleDirection(ICandleMessage candle)
{
if (candle.OpenPrice < candle.ClosePrice)
return 1;
if (candle.OpenPrice > candle.ClosePrice)
return -1;
return 0;
}
private bool IsWithinTradeHours(DateTimeOffset time)
{
var hour = time.Hour;
return hour >= StartHour && hour <= EndHour;
}
private decimal ToPrice(int pips)
{
return pips * _pipSize;
}
private decimal CalculatePipSize()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
return 0.0001m;
var decimals = CountDecimals(step);
return decimals == 3 || decimals == 5 ? step * 10m : step;
}
private static int CountDecimals(decimal value)
{
value = Math.Abs(value);
var decimals = 0;
while (value != Math.Truncate(value) && decimals < 10)
{
value *= 10m;
decimals++;
}
return decimals;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Messages import DataType, CandleStates
from System import TimeSpan, Math
class n_candles_sequence_streak_strategy(Strategy):
def __init__(self):
super(n_candles_sequence_streak_strategy, self).__init__()
self._consecutive_candles = self.Param("ConsecutiveCandles", 4)
self._take_profit_pips = self.Param("TakeProfitPips", 50)
self._stop_loss_pips = self.Param("StopLossPips", 50)
self._trailing_stop_pips = self.Param("TrailingStopPips", 10)
self._trailing_step_pips = self.Param("TrailingStepPips", 4)
self._max_positions = self.Param("MaxPositions", 2)
self._use_trade_hours = self.Param("UseTradeHours", False)
self._start_hour = self.Param("StartHour", 11)
self._end_hour = self.Param("EndHour", 18)
self._min_profit = self.Param("MinProfit", 3.0)
self._closing_behavior = self.Param("ClosingBehavior", 0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._streak_count = 0
self._last_direction = 0
self._pattern_direction = 0
self._entries_in_direction = 0
self._black_sheep_triggered = False
self._has_position = False
self._entry_price = 0.0
self._pip_size = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(n_candles_sequence_streak_strategy, self).OnStarted2(time)
self._pip_size = self._calculate_pip_size()
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._update_trailing_stops(candle)
self._manage_floating_profit(candle)
direction = self._get_candle_direction(candle)
if direction == 0:
self._handle_pattern_break()
self._last_direction = 0
self._streak_count = 0
return
if self._last_direction == direction:
self._streak_count += 1
else:
if self._last_direction != 0:
self._handle_pattern_break()
self._last_direction = direction
self._streak_count = 1
if self._streak_count >= self._consecutive_candles.Value:
if self._pattern_direction != direction:
self._pattern_direction = direction
self._entries_in_direction = 0
self._black_sheep_triggered = False
if direction > 0:
self._try_enter_long(candle)
else:
self._try_enter_short(candle)
self._manage_exits(candle)
def _manage_floating_profit(self, candle):
if self._min_profit.Value <= 0 or self.Position == 0 or not self._has_position:
return
floating = self._calculate_open_profit(float(candle.ClosePrice))
if floating >= self._min_profit.Value:
self._close_position()
def _try_enter_long(self, candle):
if self._entries_in_direction >= self._max_positions.Value:
return
if self._use_trade_hours.Value and not self._is_within_trade_hours(candle.CloseTime):
return
if self.Position < 0:
self.BuyMarket(abs(self.Position))
self._reset_position_state()
return
if self.Position != 0:
return
self.BuyMarket()
self._initialize_long_state(float(candle.ClosePrice))
def _try_enter_short(self, candle):
if self._entries_in_direction >= self._max_positions.Value:
return
if self._use_trade_hours.Value and not self._is_within_trade_hours(candle.CloseTime):
return
if self.Position > 0:
self.SellMarket(self.Position)
self._reset_position_state()
return
if self.Position != 0:
return
self.SellMarket()
self._initialize_short_state(float(candle.ClosePrice))
def _initialize_long_state(self, entry_price):
self._has_position = True
self._entry_price = entry_price
self._entries_in_direction = 1
self._black_sheep_triggered = False
stop_distance = self._to_price(self._stop_loss_pips.Value) if self._stop_loss_pips.Value > 0 else None
take_distance = self._to_price(self._take_profit_pips.Value) if self._take_profit_pips.Value > 0 else None
self._long_stop = entry_price - stop_distance if stop_distance is not None else None
self._long_take = entry_price + take_distance if take_distance is not None else None
self._short_stop = None
self._short_take = None
def _initialize_short_state(self, entry_price):
self._has_position = True
self._entry_price = entry_price
self._entries_in_direction = 1
self._black_sheep_triggered = False
stop_distance = self._to_price(self._stop_loss_pips.Value) if self._stop_loss_pips.Value > 0 else None
take_distance = self._to_price(self._take_profit_pips.Value) if self._take_profit_pips.Value > 0 else None
self._short_stop = entry_price + stop_distance if stop_distance is not None else None
self._short_take = entry_price - take_distance if take_distance is not None else None
self._long_stop = None
self._long_take = None
def _manage_exits(self, candle):
if not self._has_position:
return
if self.Position > 0:
if self._long_stop is not None and float(candle.LowPrice) <= self._long_stop:
self.SellMarket(self.Position)
self._reset_position_state()
return
if self._long_take is not None and float(candle.HighPrice) >= self._long_take:
self.SellMarket(self.Position)
self._reset_position_state()
return
elif self.Position < 0:
if self._short_stop is not None and float(candle.HighPrice) >= self._short_stop:
self.BuyMarket(abs(self.Position))
self._reset_position_state()
return
if self._short_take is not None and float(candle.LowPrice) <= self._short_take:
self.BuyMarket(abs(self.Position))
self._reset_position_state()
return
def _update_trailing_stops(self, candle):
if not self._has_position or self._trailing_stop_pips.Value <= 0 or self._pip_size <= 0:
return
distance = self._to_price(self._trailing_stop_pips.Value)
step = self._to_price(self._trailing_step_pips.Value) if self._trailing_step_pips.Value > 0 else 0.0
if self.Position > 0:
threshold = float(candle.ClosePrice) - (distance + step)
if float(candle.ClosePrice) - self._entry_price > distance + step:
if self._long_stop is None or self._long_stop < threshold:
self._long_stop = float(candle.ClosePrice) - distance
elif self.Position < 0:
threshold = float(candle.ClosePrice) + (distance + step)
if self._entry_price - float(candle.ClosePrice) > distance + step:
if self._short_stop is None or self._short_stop > threshold:
self._short_stop = float(candle.ClosePrice) + distance
def _handle_pattern_break(self):
if self._pattern_direction == 0 or self._black_sheep_triggered:
return
closing = self._closing_behavior.Value
if closing == 0:
self._close_position()
elif closing == 1:
if self._pattern_direction > 0 and self.Position < 0:
self._close_position()
elif self._pattern_direction < 0 and self.Position > 0:
self._close_position()
elif closing == 2:
if self._pattern_direction > 0 and self.Position > 0:
self._close_position()
elif self._pattern_direction < 0 and self.Position < 0:
self._close_position()
self._black_sheep_triggered = True
self._entries_in_direction = 0
self._pattern_direction = 0
def _close_position(self):
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(abs(self.Position))
self._reset_position_state()
def _reset_position_state(self):
self._has_position = self.Position != 0
self._entries_in_direction = 1 if self._has_position else 0
if not self._has_position:
self._entry_price = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
def _calculate_open_profit(self, current_price):
if not self._has_position or self.Position == 0:
return 0.0
volume = abs(self.Position)
if self.Position > 0:
return (current_price - self._entry_price) * volume
else:
return (self._entry_price - current_price) * volume
def _get_candle_direction(self, candle):
if float(candle.OpenPrice) < float(candle.ClosePrice):
return 1
if float(candle.OpenPrice) > float(candle.ClosePrice):
return -1
return 0
def _is_within_trade_hours(self, time):
hour = time.Hour
return hour >= self._start_hour.Value and hour <= self._end_hour.Value
def _to_price(self, pips):
return pips * self._pip_size
def _calculate_pip_size(self):
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0
if step <= 0:
return 0.0001
decimals = self._count_decimals(step)
return step * 10.0 if decimals == 3 or decimals == 5 else step
def _count_decimals(self, value):
value = abs(value)
decimals = 0
while value != int(value) and decimals < 10:
value *= 10.0
decimals += 1
return decimals
def OnReseted(self):
super(n_candles_sequence_streak_strategy, self).OnReseted()
self._streak_count = 0
self._last_direction = 0
self._pattern_direction = 0
self._entries_in_direction = 0
self._black_sheep_triggered = False
self._has_position = False
self._entry_price = 0.0
self._pip_size = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
def CreateClone(self):
return n_candles_sequence_streak_strategy()