GitHub で見る

彼女カンスキゴールの毎日の戦略

概要

She Kanskigor Daily Strategy は、オリジナルの MetaTrader エキスパート アドバイザー SHE_kanskigor.mq4 を反映した 1 日 1 回のブレイクアウト システムです。この戦略は、前の日足のローソク足の方向を評価し、新しい取引日の開始時に狭い時間枠内で単一の市場ポジションをオープンします。ポジションを自動的に監視し、有価証券の価格ステップで表される設定可能なテイクプロフィットまたはストップロスの距離によってクローズします。

取引ロジック

  1. 選択した証券の日中ローソク足 (デフォルト: 1 分) と日足ローソク足の両方を購読します。
  2. 完成したデイリーキャンドルが到着するたびに、保存されているデイリーオープンとクローズを更新します。
  3. 完成した日中キャンドルごとに:
    • 新しい暦日が始まると、「今日取引」フラグをリセットします。
    • 終値がストップロスまたはテイクプロフィットのしきい値に達しているかどうかを確認して、アクティブなポジションを管理します。
    • 現在の時間が設定された取引ウィンドウ (デフォルトの開始: 00:05、ウィンドウの長さ: 5 分) 内であるかどうかを確認します。
    • 今日まだポジションがオープンされておらず、有効な以前の日次ローソク足が利用可能な場合:
      • 前日の始値が終値より高い場合(弱気のローソク足)、ロングになります。
      • 前回の日次始値が終値よりも低い場合(強気のローソク足)、ショートします。
    • 前日終値が変わらずの場合は取引をスキップします。
  4. この戦略は、終値が設定されたしきい値に達すると、成行注文を使用して保護的出口を実行します。

パラメーター

名前 説明 デフォルト
ボリューム エントリーに使用される注文量。 0.1
利益確定 価格段階で表現される利益目標。値 0 はターゲットを無効にします。 35
ストップロス 価格ステップで表される損失のしきい値。 0 の値は停止を無効にします。 55
開始時間 エントリーウィンドウが開始される時刻 (為替タイムゾーン)。 00:05
ウィンドウ (分) エントリウィンドウの継続時間 (分単位)。 5
日中キャンドル 日中処理に使用されるローソク足のデータ型 (デフォルト: 1 分足ローソク足)。 TimeFrameCandleMessage(1m)

注意事項

  • この戦略では、取引日あたり 1 回のエントリーのみが許可されます。
  • 毎日のローソク足データが利用可能である必要があります。それ以外の場合、戦略は完成したキャンドルが到着するまで待機します。
  • 保護出口は、終了した日中ローソク足の終値に基づいて動作します。
  • このコードは、StockSharp の高レベルの API (SubscribeCandlesBind) を使用し、プロジェクトのコーディング標準 (タブ、英語のコメント、パラメータのメタデータ) に準拠しています。
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>
/// Daily breakout strategy that opens a position during a short time window.
/// Uses the previous daily candle direction to decide whether to buy or sell.
/// Applies configurable take-profit and stop-loss levels expressed in price steps.
/// </summary>
public class SheKanskigorDailyStrategy : Strategy
{
	private readonly StrategyParam<TimeSpan> _startTime;
	private readonly StrategyParam<int> _tradeWindowMinutes;
	private readonly StrategyParam<decimal> _takeProfitSteps;
	private readonly StrategyParam<decimal> _stopLossSteps;
	private readonly StrategyParam<DataType> _intradayCandleType;

	private readonly DataType _dailyCandleType;

	private DateTime _currentDate;
	private bool _tradePlaced;
	private bool _dailyReady;
	private decimal _previousOpen;
	private decimal _previousClose;
	private decimal _entryPrice;

	/// <summary>
	/// Start time of the trading window.
	/// </summary>
	public TimeSpan StartTime
	{
		get => _startTime.Value;
		set => _startTime.Value = value;
	}

