在 GitHub 上查看

CM Fishing 策略

概述

CM Fishing 策略 来源于 MQL 脚本 cm_fishing.mq4,属于网格交易思路。当价格相对于上一笔成交偏移一定点数时,策略会开仓。可以逐步建立多头或空头网格,并在达到设定利润时平仓。

本实现仅保留核心交易逻辑,不包含原脚本中的图形界面,所有委托通过 StockSharp 的高级 API 执行。

参数

名称 说明
Buy 是否允许开多仓。
Sell 是否允许开空仓。
StepBuy 价格向下移动多少点后开多仓。
StepSell 价格向上移动多少点后开空仓。
CloseProfitBuy 多头达到该利润时全部平仓。
CloseProfitSell 空头达到该利润时全部平仓。
CloseProfit 无论方向,当利润达到该值时平掉所有仓位。
BuyVolume 每次开多仓的数量。
SellVolume 每次开空仓的数量。

交易逻辑

  1. 实时跟踪每笔成交价格。
  2. 当价格自上次成交价向下移动 StepBuy 点且允许开多时,发送市价买单。
  3. 当价格自上次成交价向上移动 StepSell 点且允许开空时,发送市价卖单。
  4. 维护当前持仓的平均开仓价。
  5. 当未实现利润超过相应的 CloseProfit* 参数时平仓。

该策略基于逐笔成交数据运行,适用于演示和学习目的。

备注

  • 本实现不包含原脚本的图形界面。
  • 任何时刻仅保持净多或净空仓位。
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;
		}
	}
}