GitHub で見る

ベリーブロンディシステム

概要

Very Blondie System は、もともと MetaTrader 4 エキスパート アドバイザー「VBS - Very Blondie System」として配布された短期平均回帰グリッド戦略です。このポートは、最近の取引レンジからのブレイクアウトをフェードアウトするという当初のアイデアを維持しています。価格が過去の PeriodX ローソク足で見られた最高高値または最低安値から十分に離れたところに移動すると、戦略は成行注文ですぐにエントリーし、価格が拡大し続けた場合にその動きにスケールするために 4 つのマーチンゲール スタイルの指値注文を追加します。

データと指標

  • プライマリデータ: CandleType パラメータで構成された単一のローソク足シリーズ (MQL バージョンはチャートの時間枠で取引されます)。
  • インジケーター: Highest および Lowest インジケーター (長さ = PeriodLength) は、ブレイクアウト検出に使用されるローリング レンジの極値を追跡します。
  • レベル 1 相場: 最高買値/売値は、元の MT4 オフセットで成行注文と指値注文を行うために使用されます。

エントリーロジック

  1. 終了した各ローソク足で、最後の PeriodLength バーの最高値と最低値を計算します。
  2. 現在の最高の買値/売値を読み取ります (相場が見つからない場合は、ローソク足の終値にフォールバックします)。
  3. 長いセットアップ: highest - bid > LimitPoints * PointValue の場合、基本ボリュームで買い成行注文を送信し、アスクの下に 4 つの買い指値注文を出します。各指値注文は GridPoints * PointValue より遠くに位置し、前の注文の量を 2 倍にします (1x、2x、4x、8x、16x)。
  4. 簡単なセットアップ: bid - lowest > LimitPoints * PointValue の場合、買いロジックと同じ距離と数量乗数で売り成行注文と入札を上回る 4 つの売り指値注文を送信します。
  5. 一度にアクティブにできるバスケットは 1 つだけです。新しいシグナルは、前のサイクルからのすべてのポジションと未決注文がなくなるまで無視されます。

ポジション管理

  • 変動利益目標: 元の Amount パラメータは、すべての取引にわたって OrderProfit + OrderSwap を監視しました。ポートはこれを集約位置 (close - entryPrice) * position * conversionFactor >= ProfitTarget で再現します。しきい値に達すると、すべてのポジションが成行注文でクローズされ、残りのグリッド注文はすべてキャンセルされます。
  • ロックダウン損益分岐点: LockDownPoints > 0 のとき、取引で LockDownPoints ポイントの利益が得られると、MT4 コードは約定した各注文のストップロスを entry price ± Point に移動しました。 StockSharp バージョンはネット ポジションを追跡します。価格が LockDownPoints * PointValue 進むとすぐに、損益分岐点レベルは entryPrice ± PointValue になります。後のローソク足がそのレベル(ロングの場合は安値、ショートの場合は高値)に達すると、バスケット全体がフラットになり、すべての未決注文がキャンセルされます。
  • 手動終了: 戦略を停止するか、利益/損益分岐点条件に到達すると、MT4 の CloseAll() ルーチンを模倣するために 4 つの保留中の指値注文が常にキャンセルされます。

資金管理

  • ベースボリューム: MT4 式 MathRound(AccountBalance()/100) / 1000 と一致します。この戦略は、現在のポートフォリオ値 (または取引が行われていないときの開始値) を読み取り、それをゼロから四捨五入してロットに変換します。結果は Security.VolumeStep に調整され、MinVolume/MaxVolume に従い、ポートフォリオ スナップショットが利用できない場合は戦略 Volume (または 1) に戻ります。
  • Martingale グリッド: 追加の指値注文ごとに、基本ボリュームが最大 4 レベル (1x、2x、4x、8x、16x) まで 2 倍になります。会場が拒否する端数のロットの送信を避けるために、ボリュームは同じヘルパーで正規化されます。
  • PointValue パラメータ: MT4 の PointSecurity.PriceStep と異なる場合があります (特に 5 桁の FX 相場の場合)。 PointValue はデフォルトで PriceStep/Step からの自動検出になりますが、元の EA の動作と正確に一致するようにオーバーライドできます。

パラメーター

名前 説明 デフォルト
PeriodLength 最高値と最低値のルックバック ウィンドウ 60
LimitPoints 現在の価格とバスケットをトリガーする極端な範囲の間の最小距離 (MT4 ポイント) 1000
GridPoints 連続するグリッドオーダー間の間隔 (MT4 ポイント単位) 1500
ProfitTarget 口座通貨で表される変動利益目標 40
LockDownPoints 損益分岐点を達成するための利益距離 (MT4 ポイント単位) 0
PointValue 1 つの MT4 ポイントによって生成される価格変動 (0 = 自動検出) 0
CandleType 戦略を推進するために使用されたキャンドル シリーズ TimeFrameCandle, 1 minute

移植メモ

  • 変動損益は、各注文の OrderProfit + OrderSwap を合計するのではなく、集計位置で近似されます。これは、すべての取引が同じ方向にある場合の元の動作と一致します。これが、EA の動作方法です。
  • ストップロス修正は、武装損益分岐点価格での即時市場撤退によってエミュレートされます。 StockSharp は、OrderModify リクエストを送信する代わりに、ロジック層を戦略レイヤーに保持します。
  • 未決の指値注文は、Security.ShrinkPrice を使用して正規化された価格で登録されます。セキュリティ メタデータに PriceStep がない場合は、グリッドの不整列を避けるために、手動で PointValue を設定します。
  • この戦略は 1 つの手段を想定しており、変換ガイドラインで要求されている高レベルの API ヘルパー (SubscribeCandlesSubscribeLevel1BuyLimitSellLimit など) を使用します。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Mean reversion strategy that trades when price deviates from a range channel.
/// Buys at lower channel, sells at upper channel, exits at channel midpoint.
/// </summary>
public class VeryBlondieSystemStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _periodLength;

	public VeryBlondieSystemStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");

		_periodLength = Param(nameof(PeriodLength), 30)
			.SetDisplay("Period Length", "Period for Highest/Lowest channel.", "Indicators");
	}

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

	public int PeriodLength
	{
		get => _periodLength.Value;
		set => _periodLength.Value = value;
	}

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

		var highest = new Highest { Length = PeriodLength };
		var lowest = new Lowest { Length = PeriodLength };
		var sma = new SimpleMovingAverage { Length = PeriodLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, sma, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal highestValue, decimal lowestValue, decimal smaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var close = candle.ClosePrice;
		var range = highestValue - lowestValue;
		if (range <= 0)
			return;

		// Percentage position within channel
		var position = (close - lowestValue) / range;

		// Exit conditions: revert to mean
		if (Position > 0 && close >= smaValue)
		{
			SellMarket();
		}
		else if (Position < 0 && close <= smaValue)
		{
			BuyMarket();
		}

		// Entry: at channel extremes
		if (Position == 0)
		{
			if (position < 0.15m)
			{
				// Near lower channel - buy for mean reversion
				BuyMarket();
			}
			else if (position > 0.85m)
			{
				// Near upper channel - sell for mean reversion
				SellMarket();
			}
		}
	}
}