GitHub で見る

S7 Up Bot戦略

ほぼ等しい高値または安値の後に急激な価格変動が続くパターンを探すブレイクアウトシステムです。 2つの連続した安値がほぼ等しく、価格がSpan Price上昇したとき、ボットはロングに入ります。 2つの高値が揃って価格がSpan Price下落したとき、ショートに入ります。 ポジションはオプションのテイクプロフィット、ストップロス、トレーリングストップ、早期退出機能で保護されます。

詳細

  • エントリー条件:
    • 買い: 現在の安値と前の安値の差がHL Divergence未満で、価格が安値よりSpan Price上にある。
    • 売り: 現在の高値と前の高値の差がHL Divergence未満で、価格が高値よりSpan Price下にある。
  • ロング/ショート: 両方。
  • エグジット条件:
    • テイクプロフィットまたはストップロス。
    • トレーリングストップまたはゼロトレーリング調整。
    • 価格が前の高値/安値を超えた場合(Exit At Extremum)、または反転レベルに近づいた場合(Exit At Reversal)の早期退出。
  • ストップ: オプションのトレーリング付き絶対テイクプロフィットとストップロス。
  • フィルター: なし。

パラメーター

  • Take Profit – 価格単位での利益目標。
  • Stop Loss – 価格単位での損失制限、0で自動の極値ベースストップ。
  • HL Divergence – 2つの連続した高値または安値の最大許容差。
  • Span Price – エントリーに必要な極値から価格までの距離。
  • Max Trades – 同時取引の最大数。
  • Use Trailing Stop – トレーリングストップメカニズムを有効化。
  • Trail Stop – トレーリングストップ距離。
  • Zero Trailing – ポジションが利益を出したら価格方向にストップを移動。
  • Step Trailing – ゼロトレーリングを調整する最小ステップ。
  • Exit At Extremum – 価格が前の高値/安値を超えたら決済。
  • Exit At Reversal – 価格が反対の極値に近づいたら決済。
  • Span To Revers – 反転退出を起動する極値からの距離。
  • Candle Type – 分析に使用する時間軸。
  • Order Volume – 取引あたりの数量。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// S7 Up Bot breakout strategy.
/// Opens long after double bottom and short after double top.
/// </summary>
public class S7UpBotStrategy : Strategy
{
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _hlDivergence;
	private readonly StrategyParam<decimal> _spanPrice;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevLow;
	private decimal _prevHigh;
	private decimal _entryPrice;
	private decimal _stopPrice;
	private decimal _takeProfitPrice;
	private bool _isLong;
	private bool _inPosition;

	public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
	public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
	public decimal HlDivergence { get => _hlDivergence.Value; set => _hlDivergence.Value = value; }
	public decimal SpanPrice { get => _spanPrice.Value; set => _spanPrice.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public S7UpBotStrategy()
	{
		_takeProfit = Param(nameof(TakeProfit), 500m)
			.SetDisplay("Take Profit", "Absolute take profit", "Risk");

		_stopLoss = Param(nameof(StopLoss), 300m)
			.SetDisplay("Stop Loss", "Absolute stop loss", "Risk");

		_hlDivergence = Param(nameof(HlDivergence), 100m)
			.SetGreaterThanZero()
			.SetDisplay("HL Divergence", "Max difference between highs or lows", "General");

		_spanPrice = Param(nameof(SpanPrice), 50m)
			.SetGreaterThanZero()
			.SetDisplay("Span Price", "Distance from extreme to price", "General");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevLow = 0m;
		_prevHigh = 0m;
		_entryPrice = 0m;
		_stopPrice = 0m;
		_takeProfitPrice = 0m;
		_isLong = false;
		_inPosition = false;
	}

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

		_prevLow = 0m;
		_prevHigh = 0m;
		_inPosition = false;

		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 (_inPosition)
			ManagePosition(candle, price);

		if (!_inPosition && _prevLow != 0m && _prevHigh != 0m)
			CheckEntry(candle, price);

		_prevLow = candle.LowPrice;
		_prevHigh = candle.HighPrice;
	}

	private void CheckEntry(ICandleMessage candle, decimal price)
	{
		// Double bottom: consecutive similar lows + bounce
		if (Math.Abs(candle.LowPrice - _prevLow) < HlDivergence &&
			price - candle.LowPrice > SpanPrice)
		{
			if (Position <= 0)
				BuyMarket();
			_inPosition = true;
			_isLong = true;
			_entryPrice = price;
			_stopPrice = price - StopLoss;
			_takeProfitPrice = price + TakeProfit;
		}
		// Double top: consecutive similar highs + drop
		else if (Math.Abs(candle.HighPrice - _prevHigh) < HlDivergence &&
			candle.HighPrice - price > SpanPrice)
		{
			if (Position >= 0)
				SellMarket();
			_inPosition = true;
			_isLong = false;
			_entryPrice = price;
			_stopPrice = price + StopLoss;
			_takeProfitPrice = price - TakeProfit;
		}
	}

	private void ManagePosition(ICandleMessage candle, decimal price)
	{
		if (_isLong)
		{
			if (candle.HighPrice >= _takeProfitPrice || candle.LowPrice <= _stopPrice)
			{
				SellMarket();
				_inPosition = false;
			}
		}
		else
		{
			if (candle.LowPrice <= _takeProfitPrice || candle.HighPrice >= _stopPrice)
			{
				BuyMarket();
				_inPosition = false;
			}
		}
	}
}