トレーリングストップとテイク戦略
概要
トレーリングストップとテイク戦略は、MQL/19963 の MetaTrader エキスパートアドバイザーの直接 StockSharp への適応です。アクティブなトレード管理に焦点を当てています:ポジションが開かれると、戦略は初期ストップロスとテイクプロフィットレベルを設定し、価格が動くにつれて両方のレベルを追従させます。トレーリング調整は設定可能な最小ステップサイズ、ブレークイーブン保護、およびトレードがまだ損失状態にある間はトレーリングを避けるオプションを遵守します。
戦略は完成したローソク足を使用して単一の証券で動作します。戦略がフラットの場合、最新のローソク足のボディの方向にポジションを開きます(強気のクローズはロングにつながり、弱気のクローズはショートにつながります)。これは MQL スクリプトが使用した元のテスト動作を反映し、トレーリングエンジンが管理するための継続的なポジションのフローを提供します。
仕組み
- 設定されたローソク足タイプをサブスクライブし、完成したローソク足のみを処理します。
- ポジションが開いていない場合、強気のローソク足でロングまたは弱気のローソク足でショートに入ります(ポジションタイプフィルターを遵守)。
- 新しいポジションで、
InitialStopLossPoints/InitialTakeProfitPointsを使用してストップロスとテイクプロフィットの距離を初期化します。これらがゼロの場合、代わりにトレーリング距離が使用されます。 - 各ローソク足のクローズで、更新されたトレーリングターゲットを計算します:
- ストップは市場がトレーリングステップ分進んだ後にのみ価格に近づきます。
- テイクプロフィットは価格が少なくともトレーリングステップ分引き戻された時に近づきます。
AllowTrailingLossが無効な場合、ブレークイーブン保護によってレベルが損失ゾーンに移動するのを防ぎます。
- 価格がトレーリングストップまたはテイクプロフィットレベルを越えた時、成行注文で出口し、すべての保存されたレベルをリセットします。
トレーリングロジック
ロングポジション
- 初期ストップはエントリーから少なくとも
SpreadMultiplier * PriceStepの距離に制限されます。 - 初期テイクプロフィットはエントリーより少なくとも同じ最小距離上に位置します。
- トレーリングストップはトレーリングステップとオプションのブレークイーブンフィルターを遵守しながら、
TrailingStopLossPointsだけクローズ価格を下方に追従します。 - トレーリングテイクプロフィットは価格が引き戻された時に締まり、損失のトレーリングが許可されていない場合はブレークイーブンレベルを下回りません。
ショートポジション
- 初期ストップはエントリーの上に設定され、スプレッド乗数距離より近くはなりません。
- 初期テイクプロフィットはエントリーの下から同じ最小距離ルールで開始します。
- トレーリングストップは価格が下落するにつれて下落しますが、損失トレーリングが許可されていない限りブレークイーブンより高く移動しません。
- トレーリングテイクプロフィットは引き戻しで価格に向かって上昇し、必要に応じてブレークイーブンに制限されます。
パラメーター
| パラメーター | 説明 |
|---|---|
CandleType |
価格評価に使用されるローソク足の集約。 |
Volume |
エントリーとエグジットのデフォルト注文数量。 |
PositionType |
エンジンをロングポジション、ショートポジション、または両方の管理に制限します。 |
InitialStopLossPoints |
価格ポイント単位の初期ストップロスサイズ(ゼロの場合はトレーリング距離を使用)。 |
InitialTakeProfitPoints |
価格ポイント単位の初期テイクプロフィットサイズ(ゼロの場合はトレーリング距離を使用)。 |
TrailingStopLossPoints |
価格とトレーリングストップ間の距離。 |
TrailingTakeProfitPoints |
価格とトレーリングテイクプロフィット間の距離。 |
TrailingStepPoints |
ストップまたはターゲットを調整する前に必要な最小ポイント移動。 |
AllowTrailingLoss |
トレードがまだブレークイーブン以下にある間もトレーリングを有効にします。 |
BreakevenPoints |
ブレークイーブンバリアを形成するためにエントリー価格に追加するポイントオフセット。 |
SpreadMultiplier |
最小ストップ距離近似のための乗数(MQL の StopLevel をシミュレート)。 |
注意事項
- ストップとターゲットはトリガーされた時に成行注文で実行され、実装をシンプルに保ち、元のストップ変更を反映します。
SpreadMultiplierは、ストップレベルが現在のスプレッドより近くに配置できないという MQL の動作を近似します。実行場所に合わせてこの値を調整してください。- 戦略は意図的に Python バージョンを避け、要求通り C# 実装のみに焦点を当てています。
- 必要に応じて、組み込みエントリーを無効にして外部注文を注入することで、トレーリングエンジンを独自のエントリーフィルターと組み合わせることを検討してください。
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 manages trailing stop-loss and take-profit levels similar to the original MQL Expert Advisor.
/// </summary>
public class TrailingStopAndTakeStrategy : Strategy
{
public enum TrailingPositionTypes
{
All,
Long,
Short,
}
private readonly StrategyParam<decimal> _epsilon;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<TrailingPositionTypes> _positionType;
private readonly StrategyParam<decimal> _initialStopLossPoints;
private readonly StrategyParam<decimal> _initialTakeProfitPoints;
private readonly StrategyParam<decimal> _trailingStopLossPoints;
private readonly StrategyParam<decimal> _trailingTakeProfitPoints;
private readonly StrategyParam<decimal> _trailingStepPoints;
private readonly StrategyParam<bool> _allowTrailingLoss;
private readonly StrategyParam<decimal> _breakevenPoints;
private readonly StrategyParam<int> _spreadMultiplier;
private decimal _priceStep;
private decimal _previousPosition;
private decimal? _longStop;
private decimal? _longTake;
private decimal? _shortStop;
private decimal? _shortTake;
private decimal _entryPrice;
/// <summary>
/// Initializes a new instance of the <see cref="TrailingStopAndTakeStrategy"/>.
/// </summary>
public TrailingStopAndTakeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
.SetDisplay("Candle Type", "Candle aggregation used for trailing decisions", "General");
_positionType = Param(nameof(PositionType), TrailingPositionTypes.All)
.SetDisplay("Position Filter", "Positions managed by the trailing engine", "Trading");
_initialStopLossPoints = Param(nameof(InitialStopLossPoints), 400m)
.SetRange(0m, 10000m)
.SetDisplay("Initial Stop", "Initial stop-loss size in price points", "Risk")
;
_initialTakeProfitPoints = Param(nameof(InitialTakeProfitPoints), 400m)
.SetRange(0m, 10000m)
.SetDisplay("Initial Take", "Initial take-profit size in price points", "Risk")
;
_trailingStopLossPoints = Param(nameof(TrailingStopLossPoints), 200m)
.SetRange(0m, 10000m)
.SetDisplay("Trailing Stop", "Trailing stop distance in price points", "Risk")
;
_trailingTakeProfitPoints = Param(nameof(TrailingTakeProfitPoints), 200m)
.SetRange(0m, 10000m)
.SetDisplay("Trailing Take", "Trailing take-profit distance in price points", "Risk")
;
_trailingStepPoints = Param(nameof(TrailingStepPoints), 10m)
.SetRange(0m, 1000m)
.SetDisplay("Trailing Step", "Minimum movement required before adjusting targets", "Risk");
_epsilon = Param(nameof(Epsilon), 0.0000001m)
.SetGreaterThanZero()
.SetDisplay("Trailing Epsilon", "Minimum trailing step size", "Risk");
_allowTrailingLoss = Param(nameof(AllowTrailingLoss), false)
.SetDisplay("Trail In Loss", "Allow trailing while position is not yet profitable", "Risk");
_breakevenPoints = Param(nameof(BreakevenPoints), 6m)
.SetRange(0m, 1000m)
.SetDisplay("Breakeven Points", "Profit offset used for breakeven protection", "Risk");
_spreadMultiplier = Param(nameof(SpreadMultiplier), 2)
.SetRange(1, 20)
.SetDisplay("Spread Multiplier", "Multiplier applied to minimal stop distance", "Execution");
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Position filter managed by the trailing logic.
/// </summary>
public TrailingPositionTypes PositionType
{
get => _positionType.Value;
set => _positionType.Value = value;
}
/// <summary>
/// Initial stop-loss size expressed in price points.
/// </summary>
public decimal InitialStopLossPoints
{
get => _initialStopLossPoints.Value;
set => _initialStopLossPoints.Value = value;
}
/// <summary>
/// Initial take-profit size expressed in price points.
/// </summary>
public decimal InitialTakeProfitPoints
{
get => _initialTakeProfitPoints.Value;
set => _initialTakeProfitPoints.Value = value;
}
/// <summary>
/// Trailing stop distance expressed in price points.
/// </summary>
public decimal TrailingStopLossPoints
{
get => _trailingStopLossPoints.Value;
set => _trailingStopLossPoints.Value = value;
}
/// <summary>
/// Trailing take-profit distance expressed in price points.
/// </summary>
public decimal TrailingTakeProfitPoints
{
get => _trailingTakeProfitPoints.Value;
set => _trailingTakeProfitPoints.Value = value;
}
/// <summary>
/// Minimum movement required before stops or targets are updated.
/// </summary>
public decimal TrailingStepPoints
{
get => _trailingStepPoints.Value;
set => _trailingStepPoints.Value = value;
}
/// <summary>
/// Minimum trailing step size used as a floor.
/// </summary>
public decimal Epsilon
{
get => _epsilon.Value;
set => _epsilon.Value = value;
}
/// <summary>
/// Enables trailing adjustments while the position remains in the loss zone.
/// </summary>
public bool AllowTrailingLoss
{
get => _allowTrailingLoss.Value;
set => _allowTrailingLoss.Value = value;
}
/// <summary>
/// Profit offset in points used to define the breakeven level.
/// </summary>
public decimal BreakevenPoints
{
get => _breakevenPoints.Value;
set => _breakevenPoints.Value = value;
}
/// <summary>
/// Multiplier applied to the minimal stop distance approximation.
/// </summary>
public int SpreadMultiplier
{
get => _spreadMultiplier.Value;
set => _spreadMultiplier.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_priceStep = Security?.PriceStep ?? 1m;
if (_priceStep <= 0m)
_priceStep = 1m;
_previousPosition = 0m;
ResetLevels();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_priceStep = 0m;
_previousPosition = 0m;
_entryPrice = 0m;
ResetLevels();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Handle long positions first.
if (Position > 0m)
{
if (PositionType == TrailingPositionTypes.Short)
{
ResetLongLevels();
}
else
{
if (_previousPosition <= 0m)
ResetShortLevels();
EnsureLongInitialized();
UpdateLongTrailing(candle);
ManageLongExits(candle);
}
}
else if (Position < 0m)
{
if (PositionType == TrailingPositionTypes.Long)
{
ResetShortLevels();
}
else
{
if (_previousPosition >= 0m)
ResetLongLevels();
EnsureShortInitialized();
UpdateShortTrailing(candle);
ManageShortExits(candle);
}
}
else
{
ResetLevels();
}
// Try to open a new position once flat.
TryEnter(candle);
_previousPosition = Position;
}
private void TryEnter(ICandleMessage candle)
{
if (Position != 0m || Volume <= 0m)
return;
// Simple directional entry mirroring the tester behavior from the MQL script.
if (PositionType == TrailingPositionTypes.Long)
{
if (candle.ClosePrice > candle.OpenPrice)
BuyMarket(Volume);
}
else if (PositionType == TrailingPositionTypes.Short)
{
if (candle.ClosePrice < candle.OpenPrice)
SellMarket(Volume);
}
else
{
if (candle.ClosePrice > candle.OpenPrice)
BuyMarket(Volume);
else if (candle.ClosePrice < candle.OpenPrice)
SellMarket(Volume);
}
}
private void EnsureLongInitialized()
{
if (Position <= 0m)
return;
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var minDistance = GetMinStopDistance();
if (_longStop == null)
{
var points = InitialStopLossPoints > 0m
? InitialStopLossPoints
: TrailingStopLossPoints > 0m ? TrailingStopLossPoints : 0m;
if (points > 0m)
{
var candidate = entryPrice - points * _priceStep;
var minAllowed = entryPrice - minDistance;
_longStop = Math.Min(candidate, minAllowed);
}
}
if (_longTake == null)
{
var points = InitialTakeProfitPoints > 0m
? InitialTakeProfitPoints
: TrailingTakeProfitPoints > 0m ? TrailingTakeProfitPoints : 0m;
if (points > 0m)
{
var candidate = entryPrice + points * _priceStep;
var minAllowed = entryPrice + minDistance;
_longTake = Math.Max(candidate, minAllowed);
}
}
}
private void EnsureShortInitialized()
{
if (Position >= 0m)
return;
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var minDistance = GetMinStopDistance();
if (_shortStop == null)
{
var points = InitialStopLossPoints > 0m
? InitialStopLossPoints
: TrailingStopLossPoints > 0m ? TrailingStopLossPoints : 0m;
if (points > 0m)
{
var candidate = entryPrice + points * _priceStep;
var minAllowed = entryPrice + minDistance;
_shortStop = Math.Max(candidate, minAllowed);
}
}
if (_shortTake == null)
{
var points = InitialTakeProfitPoints > 0m
? InitialTakeProfitPoints
: TrailingTakeProfitPoints > 0m ? TrailingTakeProfitPoints : 0m;
if (points > 0m)
{
var candidate = entryPrice - points * _priceStep;
var minAllowed = entryPrice - minDistance;
_shortTake = Math.Min(candidate, minAllowed);
}
}
}
private void UpdateLongTrailing(ICandleMessage candle)
{
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var breakeven = entryPrice + BreakevenPoints * _priceStep;
var trailingStep = Math.Max(TrailingStepPoints * _priceStep, Epsilon);
var minDistance = GetMinStopDistance();
if (TrailingStopLossPoints > 0m)
{
var candidate = candle.ClosePrice - TrailingStopLossPoints * _priceStep;
var minAllowed = candle.ClosePrice - minDistance;
var newStop = Math.Min(candidate, minAllowed);
if (!AllowTrailingLoss && newStop < breakeven)
{
// Skip moving the stop into the loss area when disabled.
}
else if (_longStop == null || newStop > _longStop.Value + trailingStep)
{
_longStop = newStop;
}
}
if (TrailingTakeProfitPoints > 0m)
{
var candidate = candle.ClosePrice + TrailingTakeProfitPoints * _priceStep;
var minAllowed = candle.ClosePrice + minDistance;
var newTake = Math.Max(candidate, minAllowed);
if (!AllowTrailingLoss && newTake < breakeven)
newTake = breakeven;
if (_longTake == null || newTake < _longTake.Value - trailingStep)
_longTake = newTake;
}
}
private void UpdateShortTrailing(ICandleMessage candle)
{
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var breakeven = entryPrice - BreakevenPoints * _priceStep;
var trailingStep = Math.Max(TrailingStepPoints * _priceStep, Epsilon);
var minDistance = GetMinStopDistance();
if (TrailingStopLossPoints > 0m)
{
var candidate = candle.ClosePrice + TrailingStopLossPoints * _priceStep;
var minAllowed = candle.ClosePrice + minDistance;
var newStop = Math.Max(candidate, minAllowed);
if (!AllowTrailingLoss && newStop > breakeven)
{
// Skip moving the stop into the loss area when disabled.
}
else if (_shortStop == null || newStop < _shortStop.Value - trailingStep)
{
_shortStop = newStop;
}
}
if (TrailingTakeProfitPoints > 0m)
{
var candidate = candle.ClosePrice - TrailingTakeProfitPoints * _priceStep;
var minAllowed = candle.ClosePrice - minDistance;
var newTake = Math.Min(candidate, minAllowed);
if (!AllowTrailingLoss && newTake > breakeven)
newTake = breakeven;
if (_shortTake == null || newTake > _shortTake.Value + trailingStep)
_shortTake = newTake;
}
}
private void ManageLongExits(ICandleMessage candle)
{
if (_longStop.HasValue && candle.LowPrice <= _longStop.Value)
{
SellMarket(Position);
ResetLongLevels();
return;
}
if (_longTake.HasValue && candle.HighPrice >= _longTake.Value)
{
SellMarket(Position);
ResetLongLevels();
}
}
private void ManageShortExits(ICandleMessage candle)
{
if (_shortStop.HasValue && candle.HighPrice >= _shortStop.Value)
{
BuyMarket(Math.Abs(Position));
ResetShortLevels();
return;
}
if (_shortTake.HasValue && candle.LowPrice <= _shortTake.Value)
{
BuyMarket(Math.Abs(Position));
ResetShortLevels();
}
}
private decimal GetMinStopDistance()
{
var multiplier = SpreadMultiplier < 1 ? 1 : SpreadMultiplier;
return _priceStep * multiplier;
}
private void ResetLevels()
{
ResetLongLevels();
ResetShortLevels();
}
private void ResetLongLevels()
{
_longStop = null;
_longTake = null;
}
private void ResetShortLevels()
{
_shortStop = null;
_shortTake = null;
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (trade?.Trade == null) return;
if (Position != 0 && _entryPrice == 0m)
_entryPrice = trade.Trade.Price;
if (Position == 0)
_entryPrice = 0m;
}
}
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 trailing_stop_and_take_strategy(Strategy):
POS_ALL = 0
POS_LONG = 1
POS_SHORT = 2
def __init__(self):
super(trailing_stop_and_take_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1)))
self._position_type = self.Param("PositionType", self.POS_ALL)
self._initial_stop_loss_points = self.Param("InitialStopLossPoints", 400.0)
self._initial_take_profit_points = self.Param("InitialTakeProfitPoints", 400.0)
self._trailing_stop_loss_points = self.Param("TrailingStopLossPoints", 200.0)
self._trailing_take_profit_points = self.Param("TrailingTakeProfitPoints", 200.0)
self._trailing_step_points = self.Param("TrailingStepPoints", 10.0)
self._epsilon = self.Param("Epsilon", 0.0000001)
self._allow_trailing_loss = self.Param("AllowTrailingLoss", False)
self._breakeven_points = self.Param("BreakevenPoints", 6.0)
self._spread_multiplier = self.Param("SpreadMultiplier", 2)
self._price_step = 1.0
self._previous_position = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def PositionType(self):
return self._position_type.Value
@property
def InitialStopLossPoints(self):
return self._initial_stop_loss_points.Value
@property
def InitialTakeProfitPoints(self):
return self._initial_take_profit_points.Value
@property
def TrailingStopLossPoints(self):
return self._trailing_stop_loss_points.Value
@property
def TrailingTakeProfitPoints(self):
return self._trailing_take_profit_points.Value
@property
def TrailingStepPoints(self):
return self._trailing_step_points.Value
@property
def Epsilon(self):
return self._epsilon.Value
@property
def AllowTrailingLoss(self):
return self._allow_trailing_loss.Value
@property
def BreakevenPoints(self):
return self._breakeven_points.Value
@property
def SpreadMultiplier(self):
return self._spread_multiplier.Value
def OnStarted2(self, time):
super(trailing_stop_and_take_strategy, self).OnStarted2(time)
sec = self.Security
self._price_step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 1.0
self._previous_position = 0.0
self._reset_levels()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
pos = float(self.Position)
if pos > 0:
if self.PositionType == self.POS_SHORT:
self._reset_long_levels()
else:
if self._previous_position <= 0:
self._reset_short_levels()
self._ensure_long_initialized()
self._update_long_trailing(candle)
self._manage_long_exits(candle)
elif pos < 0:
if self.PositionType == self.POS_LONG:
self._reset_short_levels()
else:
if self._previous_position >= 0:
self._reset_long_levels()
self._ensure_short_initialized()
self._update_short_trailing(candle)
self._manage_short_exits(candle)
else:
self._reset_levels()
self._entry_price = 0.0
self._try_enter(candle)
self._previous_position = float(self.Position)
def _try_enter(self, candle):
pos = float(self.Position)
vol = float(self.Volume)
if pos != 0 or vol <= 0:
return
close = float(candle.ClosePrice)
if self.PositionType == self.POS_LONG:
if close > float(candle.OpenPrice):
self.BuyMarket(vol)
self._entry_price = close
elif self.PositionType == self.POS_SHORT:
if close < float(candle.OpenPrice):
self.SellMarket(vol)
self._entry_price = close
else:
if close > float(candle.OpenPrice):
self.BuyMarket(vol)
self._entry_price = close
elif close < float(candle.OpenPrice):
self.SellMarket(vol)
self._entry_price = close
def _ensure_long_initialized(self):
if float(self.Position) <= 0:
return
entry = self._entry_price
if entry <= 0:
return
min_dist = self._get_min_stop_distance()
if self._long_stop is None:
pts = float(self.InitialStopLossPoints) if float(self.InitialStopLossPoints) > 0 else (float(self.TrailingStopLossPoints) if float(self.TrailingStopLossPoints) > 0 else 0.0)
if pts > 0:
candidate = entry - pts * self._price_step
min_allowed = entry - min_dist
self._long_stop = min(candidate, min_allowed)
if self._long_take is None:
pts = float(self.InitialTakeProfitPoints) if float(self.InitialTakeProfitPoints) > 0 else (float(self.TrailingTakeProfitPoints) if float(self.TrailingTakeProfitPoints) > 0 else 0.0)
if pts > 0:
candidate = entry + pts * self._price_step
min_allowed = entry + min_dist
self._long_take = max(candidate, min_allowed)
def _ensure_short_initialized(self):
if float(self.Position) >= 0:
return
entry = self._entry_price
if entry <= 0:
return
min_dist = self._get_min_stop_distance()
if self._short_stop is None:
pts = float(self.InitialStopLossPoints) if float(self.InitialStopLossPoints) > 0 else (float(self.TrailingStopLossPoints) if float(self.TrailingStopLossPoints) > 0 else 0.0)
if pts > 0:
candidate = entry + pts * self._price_step
min_allowed = entry + min_dist
self._short_stop = max(candidate, min_allowed)
if self._short_take is None:
pts = float(self.InitialTakeProfitPoints) if float(self.InitialTakeProfitPoints) > 0 else (float(self.TrailingTakeProfitPoints) if float(self.TrailingTakeProfitPoints) > 0 else 0.0)
if pts > 0:
candidate = entry - pts * self._price_step
min_allowed = entry - min_dist
self._short_take = min(candidate, min_allowed)
def _update_long_trailing(self, candle):
entry = self._entry_price
if entry <= 0:
return
breakeven = entry + float(self.BreakevenPoints) * self._price_step
trailing_step = max(float(self.TrailingStepPoints) * self._price_step, float(self.Epsilon))
min_dist = self._get_min_stop_distance()
if float(self.TrailingStopLossPoints) > 0:
candidate = float(candle.ClosePrice) - float(self.TrailingStopLossPoints) * self._price_step
min_allowed = float(candle.ClosePrice) - min_dist
new_stop = min(candidate, min_allowed)
if not self.AllowTrailingLoss and new_stop < breakeven:
pass
elif self._long_stop is None or new_stop > self._long_stop + trailing_step:
self._long_stop = new_stop
if float(self.TrailingTakeProfitPoints) > 0:
candidate = float(candle.ClosePrice) + float(self.TrailingTakeProfitPoints) * self._price_step
min_allowed = float(candle.ClosePrice) + min_dist
new_take = max(candidate, min_allowed)
if not self.AllowTrailingLoss and new_take < breakeven:
new_take = breakeven
if self._long_take is None or new_take < self._long_take - trailing_step:
self._long_take = new_take
def _update_short_trailing(self, candle):
entry = self._entry_price
if entry <= 0:
return
breakeven = entry - float(self.BreakevenPoints) * self._price_step
trailing_step = max(float(self.TrailingStepPoints) * self._price_step, float(self.Epsilon))
min_dist = self._get_min_stop_distance()
if float(self.TrailingStopLossPoints) > 0:
candidate = float(candle.ClosePrice) + float(self.TrailingStopLossPoints) * self._price_step
min_allowed = float(candle.ClosePrice) + min_dist
new_stop = max(candidate, min_allowed)
if not self.AllowTrailingLoss and new_stop > breakeven:
pass
elif self._short_stop is None or new_stop < self._short_stop - trailing_step:
self._short_stop = new_stop
if float(self.TrailingTakeProfitPoints) > 0:
candidate = float(candle.ClosePrice) - float(self.TrailingTakeProfitPoints) * self._price_step
min_allowed = float(candle.ClosePrice) - min_dist
new_take = min(candidate, min_allowed)
if not self.AllowTrailingLoss and new_take > breakeven:
new_take = breakeven
if self._short_take is None or new_take > self._short_take + trailing_step:
self._short_take = new_take
def _manage_long_exits(self, candle):
if self._long_stop is not None and float(candle.LowPrice) <= self._long_stop:
self.SellMarket(float(self.Position))
self._reset_long_levels()
return
if self._long_take is not None and float(candle.HighPrice) >= self._long_take:
self.SellMarket(float(self.Position))
self._reset_long_levels()
def _manage_short_exits(self, candle):
if self._short_stop is not None and float(candle.HighPrice) >= self._short_stop:
self.BuyMarket(abs(float(self.Position)))
self._reset_short_levels()
return
if self._short_take is not None and float(candle.LowPrice) <= self._short_take:
self.BuyMarket(abs(float(self.Position)))
self._reset_short_levels()
def _get_min_stop_distance(self):
mult = self.SpreadMultiplier if self.SpreadMultiplier >= 1 else 1
return self._price_step * mult
def _reset_levels(self):
self._reset_long_levels()
self._reset_short_levels()
def _reset_long_levels(self):
self._long_stop = None
self._long_take = None
def _reset_short_levels(self):
self._short_stop = None
self._short_take = None
def OnOwnTradeReceived(self, trade):
super(trailing_stop_and_take_strategy, self).OnOwnTradeReceived(trade)
if trade is None or trade.Trade is None:
return
pos = float(self.Position)
if pos != 0 and self._entry_price == 0:
self._entry_price = float(trade.Trade.Price)
if pos == 0:
self._entry_price = 0.0
def OnReseted(self):
super(trailing_stop_and_take_strategy, self).OnReseted()
self._price_step = 0.0
self._previous_position = 0.0
self._entry_price = 0.0
self._reset_levels()
def CreateClone(self):
return trailing_stop_and_take_strategy()