GitHub で見る

Martin戦略 - 損失なし決済 V3

このマーチンゲール平均化戦略は、最初のエントリーから設定された割合だけ価格が下落するたびにロングポジションに追加します。新しい注文ごとに現金額が乗数で増加し、平均価格が調整されます。ポジションはローソク足の高値が平均価格にテイクプロフィット率を加えた値に達したときに決済され、利益がある場合のみの出口が保証されます。

詳細

  • エントリー条件:
    • ロング: フラットInitial Cashで購入
    • 追加: Price <= EntryPrice * (1 - PriceStep% * orderCount) && orderCount < MaxOrders
  • ロング/ショート: ロングのみ
  • エグジット条件:
    • High >= AvgPrice * (1 + TakeProfit%)
  • ストップ: いいえ
  • デフォルト値:
    • Initial Cash = 100
    • Max Orders = 20
    • Price Step % = 1.5
    • Take Profit % = 1
    • Increase Factor = 1.05
  • フィルター:
    • カテゴリ: ナンピン買い
    • 方向: ロングのみ
    • インジケーター: なし
    • ストップ: いいえ
    • 複雑さ: 低
    • 時間軸: 任意
    • 季節性: いいえ
    • ニューラルネットワーク: いいえ
    • ダイバージェンス: いいえ
    • リスクレベル: 高
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;

public class MartinNoLossExitV3Strategy : Strategy
{
	private readonly StrategyParam<decimal> _priceStepPercent;
	private readonly StrategyParam<decimal> _takeProfitPercent;
	private readonly StrategyParam<decimal> _increaseFactor;
	private readonly StrategyParam<int> _maxOrders;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;

	private ExponentialMovingAverage _ema;
	private decimal _entryPrice;
	private decimal _totalCost;
	private decimal _totalQty;
	private decimal _lastCash;
	private int _orderCount;
	private bool _inPosition;

	public decimal PriceStepPercent { get => _priceStepPercent.Value; set => _priceStepPercent.Value = value; }
	public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
	public decimal IncreaseFactor { get => _increaseFactor.Value; set => _increaseFactor.Value = value; }
	public int MaxOrders { get => _maxOrders.Value; set => _maxOrders.Value = value; }
	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public MartinNoLossExitV3Strategy()
	{
		_priceStepPercent = Param(nameof(PriceStepPercent), 2.5m);
		_takeProfitPercent = Param(nameof(TakeProfitPercent), 2.5m);
		_increaseFactor = Param(nameof(IncreaseFactor), 1.10m);
		_maxOrders = Param(nameof(MaxOrders), 8);
		_emaLength = Param(nameof(EmaLength), 50);
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame());
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_ema = null;
		_entryPrice = 0m;
		_totalCost = 0m;
		_totalQty = 0m;
		_lastCash = 0m;
		_orderCount = 0;
		_inPosition = false;
	}

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

		_entryPrice = 0m;
		_totalCost = 0m;
		_totalQty = 0m;
		_lastCash = 0m;
		_orderCount = 0;
		_inPosition = false;
		_ema = new ExponentialMovingAverage { Length = EmaLength };

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

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

		if (!_ema.IsFormed)
			return;

		var initialCash = 100m;

		if (_inPosition)
		{
			var avgPrice = _totalQty > 0m ? _totalCost / _totalQty : 0m;
			var takeProfitPrice = avgPrice * (1 + TakeProfitPercent / 100m);

			if (candle.HighPrice >= takeProfitPrice && Position > 0)
			{
				SellMarket();
				_inPosition = false;
				_entryPrice = 0m;
				_totalCost = 0m;
				_totalQty = 0m;
				_lastCash = 0m;
				_orderCount = 0;
				return;
			}

			var nextEntryPrice = _entryPrice * (1 - PriceStepPercent / 100m * _orderCount);
			if (_orderCount < MaxOrders && candle.ClosePrice <= nextEntryPrice)
			{
				BuyMarket();
				var newCash = _lastCash * IncreaseFactor;
				_totalCost += newCash;
				_totalQty += newCash / candle.ClosePrice;
				_lastCash = newCash;
				_orderCount++;
			}
		}
		else
		{
			if (candle.ClosePrice > ema)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				_totalCost = initialCash;
				_totalQty = initialCash / candle.ClosePrice;
				_lastCash = initialCash;
				_orderCount = 1;
				_inPosition = true;
			}
		}
	}
}