GitHub で見る
バックテスト貿易アシスタントパネル戦略
概要
バックテスト トレード アシスタント パネル戦略は、MetaTrader 4 エキスパート アドバイザー バックテスト トレード アシスタント パネル V1.10 から変換された手動ヘルパーです。オリジナルのスクリプトはテスター内にグラフィカルなコントロール パネルを作成し、オペレーターがロット サイズ、ストップロス、テイクプロフィットの距離を変更し、即時に買いまたは売りの成行注文を送信できるようにしました。 StockSharp ポートは、チャート上のウィジェットではなく、厳密に型指定されたパラメーターとパブリック ヘルパー メソッドを公開することにより、戦略コンポーネント内で同じワークフローを提供します。
主な機能:
- MetaTrader スタイルのストップロスとテイクプロフィットの距離 (「ポイント」で測定) とともに、構成可能な注文量を維持します。
ManualBuy() および ManualSell() ヘルパーを通じて、オンデマンドで買いまたは売りの成行注文を発行します。
- 変換されたポイント値を使用して、各手動入力後にストップロスとテイクプロフィットのオフセットを自動的に付加します。
- MT4 パネルの編集可能なテキスト フィールドを模倣して、実行時に取引量とリスク ディスタンスを更新するユーティリティ メソッドを提供します。
パラメーター
| 名前 |
説明 |
デフォルト |
OrderVolume |
手動成行注文に適用されるロット単位のボリューム。値を変更すると、ベース Strategy.Volume も更新されます。 |
0.1 |
StopLossPips |
約定価格から保護ストップまでの距離。MetaTrader ポイントで表されます。自動ストップロスの配置を無効にするには、0 に設定します。 |
50 |
TakeProfitPips |
約定価格から利益目標までの距離。MetaTrader ポイントで表されます。自動テイクプロフィット配置を無効にするには、0 に設定します。 |
100 |
MagicNumber |
簿記のために元の EA から保存された識別子。これは、StockSharp 実行ロジックによって直接使用されませんが、カスタム拡張機能で参照できます。 |
99 |
手動操作
オリジナルの EA はクリック可能なボタンに依存していました。 StockSharp では、同じアクションがパブリック メソッドとして使用できます。
SetOrderVolume(decimal volume) – OrderVolume パラメータと内部の Strategy.Volume 値を同期します。
SetStopLoss(decimal pips) / SetTakeProfit(decimal pips) – 戦略の実行中に保護距離を調整します。値は、MT4 テキスト ボックスとまったく同様に、MetaTrader ポイントで解釈されます。
ManualBuy() – 現在の出来高を使用して成行買い注文を送信します。実行後、ストラテジーは設定されたストップロスポイントとテイクプロフィットポイントを(シンボルメタデータを使用して)価格オフセットに変換し、SetStopLoss/SetTakeProfit を呼び出して、結果として生じるネットポジションの保護注文を登録します。
ManualSell() – 成行売り注文の対称ヘルパー。
CloseAllPositions() – エクスポージャー全体を市場価格でクローズします。これは、テスターが手動でポジションを平坦化できるワークフローを反映しています。
すべての保護距離は、MT4 と同じ pip サイズ ヒューリスティックで変換されます。5 桁および 3 桁のシンボルは PriceStep を 10 倍して単一の「ポイント」を取得しますが、他のシンボルは生の PriceStep に依存します。市場データが必要なメタデータを提供しない場合、一貫した動作を維持するためにフォールバック サイズ 0.0001 が使用されます。
行動メモ
- この戦略は、レベル 1 の更新をサブスクライブして、最良の買値/売値を追跡します。これらの価格が利用できない場合は、保護オフセットを適用する前の最後の取引価格に戻ります。
- 自動取引シグナルは生成されません。このモジュールは、MT4 パネルと同様に手動実行アシスタントとして厳密に機能します。
- StockSharp は保護命令をネイティブに管理するため、明示的なマジックナンバーは必要ありません。このフィールドは、ソース Expert Advisor と同等の目的でのみ含まれています。
- ストップロスとテイクプロフィットの距離は、ボタンを押す前に
ManualBuy()/ManualSell() をトリガーして MT4 テキストフィールドの編集をエミュレートする前にいつでも調整できます。
オリジナルの EA との違い
- MetaTrader ユーザー インターフェイスは、ストラテジ パラメーターとメソッド呼び出しに置き換えられます。すべての機能は、チャート コントロールをレンダリングせずにプログラムで使用できます。
- StockSharp の
BuyMarket/SellMarket ヘルパーは直接のスリッページ引数を公開しないため、MT4 OrderSend 呼び出し (50 ポイントで固定) からのスリッページ処理は再現されません。必要に応じて、周囲の環境で実行耐性を管理する必要があります。
- 秘密保持命令は、
OrderSend の直接呼び出しではなく、StockSharp の高レベルの SetStopLoss/SetTakeProfit ヘルパーを使用して作成され、実装の一貫性が StockSharp の規則に保たれます。
使い方のヒント
- 通常どおり、StockSharp で目的のシンボル、ポートフォリオ、コネクタを設定し、戦略を開始します。
- パラメータ グリッドまたは提供されたセッター メソッドを使用して、
OrderVolume、StopLossPips、および TakeProfitPips を調整します。
- 任意のエントリが必要な場合は、常に
ManualBuy() または ManualSell() を呼び出します。ヘルパーは、要求された保護命令を自動的に添付します。
CloseAllPositions() を使用すると、バックテストまたはライブ裁量取引セッション中にエクスポージャーを即座に平坦化できます。
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>
/// Trade assistant strategy with configurable stop-loss and take-profit.
/// Simplified from the backtesting trade assistant panel.
/// </summary>
public class BacktestingTradeAssistantPanelStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private decimal _pipSize;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
/// <summary>
/// Stop loss distance in pips.
/// </summary>
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take profit distance in pips.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Candle type for signals.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public BacktestingTradeAssistantPanelStrategy()
{
_stopLossPips = Param(nameof(StopLossPips), 50m)
.SetNotNegative()
.SetDisplay("Stop Loss (pips)", "Stop-loss distance in pips", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 100m)
.SetNotNegative()
.SetDisplay("Take Profit (pips)", "Take-profit distance in pips", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle series for trading signals", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_pipSize = 0m;
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pipSize = CalculatePipSize();
_sma = new SimpleMovingAverage { Length = 20 };
SubscribeCandles(CandleType)
.Bind(_sma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormed)
return;
var price = candle.ClosePrice;
// Check stop-loss and take-profit
if (Position != 0 && _entryPrice > 0m)
{
if (Position > 0)
{
if (_stopPrice.HasValue && price <= _stopPrice.Value)
{
SellMarket(Math.Abs(Position));
ResetPosition();
return;
}
if (_takePrice.HasValue && price >= _takePrice.Value)
{
SellMarket(Math.Abs(Position));
ResetPosition();
return;
}
}
else if (Position < 0)
{
if (_stopPrice.HasValue && price >= _stopPrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetPosition();
return;
}
if (_takePrice.HasValue && price <= _takePrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetPosition();
return;
}
}
}
// Entry: SMA crossover
if (Position == 0)
{
var pip = _pipSize > 0m ? _pipSize : 1m;
if (price > smaValue)
{
BuyMarket();
_entryPrice = price;
_stopPrice = StopLossPips > 0m ? price - StopLossPips * pip : null;
_takePrice = TakeProfitPips > 0m ? price + TakeProfitPips * pip : null;
}
else if (price < smaValue)
{
SellMarket();
_entryPrice = price;
_stopPrice = StopLossPips > 0m ? price + StopLossPips * pip : null;
_takePrice = TakeProfitPips > 0m ? price - TakeProfitPips * pip : null;
}
}
}
private void ResetPosition()
{
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
private decimal CalculatePipSize()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
return 0.0001m;
var decimals = Security?.Decimals ?? 0;
return decimals is 5 or 3 ? step * 10m : step;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import Math, TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class backtesting_trade_assistant_panel_strategy(Strategy):
def __init__(self):
super(backtesting_trade_assistant_panel_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._stop_loss_pips = self.Param("StopLossPips", 50.0)
self._take_profit_pips = self.Param("TakeProfitPips", 100.0)
self._sma = None
self._pip_size = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@StopLossPips.setter
def StopLossPips(self, value):
self._stop_loss_pips.Value = value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@TakeProfitPips.setter
def TakeProfitPips(self, value):
self._take_profit_pips.Value = value
def OnReseted(self):
super(backtesting_trade_assistant_panel_strategy, self).OnReseted()
self._sma = None
self._pip_size = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def _calculate_pip_size(self):
sec = self.Security
if sec is None:
return 0.0001
step = sec.PriceStep
if step is None or float(step) <= 0:
return 0.0001
step_val = float(step)
decimals = sec.Decimals
if decimals is not None and (int(decimals) == 5 or int(decimals) == 3):
return step_val * 10.0
return step_val
def OnStarted2(self, time):
super(backtesting_trade_assistant_panel_strategy, self).OnStarted2(time)
self._pip_size = self._calculate_pip_size()
self._sma = SimpleMovingAverage()
self._sma.Length = 20
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._sma, self._process_candle).Start()
def _reset_position(self):
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormed:
return
price = float(candle.ClosePrice)
sma_val = float(sma_value)
# Check stop-loss and take-profit
if self.Position != 0 and self._entry_price > 0:
if self.Position > 0:
if self._stop_price is not None and price <= self._stop_price:
self.SellMarket(abs(float(self.Position)))
self._reset_position()
return
if self._take_price is not None and price >= self._take_price:
self.SellMarket(abs(float(self.Position)))
self._reset_position()
return
elif self.Position < 0:
if self._stop_price is not None and price >= self._stop_price:
self.BuyMarket(abs(float(self.Position)))
self._reset_position()
return
if self._take_price is not None and price <= self._take_price:
self.BuyMarket(abs(float(self.Position)))
self._reset_position()
return
# Entry: SMA crossover
if self.Position == 0:
pip = self._pip_size if self._pip_size > 0 else 1.0
sl_pips = float(self.StopLossPips)
tp_pips = float(self.TakeProfitPips)
if price > sma_val:
self.BuyMarket()
self._entry_price = price
self._stop_price = price - sl_pips * pip if sl_pips > 0 else None
self._take_price = price + tp_pips * pip if tp_pips > 0 else None
elif price < sma_val:
self.SellMarket()
self._entry_price = price
self._stop_price = price + sl_pips * pip if sl_pips > 0 else None
self._take_price = price - tp_pips * pip if tp_pips > 0 else None
def CreateClone(self):
return backtesting_trade_assistant_panel_strategy()