GitHub で見る

コミッション計算戦略

概要

コミッション計算戦略は、元の MetaTrader スクリプトを反映したユーティリティ戦略です。選択した約定モード (成行、指値、またはストップ) を使用して単一の裁量注文を送信し、結果として生じるすべての約定に適用されるブローカー手数料を測定します。この戦略は、累積コミッションを保存し、終了時に開始残高、合計手数料、および手数料調整後の残高を含む最終レポートを印刷します。

従来のシグナル主導の戦略とは異なり、市場データや指標は必要ありません。この戦略は、手動または半手動の執行に対する自動化された手数料会計に焦点を当てています。

取引ロジック

  1. 戦略が開始されると、初期ポートフォリオ残高が取得され、デフォルトの取引量が設定されます。
  2. オプションの保護ストップロスおよびテイクプロフィットレベルは、エントリー価格とターゲット価格の両方が有効な場合、StartProtection を通じて有効になります。距離は絶対価格単位で計算され、MQL の実装を模倣します。
  3. 設定された順序モードは 1 回だけ実行されます。パラメーターに一貫性がない場合 (たとえば、指値注文のエントリー価格が欠落している場合)、ストラテジーは問題をログに記録し、注文の送信をスキップします。
  4. OnNewMyTrade を通じて受け取ったすべての取引は、設定されたパーセンテージ レートを使用して手数料を計算するために処理されます。
  5. この戦略は、すべての手数料を集計し、最新の手数料を記憶し、停止時に詳細な概要を記録します。

この実装では、ブローカー手数料が price × volume × commissionRate / 100 に比例すると想定しています。モデル化されている会場に合わせてレートを調整します。

パラメーター

名前 デフォルト 説明
Quantity 0.001 ヘルパー メソッド (BuyMarketSellLimit など) によって送信された取引量。
EntryPrice 31365 指値注文または逆指値注文、および保護距離の計算に使用される価格。
StopLossPrice 31200 ストップロス距離を定義する価格。距離が正でない場合、ストップロス保護は無効になります。
TakeProfitPrice 32100 利益確定距離を定義する価格。距離が正でない場合は、テイクプロフィット保護が無効になります。
CommissionRate 0.04 取引される想定元本に対する割合として表される手数料率。
Mode None ストラテジーの開始時に実行する注文タイプ。オプション: NoneMarketBuyMarketSellBuyLimitSellLimitBuyStopSellStop

注意事項とベストプラクティス

  • 手動注文をサポートするポートフォリオで戦略を開始します。データの購読は必要ありません。
  • 料金の過小評価または過大評価を避けるために、ブローカー手数料モデルが CommissionRate パラメータと一致していることを確認してください。
  • 未決注文の場合は、戦略を開始する前に EntryPrice を有効なレベルに設定します。それ以外の場合、注文は送信されません。
  • 保護レベルが有効になっている場合、戦略は、元の MQL の動作を厳密に模倣するために、トリガー時に市場終了を使用するようにコネクタに指示します。

結果報告

OnStopped が呼び出されると、戦略は次のようにログを記録します。

  • 初期バランスのスナップショット (戦略の開始時に取得)。
  • 処理されたすべての取引の合計仲介手数料。
  • 最終的な残高は、累積料金を差し引いて調整されます。

これにより、この戦略は、迅速な what-if 分析やバックテスト中のブローカー手数料スケジュールの検証に適しています。

namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Commission Calculator strategy: CCI level crossover.
/// Buys when CCI crosses above -100 (oversold exit), sells when CCI crosses below 100 (overbought exit).
/// </summary>
public class CommissionCalculatorStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevCci;
	private int _candlesSinceTrade;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int Period { get => _period.Value; set => _period.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public CommissionCalculatorStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_period = Param(nameof(Period), 30)
			.SetGreaterThanZero()
			.SetDisplay("Period", "CCI period", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCci = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCci = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
		var cci = new CommodityChannelIndex { Length = Period };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal cciValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (_hasPrev)
		{
			if (_prevCci < -100 && cciValue >= -100 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (_prevCci > 100 && cciValue <= 100 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevCci = cciValue;
		_hasPrev = true;
	}
}