GitHub で見る

CM Fishing 戦略

概要

CM Fishing 戦略は、オリジナルのMQLスクリプトcm_fishing.mq4から移植されたグリッドトレードアプローチです。最後に執行された取引から価格が固定ポイント数だけ動いた際に成行注文を発注します。ロングまたはショートポジションのグリッドを構築し、指定した利益目標に達した時点でそれらを決済できます。

この実装はオリジナルスクリプトのグラフィカルインターフェースを除いた、コアのトレードロジックに焦点を当てています。注文はStockSharpの高レベルAPIを使用して執行されます。

パラメーター

名前 説明
Buy ロングポジションの開設を有効または無効にします。
Sell ショートポジションの開設を有効または無効にします。
StepBuy 新しいロングポジションを開く前に、最後の取引から下方向に動く必要がある価格ステップ(ポイント)。
StepSell 新しいショートポジションを開く前に、最後の取引から上方向に動く必要がある価格ステップ(ポイント)。
CloseProfitBuy すべてのロングポジションを閉じるための利益閾値。
CloseProfitSell すべてのショートポジションを閉じるための利益閾値。
CloseProfit 方向に関わらず、オープンポジションを決済する利益閾値。
BuyVolume 各ロングトレードの注文ボリューム。
SellVolume 各ショートトレードの注文ボリューム。

トレードロジック

  1. リアルタイムで取引価格を追跡する。
  2. 価格が最後の取引レベルからStepBuyだけ下落し、Buyが有効な場合、成行買い注文を送信する。
  3. 価格が最後の取引レベルからStepSellだけ上昇し、Sellが有効な場合、成行売り注文を送信する。
  4. 現在のポジションの平均エントリー価格を維持する。
  5. 未実現利益が対応するCloseProfit*パラメーターを超えた時点でポジションを決済する。

この戦略はティックデータで動作し、デモおよび教育目的に適しています。

注意事項

  • この実装はオリジナルスクリプトのユーザーインターフェースを再現しません。
  • どの時点でも、ネットポジション(ロングまたはショート)は1つのみ維持されます。
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Grid trading strategy based on fixed price steps.
/// Buys when price drops by step amount, sells when price rises by step amount.
/// Closes on profit threshold.
/// </summary>
public class CmFishingStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stepSize;
	private readonly StrategyParam<decimal> _profitTarget;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _referencePrice;
	private decimal _entryPrice;

	public decimal StepSize
	{
		get => _stepSize.Value;
		set => _stepSize.Value = value;
	}

	public decimal ProfitTarget
	{
		get => _profitTarget.Value;
		set => _profitTarget.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public CmFishingStrategy()
	{
		_stepSize = Param(nameof(StepSize), 500m)
			.SetGreaterThanZero()
			.SetDisplay("Step Size", "Price step for grid entries", "Parameters");

		_profitTarget = Param(nameof(ProfitTarget), 300m)
			.SetGreaterThanZero()
			.SetDisplay("Profit Target", "Price profit to close position", "Parameters");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_referencePrice = 0;
		_entryPrice = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_referencePrice = 0;
		_entryPrice = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

		var price = candle.ClosePrice;

		if (_referencePrice == 0)
		{
			_referencePrice = price;
			return;
		}

		// Check profit target first
		if (Position > 0 && price >= _entryPrice + ProfitTarget)
		{
			SellMarket();
			_referencePrice = price;
			_entryPrice = 0;
			return;
		}
		else if (Position < 0 && price <= _entryPrice - ProfitTarget)
		{
			BuyMarket();
			_referencePrice = price;
			_entryPrice = 0;
			return;
		}

		// Grid entries: buy on dip, sell on rise
		if (price <= _referencePrice - StepSize && Position <= 0)
		{
			BuyMarket();
			_entryPrice = price;
			_referencePrice = price;
		}
		else if (price >= _referencePrice + StepSize && Position >= 0)
		{
			SellMarket();
			_entryPrice = price;
			_referencePrice = price;
		}
	}
}