GitHub で見る

SimpleTrade フリップ戦略

概要

  • MetaTrader 4 エキスパート アドバイザー SimpleTrade.mq4 (別名「neroTrade」) の StockSharp ポート。
  • CandleType パラメータで設定された時間枠での単一シンボル取引用に設計されています。
  • 常に最大でも 1 つのオープン ポジションを維持し、新しいバーが開くたびに方向を反転します。

取引ロジック

  1. 新しいローソク足がアクティブになるたびに、この戦略はそのローソク足の始値を LookbackBars 期間古いローソク足の始値と比較します。
  2. 新しいオープンが過去の基準よりも厳密に高い場合、既存のポジションはすべてクローズされ、TradeVolume ロットの新しいロング成行注文が送信されます。
  3. それ以外の場合 (オープンが等しいかそれ以下の場合)、戦略は既存のポジションをすべてクローズし、同じサイズのショート マーケット ポジションをオープンします。
  4. StopLossPoints パラメータは、元の EA の stop 設定を反映しています。証券の PriceStepStopLossPoints の両方が利用可能な場合、ストラテジーは値を絶対距離に変換して StartProtection に転送し、StockSharp が保護的なストップロス注文を自動的に維持できるようにします。
  5. ローソク足のオープンは、高レベルのローソク足サブスクリプション API を使用して追跡されます。完了したローソク足は履歴リストに追加され、アクティブなローソク足はバーごとに 1 回決定をトリガーします。

パラメーター

パラメータ 説明 デフォルト
TradeVolume 基本注文サイズはロットで表されます。ポジティブである必要があります。 1
StopLossPoints 計器点での保護停止距離。自動ストップロスを無効にするには、0 に設定します。 120
LookbackBars 始値比較に使用されるバーの数。値 3 は、元のコードの Open[0]Open[3] を再現します。 3
CandleType ローソクがリクエストされる時間枠 (DataType として)。新しい信号がいつ現れるかを制御します。 1 hour timeframe

実装メモ

  • 高レベルの SubscribeCandles(...).Bind(...) ワークフローを使用するため、戦略は軽量のままで、履歴ローソク足とライブローソク足の両方に反応します。
  • StartProtectionOnStarted 中に 1 回呼び出されます。接続されたセキュリティが PriceStep を提供していることを確認してください。そうしないと、ストップロス距離を絶対価格に変換できません。
  • すべての取引は各バーの開始時に成行注文で入力されるため、スリッページの処理は取引会場に委任され、追加の slippage パラメーターはありません。
  • 履歴オープン バッファは、不必要なメモリ使用を避けるために、小さなローリング ウィンドウ (LookbackBars + 5 値) のみを保持します。
  • Python ポートは提供されません。 CS/ ディレクトリには唯一の実装が含まれています。

ファイル構造

「」 4002_シンプルトレード/ §── CS/ │ └── SimpleTradeFlipStrategy.cs §── README.md §── 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>
/// StockSharp port of the MetaTrader "SimpleTrade" expert advisor.
/// Compares the opening price of the current bar with the bar from several periods ago and flips the position accordingly.
/// </summary>
public class SimpleTradeFlipStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<int> _lookbackBars;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _openHistory = new();
	private int _cooldown;

	/// <summary>
	/// Initializes a new instance of the <see cref="SimpleTradeFlipStrategy"/> class.
	/// </summary>
	public SimpleTradeFlipStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order size in lots", "Trading");

		_stopLossPoints = Param(nameof(StopLossPoints), 120m)
			.SetNotNegative()
			.SetDisplay("Stop-Loss Points", "Protective stop distance expressed in instrument points", "Risk");

		_lookbackBars = Param(nameof(LookbackBars), 10)
			.SetGreaterThanZero()
			.SetDisplay("Lookback Bars", "Number of bars used for the open price comparison", "Signals");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe used for signal calculations", "General");
	}

	/// <summary>
	/// Order size submitted with each entry.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in instrument points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = Math.Max(0m, value);
	}

	/// <summary>
	/// Number of historical bars used for the open price comparison.
	/// </summary>
	public int LookbackBars
	{
		get => _lookbackBars.Value;
		set => _lookbackBars.Value = Math.Max(1, value);
	}

	/// <summary>
	/// Candle type that defines the working timeframe.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_openHistory.Clear();
		_cooldown = 0;
	}

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

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

		var step = Security?.PriceStep ?? 0m;
		Unit stopLossUnit = null;

		if (StopLossPoints > 0m && step > 0m)
			stopLossUnit = new Unit(StopLossPoints * step, UnitTypes.Absolute);

		StartProtection(null, stopLossUnit);
	}

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

		// Store the open price for future comparisons.
		_openHistory.Add(candle.OpenPrice);

		var maxHistory = Math.Max(LookbackBars + 5, 5);
		if (_openHistory.Count > maxHistory)
			_openHistory.RemoveRange(0, _openHistory.Count - maxHistory);

		var lookback = LookbackBars;
		if (_openHistory.Count <= lookback)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		var volume = TradeVolume;
		if (volume <= 0m)
			return;

		var currentOpen = candle.OpenPrice;
		var referenceOpen = _openHistory[^(lookback + 1)];

		// Only trade on clear directional difference
		var diff = currentOpen - referenceOpen;
		if (Math.Abs(diff) < currentOpen * 0.001m)
			return;

		if (diff > 0 && Position <= 0)
		{
			if (Position < 0m)
				BuyMarket(Math.Abs(Position));
			BuyMarket(volume);
			_cooldown = 5;
		}
		else if (diff < 0 && Position >= 0)
		{
			if (Position > 0m)
				SellMarket(Position);
			SellMarket(volume);
			_cooldown = 5;
		}
	}
}