ディーラートレード v7.51 RIVOT (C#)
概要
Dealers Trade v7.51 は、もともと MetaTrader 4 エキスパート アドバイザー Dealers_Trade_v_7.51_RIVOT.mq4 として提供されたマーチンゲール スタイルのグリッド戦略です。このポートは、ピボットベースの方向性バイアスから離れて取引するという元のアイデアを維持し、設定可能なピップ距離だけ価格がリトレースするたびに支配的な側にスケールします。 StockSharp の実装では、高レベルの戦略ヘルパーを使用して、ローソク足のサブスクライブ、ピボット ゾーンの計算、ポジションのサイジング、リスク、手仕舞いの管理を行います。
取引ロジック
ピボット フレームワーク
- この戦略では、完成したローソク足ごとに 2 つの参照価格を構築します。
- クラシック ピボット (
P) =(previous high + previous low + previous close + current open) / 4。 - フローティング ピボット (
FLP) =(current high + current low + current close) / 3。
- クラシック ピボット (
- 現在の足の取引を可能にするには、
PとFLPの間のピップの差がGapThreshold以上である必要があります。
- この戦略では、完成したローソク足ごとに 2 つの参照価格を構築します。
方向性の偏り
- ローソク足の終値が両方のピボットの上にあり、ギャップ フィルターが満たされると、バイアスは ロング に切り替わります。
- ローソク足の終値が両方のピボットを下回り、ギャップが確認されると、バイアスはショートに切り替わります。
- バイアスは、一連のポジションが完全にクローズされるか、一連のポジションが終了した後に反対の状態が現れるまで有効です。
エントリーのスケーリング
- 一度にアクティブにできる取引は 1 つのシリーズのみです。
- 最初のエントリはバイアスの直後に続きます。
- 追加のエントリーは、価格が最新の約定から少なくとも
PipDistanceピップだけアクティブ バイアスに対してリトレースした場合にのみオープンされ、元のマーチンゲール平均法をエミュレートします。 - 新しい注文ごとに前のサイズに
VolumeMultiplierが掛けられますが、MaxVolumeを超えることはありません。 - スタックされるエントリの数は
MaxTradesによって制限されます。
リスク管理
- 出来高加重平均エントリーからの
StopLossピップでのハードストップロスにより、シリーズ全体が終了します。 - 価格が有利に戻れば、
TakeProfitピップでの固定テイクプロフィットにより利益が確定します。 - 有効にすると、トレーリングストップは、平均エントリーを超えて
TrailingStopピップを超えるたびに、価格に近づくことで利益を動的にロックします。
- 出来高加重平均エントリーからの
リセット条件
- 完全なエグジット(ストップロス、テイクプロフィット、トレーリングストップ、または手動でのポジションフラット化)により、マーチンゲールカウンターがリセットされ、方向性のバイアスが除去されます。
パラメーター
| パラメータ | デフォルト | 説明 |
|---|---|---|
Volume |
1 | 最初のエントリの基本注文サイズ。 |
MaxTrades |
5 | シリーズごとの平均エントリの最大数。 |
PipDistance |
4 | 新しいポジションを追加する前に必要な最小限の逆方向の動き (ピップ単位)。 |
TakeProfit |
15 | 出来高加重平均エントリーからグリッド全体を利益で閉じるまでの距離。 |
StopLoss |
90 | 保護的出口をトリガーする平均的な入口からの距離。 |
TrailingStop |
15 | 価格が有利に推移するとトレーリングストップオフセットが適用されます。末尾を無効にするには、ゼロに設定します。 |
VolumeMultiplier |
1.5 | 後続の各エントリの注文サイズを増やすために使用される係数。 |
MaxVolume |
5 | 乗数を適用した後の単一オーダー量の上限。 |
GapThreshold |
7 | バイアスを有効にするために必要なクラシック ピボットとフローティング ピボットの間の最小ギャップ (ピップ単位)。 |
CandleType |
15分足のローソク足 | 計算や意思決定に使用されるローソクの種類。 |
すべてのパラメータは StrategyParam<T> を通じて設定されるため、StockSharp デザイナーまたはストラテジー ランナー内で最適化できます。
使用上の注意
- この戦略はローソク足データのみに依存します。直接のティックレベルの買い/売りストリームは必要ありません。データ プロバイダーが選択した
CandleTypeを配信できることを確認してください。 - StockSharp はデフォルトでポジションを集計するため、実装では MT4 グリッド ブックをエミュレートするために内部出来高加重平均が維持されます。部分的な約定が発生した場合、組み込みのポジション計算機能により値の一貫性が維持されます。
- グラフのレンダリングでは、利用可能な場合、グラフ領域に 2 本の水平線 (
PivotとFloatingPivot) が追加されます。 - 自動逆取引はありません。システムは、バイアス フリップを受け入れる前に、進行中のシリーズが終了するのを待ちます。
MQL バージョンとの違い
- 元のスクリプトでは、MT4 チャートに複数のラベルとコメントを描画しました。ポートは機能的な取引ロジックのみを保持し、ビジュアルを StockSharp のチャート線に置き換えます。
- 未決済注文の合計に基づくアカウント保護機能、手動マジックナンバーフィルタリング、シンボル固有の PIP 値テーブルは、StockSharp では必要とされず、省略されました。
- MetaTrader コードの正確なティック価格 (
Ask == tp) での注文の決済は、ローソク足の終値での価格比較で近似されます。 - 取引管理は、MT4 チケット ループではなく成行注文 (
BuyMarket/SellMarket) を使用して実装されます。トレーリングストップとエグジットはローソク足の更新時に発生します。
ベストプラクティス
- 運用を開始する前に、常に現実的なスプレッド/手数料モデルを使用して紙取引または履歴シミュレーションで戦略をテストしてください。
- ドローダウンを制御するには、ボラティリティの高い商品の
VolumeMultiplierまたはMaxTradesを下げることを検討してください。 - 日中商品の場合は、元の設定のデータ粒度に一致するように
CandleTypeを調整します (デフォルトは 15 分ですが、M15 と H1 では EA が頻繁に使用されました)。
ファイル
CS/DealersTradeV751RivotStrategy.cs– メインの C# 実装。README_zh.md– 中国語のドキュメント。README_ru.md– ロシア語のドキュメント。
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>
/// Dealers Trade v7.51 strategy ported from MetaTrader 4 implementation.
/// Builds directional bias from classic pivot and floating pivot levels
/// and scales into the bias when price retraces by a fixed pip distance.
/// Applies martingale-style position sizing with configurable stop-loss,
/// take-profit, and trailing-stop management.
/// </summary>
public class DealersTradeV751RivotStrategy : Strategy
{
private readonly StrategyParam<int> _maxTrades;
private readonly StrategyParam<decimal> _pipDistance;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<decimal> _volumeMultiplier;
private readonly StrategyParam<decimal> _maxVolume;
private readonly StrategyParam<decimal> _gapThreshold;
private readonly StrategyParam<DataType> _candleType;
private ICandleMessage _previousCandle;
private decimal _pivotLevel;
private decimal _floatingPivot;
private decimal _gapInPips;
private decimal _lastEntryPrice;
private decimal _averageEntryPrice;
private decimal? _trailingStopLevel;
private int _direction; // -1 short, 0 neutral, 1 long
private int _entriesInSeries;
/// <summary>
/// Maximum number of entries allowed in one scaling series.
/// </summary>
public int MaxTrades
{
get => _maxTrades.Value;
set => _maxTrades.Value = value;
}
/// <summary>
/// Distance in pips between martingale entries.
/// </summary>
public decimal PipDistance
{
get => _pipDistance.Value;
set => _pipDistance.Value = value;
}
/// <summary>
/// Take-profit distance in pips.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop-loss distance in pips.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Trailing-stop distance in pips.
/// </summary>
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Multiplier applied to volume for each additional entry.
/// </summary>
public decimal VolumeMultiplier
{
get => _volumeMultiplier.Value;
set => _volumeMultiplier.Value = value;
}
/// <summary>
/// Maximum allowed volume for a single entry.
/// </summary>
public decimal MaxVolume
{
get => _maxVolume.Value;
set => _maxVolume.Value = value;
}
/// <summary>
/// Minimum pivot gap in pips required to activate the bias.
/// </summary>
public decimal GapThreshold
{
get => _gapThreshold.Value;
set => _gapThreshold.Value = value;
}
/// <summary>
/// Type of candles used for pivot calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public DealersTradeV751RivotStrategy()
{
_maxTrades = Param(nameof(MaxTrades), 2)
.SetGreaterThanZero()
.SetDisplay("Max Trades", "Maximum number of martingale entries", "Position Sizing")
.SetOptimize(1, 10, 1);
_pipDistance = Param(nameof(PipDistance), 10m)
.SetGreaterThanZero()
.SetDisplay("Pip Distance", "Distance between averaged entries in pips", "Position Sizing")
.SetOptimize(2m, 15m, 1m);
_takeProfit = Param(nameof(TakeProfit), 15m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take-profit distance in pips", "Risk Management")
.SetOptimize(5m, 50m, 5m);
_stopLoss = Param(nameof(StopLoss), 90m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop-loss distance in pips", "Risk Management")
.SetOptimize(30m, 200m, 10m);
_trailingStop = Param(nameof(TrailingStop), 15m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Trailing-stop distance in pips", "Risk Management")
.SetOptimize(5m, 40m, 5m);
_volumeMultiplier = Param(nameof(VolumeMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Volume Multiplier", "Multiplier applied after each new entry", "Position Sizing")
.SetOptimize(1.1m, 3m, 0.1m);
_maxVolume = Param(nameof(MaxVolume), 5m)
.SetGreaterThanZero()
.SetDisplay("Max Volume", "Upper limit for single-entry volume", "Position Sizing");
_gapThreshold = Param(nameof(GapThreshold), 15m)
.SetGreaterThanZero()
.SetDisplay("Gap Threshold", "Minimal pivot gap required to enable trading", "Signal")
.SetOptimize(3m, 15m, 1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for pivot calculations", "Signal");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetSeries();
_previousCandle = null;
_pivotLevel = 0m;
_floatingPivot = 0m;
_gapInPips = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
/// <inheritdoc />
protected override void OnPositionReceived(Position position)
{
base.OnPositionReceived(position);
if (Position == 0m)
{
// Reset martingale state once the position is closed externally.
ResetSeries();
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_previousCandle == null)
{
_previousCandle = candle;
return;
}
UpdatePivots(candle);
if (Position == 0m && _entriesInSeries > 0)
{
// Force reset when no exposure remains but scaling data still exists.
ResetSeries();
}
if (_entriesInSeries > 0)
{
ManageRisk(candle.ClosePrice);
}
if (_entriesInSeries >= MaxTrades)
{
_previousCandle = candle;
return;
}
if (_direction == 0)
{
EvaluateDirection(candle);
}
TryEnter(candle);
_previousCandle = candle;
}
private void UpdatePivots(ICandleMessage candle)
{
var step = GetPriceStep();
_pivotLevel = (_previousCandle!.HighPrice + _previousCandle.LowPrice + _previousCandle.ClosePrice + candle.OpenPrice) / 4m;
_floatingPivot = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
_gapInPips = step == 0m ? 0m : Math.Abs(_pivotLevel - _floatingPivot) / step;
}
private void EvaluateDirection(ICandleMessage candle)
{
var price = candle.ClosePrice;
if (price > _pivotLevel && price > _floatingPivot && _gapInPips >= GapThreshold)
{
_direction = 1;
LogInfo($"Bias switched to long. Pivot={_pivotLevel:F5}, Floating={_floatingPivot:F5}, Gap={_gapInPips:F2} pips.");
}
else if (price < _pivotLevel && price < _floatingPivot && _gapInPips >= GapThreshold)
{
_direction = -1;
LogInfo($"Bias switched to short. Pivot={_pivotLevel:F5}, Floating={_floatingPivot:F5}, Gap={_gapInPips:F2} pips.");
}
}
private void TryEnter(ICandleMessage candle)
{
if (_direction == 0)
return;
var price = candle.ClosePrice;
var step = GetPriceStep();
var distance = PipDistance * step;
if (_direction > 0)
{
if (_entriesInSeries == 0 || (_lastEntryPrice - price) >= distance)
{
EnterLong(price);
}
}
else
{
if (_entriesInSeries == 0 || (price - _lastEntryPrice) >= distance)
{
EnterShort(price);
}
}
}
private void EnterLong(decimal price)
{
var volume = CalculateNextVolume();
_lastEntryPrice = price;
_averageEntryPrice = UpdateAveragePrice(price, volume, true);
_entriesInSeries++;
LogInfo($"Opening long entry #{_entriesInSeries} at {price:F5} with volume {volume}.");
BuyMarket(volume);
}
private void EnterShort(decimal price)
{
var volume = CalculateNextVolume();
_lastEntryPrice = price;
_averageEntryPrice = UpdateAveragePrice(price, volume, false);
_entriesInSeries++;
LogInfo($"Opening short entry #{_entriesInSeries} at {price:F5} with volume {volume}.");
SellMarket(volume);
}
private decimal CalculateNextVolume()
{
var volume = Volume;
for (var i = 0; i < _entriesInSeries; i++)
{
volume *= VolumeMultiplier;
if (volume >= MaxVolume)
{
volume = MaxVolume;
break;
}
}
var volumeStep = Security?.VolumeStep ?? 0.01m;
if (volumeStep > 0m)
{
volume = Math.Ceiling(volume / volumeStep) * volumeStep;
}
return volume;
}
private decimal UpdateAveragePrice(decimal price, decimal volume, bool isLong)
{
var existingVolume = Math.Abs(Position);
var side = isLong ? 1m : -1m;
if (existingVolume <= 0m)
{
return price;
}
var totalVolume = existingVolume + volume;
var weightedAverage = ((_averageEntryPrice * existingVolume * side) + (price * volume)) / totalVolume;
return Math.Abs(weightedAverage);
}
private void ManageRisk(decimal price)
{
if (_entriesInSeries == 0)
{
_trailingStopLevel = null;
return;
}
var step = GetPriceStep();
var stopDistance = StopLoss * step;
var takeDistance = TakeProfit * step;
var trailingDistance = TrailingStop * step;
if (_direction > 0)
{
var lossLevel = _averageEntryPrice - stopDistance;
var profitLevel = _averageEntryPrice + takeDistance;
if (price <= lossLevel)
{
LogInfo($"Long stop-loss triggered at {price:F5}. Average entry {_averageEntryPrice:F5}.");
SellMarket(Math.Abs(Position));
ResetSeries();
return;
}
if (price >= profitLevel)
{
LogInfo($"Long take-profit triggered at {price:F5}. Average entry {_averageEntryPrice:F5}.");
SellMarket(Math.Abs(Position));
ResetSeries();
return;
}
if (TrailingStop > 0m)
{
var candidate = price - trailingDistance;
if (_trailingStopLevel == null || candidate > _trailingStopLevel)
{
_trailingStopLevel = candidate;
}
if (_trailingStopLevel != null && price <= _trailingStopLevel)
{
LogInfo($"Long trailing stop activated at {price:F5}.");
SellMarket(Math.Abs(Position));
ResetSeries();
}
}
}
else if (_direction < 0)
{
var lossLevel = _averageEntryPrice + stopDistance;
var profitLevel = _averageEntryPrice - takeDistance;
if (price >= lossLevel)
{
LogInfo($"Short stop-loss triggered at {price:F5}. Average entry {_averageEntryPrice:F5}.");
BuyMarket(Math.Abs(Position));
ResetSeries();
return;
}
if (price <= profitLevel)
{
LogInfo($"Short take-profit triggered at {price:F5}. Average entry {_averageEntryPrice:F5}.");
BuyMarket(Math.Abs(Position));
ResetSeries();
return;
}
if (TrailingStop > 0m)
{
var candidate = price + trailingDistance;
if (_trailingStopLevel == null || candidate < _trailingStopLevel)
{
_trailingStopLevel = candidate;
}
if (_trailingStopLevel != null && price >= _trailingStopLevel)
{
LogInfo($"Short trailing stop activated at {price:F5}.");
BuyMarket(Math.Abs(Position));
ResetSeries();
}
}
}
}
private decimal GetPriceStep()
{
var step = Security?.PriceStep ?? 0m;
if (step == 0m)
{
// Fallback to four decimal places when instrument metadata is unknown.
step = 0.0001m;
}
return step;
}
private void ResetSeries()
{
_direction = 0;
_entriesInSeries = 0;
_lastEntryPrice = 0m;
_averageEntryPrice = 0m;
_trailingStopLevel = null;
}
}
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 dealers_trade_v751_rivot_strategy(Strategy):
"""
Dealers Trade v7.51 strategy ported from MetaTrader 4.
Builds directional bias from classic pivot and floating pivot levels,
scales into the bias when price retraces by a fixed pip distance.
Applies martingale-style position sizing with SL/TP and trailing stop.
"""
def __init__(self):
super(dealers_trade_v751_rivot_strategy, self).__init__()
self._max_trades = self.Param("MaxTrades", 2) \
.SetDisplay("Max Trades", "Maximum number of martingale entries", "Position Sizing")
self._pip_distance = self.Param("PipDistance", 10.0) \
.SetDisplay("Pip Distance", "Distance between averaged entries in pips", "Position Sizing")
self._take_profit = self.Param("TakeProfit", 15.0) \
.SetDisplay("Take Profit", "Take-profit distance in pips", "Risk Management")
self._stop_loss = self.Param("StopLoss", 90.0) \
.SetDisplay("Stop Loss", "Stop-loss distance in pips", "Risk Management")
self._trailing_stop = self.Param("TrailingStop", 15.0) \
.SetDisplay("Trailing Stop", "Trailing-stop distance in pips", "Risk Management")
self._volume_multiplier = self.Param("VolumeMultiplier", 1.5) \
.SetDisplay("Volume Multiplier", "Multiplier applied after each new entry", "Position Sizing")
self._max_volume = self.Param("MaxVolume", 5.0) \
.SetDisplay("Max Volume", "Upper limit for single-entry volume", "Position Sizing")
self._gap_threshold = self.Param("GapThreshold", 15.0) \
.SetDisplay("Gap Threshold", "Minimal pivot gap required to enable trading", "Signal")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles used for pivot calculations", "Signal")
self._previous_candle = None
self._pivot_level = 0.0
self._floating_pivot = 0.0
self._gap_in_pips = 0.0
self._last_entry_price = 0.0
self._average_entry_price = 0.0
self._trailing_stop_level = None
self._direction = 0
self._entries_in_series = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(dealers_trade_v751_rivot_strategy, self).OnReseted()
self._reset_series()
self._previous_candle = None
self._pivot_level = 0.0
self._floating_pivot = 0.0
self._gap_in_pips = 0.0
def OnStarted2(self, time):
super(dealers_trade_v751_rivot_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnPositionReceived(self, position):
super(dealers_trade_v751_rivot_strategy, self).OnPositionReceived(position)
if self.Position == 0:
self._reset_series()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._previous_candle is None:
self._previous_candle = candle
return
self._update_pivots(candle)
if self.Position == 0 and self._entries_in_series > 0:
self._reset_series()
if self._entries_in_series > 0:
self._manage_risk(float(candle.ClosePrice))
if self._entries_in_series >= self._max_trades.Value:
self._previous_candle = candle
return
if self._direction == 0:
self._evaluate_direction(candle)
self._try_enter(candle)
self._previous_candle = candle
def _update_pivots(self, candle):
step = self._get_price_step()
self._pivot_level = (float(self._previous_candle.HighPrice) + float(self._previous_candle.LowPrice) +
float(self._previous_candle.ClosePrice) + float(candle.OpenPrice)) / 4.0
self._floating_pivot = (float(candle.HighPrice) + float(candle.LowPrice) + float(candle.ClosePrice)) / 3.0
self._gap_in_pips = abs(self._pivot_level - self._floating_pivot) / step if step != 0 else 0.0
def _evaluate_direction(self, candle):
price = float(candle.ClosePrice)
if price > self._pivot_level and price > self._floating_pivot and self._gap_in_pips >= self._gap_threshold.Value:
self._direction = 1
elif price < self._pivot_level and price < self._floating_pivot and self._gap_in_pips >= self._gap_threshold.Value:
self._direction = -1
def _try_enter(self, candle):
if self._direction == 0:
return
price = float(candle.ClosePrice)
step = self._get_price_step()
distance = self._pip_distance.Value * step
if self._direction > 0:
if self._entries_in_series == 0 or (self._last_entry_price - price) >= distance:
self._enter_long(price)
else:
if self._entries_in_series == 0 or (price - self._last_entry_price) >= distance:
self._enter_short(price)
def _enter_long(self, price):
self._last_entry_price = price
existing_volume = abs(float(self.Position))
if existing_volume <= 0:
self._average_entry_price = price
else:
total = existing_volume + 1.0
self._average_entry_price = abs((self._average_entry_price * existing_volume + price) / total)
self._entries_in_series += 1
self.BuyMarket()
def _enter_short(self, price):
self._last_entry_price = price
existing_volume = abs(float(self.Position))
if existing_volume <= 0:
self._average_entry_price = price
else:
total = existing_volume + 1.0
self._average_entry_price = abs((self._average_entry_price * existing_volume * -1.0 + price) / total)
self._entries_in_series += 1
self.SellMarket()
def _manage_risk(self, price):
if self._entries_in_series == 0:
self._trailing_stop_level = None
return
step = self._get_price_step()
stop_distance = self._stop_loss.Value * step
take_distance = self._take_profit.Value * step
trailing_distance = self._trailing_stop.Value * step
if self._direction > 0:
loss_level = self._average_entry_price - stop_distance
profit_level = self._average_entry_price + take_distance
if price <= loss_level:
self.SellMarket()
self._reset_series()
return
if price >= profit_level:
self.SellMarket()
self._reset_series()
return
if self._trailing_stop.Value > 0:
candidate = price - trailing_distance
if self._trailing_stop_level is None or candidate > self._trailing_stop_level:
self._trailing_stop_level = candidate
if self._trailing_stop_level is not None and price <= self._trailing_stop_level:
self.SellMarket()
self._reset_series()
elif self._direction < 0:
loss_level = self._average_entry_price + stop_distance
profit_level = self._average_entry_price - take_distance
if price >= loss_level:
self.BuyMarket()
self._reset_series()
return
if price <= profit_level:
self.BuyMarket()
self._reset_series()
return
if self._trailing_stop.Value > 0:
candidate = price + trailing_distance
if self._trailing_stop_level is None or candidate < self._trailing_stop_level:
self._trailing_stop_level = candidate
if self._trailing_stop_level is not None and price >= self._trailing_stop_level:
self.BuyMarket()
self._reset_series()
def _get_price_step(self):
step = 0.0001
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 0.0001
return step
def _reset_series(self):
self._direction = 0
self._entries_in_series = 0
self._last_entry_price = 0.0
self._average_entry_price = 0.0
self._trailing_stop_level = None
def CreateClone(self):
return dealers_trade_v751_rivot_strategy()