	/// <summary>
	/// Width of the trading window in minutes.
	/// </summary>
	public int TradeWindowMinutes
	{
		get => _tradeWindowMinutes.Value;
		set => _tradeWindowMinutes.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in security price steps.
	/// </summary>
	public decimal TakeProfitSteps
	{
		get => _takeProfitSteps.Value;
		set => _takeProfitSteps.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in security price steps.
	/// </summary>
	public decimal StopLossSteps
	{
		get => _stopLossSteps.Value;
		set => _stopLossSteps.Value = value;
	}

	/// <summary>
	/// Intraday candle type used to evaluate the trading window.
	/// </summary>
	public DataType IntradayCandleType
	{
		get => _intradayCandleType.Value;
		set => _intradayCandleType.Value = value;
	}

	/// <summary>
	/// Initializes <see cref="SheKanskigorDailyStrategy"/>.
	/// </summary>
	public SheKanskigorDailyStrategy()
	{
		_takeProfitSteps = Param(nameof(TakeProfitSteps), 35m)
			.SetDisplay("Take Profit", "Profit target in steps", "Risk")
			;

		_stopLossSteps = Param(nameof(StopLossSteps), 55m)
			.SetDisplay("Stop Loss", "Loss limit in steps", "Risk")
			;

		_startTime = Param(nameof(StartTime), new TimeSpan(0, 5, 0))
			.SetDisplay("Start Time", "Time of day to evaluate entries", "Schedule");

		_tradeWindowMinutes = Param(nameof(TradeWindowMinutes), 5)
			.SetDisplay("Window (min)", "Trading window duration in minutes", "Schedule")
			;

		_intradayCandleType = Param(nameof(IntradayCandleType), TimeSpan.FromMinutes(1).TimeFrame())
			.SetDisplay("Intraday Candle", "Candle type for intraday checks", "Data");

		_dailyCandleType = TimeSpan.FromMinutes(5).TimeFrame();
	}

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

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

		_currentDate = default;
		_tradePlaced = false;
		_dailyReady = false;
		_previousOpen = 0m;
		_previousClose = 0m;
		_entryPrice = 0m;
	}

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

		var intraday = SubscribeCandles(IntradayCandleType);
		intraday.Bind(ProcessIntraday).Start();

		var daily = SubscribeCandles(_dailyCandleType);
		daily.Bind(ProcessDaily).Start();

		StartProtection(null, null);
	}

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

		// Store the direction of the last completed daily candle.
		_previousOpen = candle.OpenPrice;
		_previousClose = candle.ClosePrice;
		_dailyReady = true;
	}

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

		var openTime = candle.OpenTime;

		if (openTime.Date != _currentDate)
		{
			_currentDate = openTime.Date;
			_tradePlaced = false;
		}

		ManagePosition(candle.ClosePrice);

		var start = StartTime;
		var end = start.Add(TimeSpan.FromMinutes(TradeWindowMinutes));
		var currentTod = openTime.TimeOfDay;

		if (currentTod < start || currentTod > end)
			return;

		if (_tradePlaced)
			return;

		if (!_dailyReady)
			return;

		if (Position != 0)
		{
			_tradePlaced = true;
			return;
		}

		if (_previousOpen > _previousClose)
		{
			BuyMarket(Volume);
			_tradePlaced = true;
		}
		else if (_previousOpen < _previousClose)
		{
			SellMarket(Volume);
			_tradePlaced = true;
		}
		else
		{
			// Skip trading when the previous day closed unchanged.
			_tradePlaced = true;
		}
	}

	private void ManagePosition(decimal closePrice)
	{
		if (Position == 0)
			return;

		var step = Security?.PriceStep ?? 0m;
		if (step <= 0m || _entryPrice == 0m)
			return;

		if (Position > 0)
		{
			var target = _entryPrice + TakeProfitSteps * step;
			var stop = _entryPrice - StopLossSteps * step;

			if (TakeProfitSteps > 0m && closePrice >= target)
			{
				SellMarket(Position);
				return;
			}

			if (StopLossSteps > 0m && closePrice <= stop)
			{
				SellMarket(Position);
			}
		}
		else
		{
			var target = _entryPrice - TakeProfitSteps * step;
			var stop = _entryPrice + StopLossSteps * step;

			if (TakeProfitSteps > 0m && closePrice <= target)
			{
				BuyMarket(-Position);
				return;
			}

			if (StopLossSteps > 0m && closePrice >= stop)
			{
				BuyMarket(-Position);
			}
		}
	}

	/// <inheritdoc />
	protected override void OnOwnTradeReceived(MyTrade trade)
	{
		base.OnOwnTradeReceived(trade);

		if (trade.Order.Security != Security)
			return;

		// Track the latest fill price to evaluate protective exits.
		_entryPrice = trade.Trade.Price;
	}
}