Ilan 1.6ダイナミックグリッド戦略
Ilan 1.6 Dynamic戦略は、クラシックなグリッドとマーチンゲールのエキスパートアドバイザーです。選択した方向で最初のトレードを開き、価格が固定ステップだけポジションに逆行するたびに追加注文を置きます。新規注文のボリュームはロットエクスポネントによって幾何学的に増加します。価格が平均エントリー価格にテイクプロフィット距離を加えた値に戻ったとき、バスケット内のすべてのポジションが閉じられます。価格が有利な方向に十分移動した場合、トレーリングストップが任意で利益を保護できます。
このアルゴリズムは価格動向のみに依存し、インジケーターを使用しません。各逆行移動後にポジションサイズが増加するため、システムは高リスクを持ちますが、急速な反転を捉えることができます。
詳細
- エントリー
- 最初の注文は設定された方向で開かれます。
- 現在のポジションに逆らって
PipStepポイントごとに追加注文が加えられ、MaxTradesまで。 - 各新規注文のボリューム =
InitialVolume * LotExponent^N。
- エグジット
- 価格が
AveragePrice ± TakeProfitに触れたときにすべてを閉じる。 - オプションのトレーリングストップが
TrailStartポイントの利益後に開始し、TrailStop距離で価格を追跡。
- 価格が
- ポジション管理
- 同時にロングのみまたはショートのみのシリーズ。
- バスケットを閉じた後、戦略は初期方向から再開します。
- パラメータ
InitialVolume– 最初の注文のボリューム(デフォルト 1)。LotExponent– 後続の注文サイズの乗数(デフォルト 1.6)。PipStep– グリッドレベル間のポイント距離(デフォルト 30)。TakeProfit– 平均価格からのポイント単位の利益目標(デフォルト 10)。MaxTrades– アクティブな注文の最大数(デフォルト 10)。StartLong– trueの場合、最初のトレードをロングで開く(デフォルト true)。UseTrailingStop– トレーリングストップを有効化(デフォルト false)。TrailStart– トレーリングを開始するポイント単位の利益(デフォルト 10)。TrailStop– ポイント単位のトレーリング距離(デフォルト 10)。CandleType– ローソク足の時間軸(デフォルト 1分)。
- フィルター
- カテゴリ: グリッド
- 方向: 両方
- インジケーター: なし
- ストップ: オプション
- 複雑さ: 中程度
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 高
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>
/// Grid averaging strategy based on the Ilan 1.6 Dynamic expert advisor.
/// Adds positions when price moves against the current one and closes the
/// whole basket on a take profit.
/// Each grid level trades 1 unit; closing flattens via multiple market orders.
/// </summary>
public class Ilan16DynamicStrategy : Strategy
{
private readonly StrategyParam<decimal> _pipStep;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _maxTrades;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _startLong;
private int _tradeCount;
private decimal _lastEntryPrice;
private decimal _avgPrice;
private bool _isLong;
/// <summary>
/// Distance in price steps between grid levels.
/// </summary>
public decimal PipStep { get => _pipStep.Value; set => _pipStep.Value = value; }
/// <summary>
/// Profit target from average price in price steps.
/// </summary>
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
/// <summary>
/// Maximum number of averaging entries.
/// </summary>
public int MaxTrades { get => _maxTrades.Value; set => _maxTrades.Value = value; }
/// <summary>
/// Type of candles to process.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Open first trade as long if true.
/// </summary>
public bool StartLong { get => _startLong.Value; set => _startLong.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public Ilan16DynamicStrategy()
{
_pipStep = Param(nameof(PipStep), 50000m)
.SetGreaterThanZero()
.SetDisplay("Pip Step", "Distance in price steps between grid levels", "Trading")
.SetOptimize(10000m, 100000m, 10000m);
_takeProfit = Param(nameof(TakeProfit), 30000m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Profit target from average price in price steps", "Trading")
.SetOptimize(10000m, 100000m, 10000m);
_maxTrades = Param(nameof(MaxTrades), 3)
.SetGreaterThanZero()
.SetDisplay("Max Trades", "Maximum number of averaging entries", "Trading")
.SetOptimize(2, 10, 1);
_startLong = Param(nameof(StartLong), true)
.SetDisplay("Start Long", "Open first trade as long", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_isLong = StartLong;
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 step = Security.PriceStep ?? 1m;
var price = candle.ClosePrice;
// No position - open initial entry
if (Position == 0)
{
if (_isLong)
BuyMarket();
else
SellMarket();
_tradeCount = 1;
_lastEntryPrice = price;
_avgPrice = price;
return;
}
// Check take profit: close entire basket
if (_isLong && price >= _avgPrice + TakeProfit * step)
{
CloseAll();
return;
}
else if (!_isLong && price <= _avgPrice - TakeProfit * step)
{
CloseAll();
return;
}
// Check for grid averaging entry (price moved against us)
if (_isLong && _tradeCount < MaxTrades && _lastEntryPrice - price >= PipStep * step)
{
BuyMarket();
_tradeCount++;
_avgPrice = (_avgPrice * (_tradeCount - 1) + price) / _tradeCount;
_lastEntryPrice = price;
}
else if (!_isLong && _tradeCount < MaxTrades && price - _lastEntryPrice >= PipStep * step)
{
SellMarket();
_tradeCount++;
_avgPrice = (_avgPrice * (_tradeCount - 1) + price) / _tradeCount;
_lastEntryPrice = price;
}
}
private void CloseAll()
{
var pos = Position;
if (pos > 0)
{
// Close long: sell abs(pos) times
for (var i = 0; i < (int)Math.Abs(pos); i++)
SellMarket();
}
else if (pos < 0)
{
// Close short: buy abs(pos) times
for (var i = 0; i < (int)Math.Abs(pos); i++)
BuyMarket();
}
ResetState();
}
private void ResetState()
{
_tradeCount = 0;
_lastEntryPrice = 0m;
_avgPrice = 0m;
_isLong = StartLong;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class ilan_16_dynamic_strategy(Strategy):
def __init__(self):
super(ilan_16_dynamic_strategy, self).__init__()
self._pip_step = self.Param("PipStep", 50000.0) \
.SetGreaterThanZero() \
.SetDisplay("Pip Step", "Distance in price steps between grid levels", "Trading") \
.SetOptimize(10000.0, 100000.0, 10000.0)
self._take_profit = self.Param("TakeProfit", 30000.0) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit", "Profit target from average price in price steps", "Trading") \
.SetOptimize(10000.0, 100000.0, 10000.0)
self._max_trades = self.Param("MaxTrades", 3) \
.SetGreaterThanZero() \
.SetDisplay("Max Trades", "Maximum number of averaging entries", "Trading") \
.SetOptimize(2, 10, 1)
self._start_long = self.Param("StartLong", True) \
.SetDisplay("Start Long", "Open first trade as long", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._trade_count = 0
self._last_entry_price = 0.0
self._avg_price = 0.0
self._is_long = True
@property
def pip_step(self):
return self._pip_step.Value
@property
def take_profit(self):
return self._take_profit.Value
@property
def max_trades(self):
return self._max_trades.Value
@property
def start_long(self):
return self._start_long.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ilan_16_dynamic_strategy, self).OnReseted()
self._reset_state()
def _reset_state(self):
self._trade_count = 0
self._last_entry_price = 0.0
self._avg_price = 0.0
self._is_long = self.start_long
def OnStarted2(self, time):
super(ilan_16_dynamic_strategy, self).OnStarted2(time)
self._is_long = self.start_long
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
step = self.Security.PriceStep if self.Security.PriceStep is not None else 1.0
step = float(step)
price = float(candle.ClosePrice)
# No position - open initial entry
if self.Position == 0:
if self._is_long:
self.BuyMarket()
else:
self.SellMarket()
self._trade_count = 1
self._last_entry_price = price
self._avg_price = price
return
# Check take profit: close entire basket
if self._is_long and price >= self._avg_price + float(self.take_profit) * step:
self._close_all()
return
elif not self._is_long and price <= self._avg_price - float(self.take_profit) * step:
self._close_all()
return
# Check for grid averaging entry (price moved against us)
if self._is_long and self._trade_count < self.max_trades and self._last_entry_price - price >= float(self.pip_step) * step:
self.BuyMarket()
self._trade_count += 1
self._avg_price = (self._avg_price * (self._trade_count - 1) + price) / self._trade_count
self._last_entry_price = price
elif not self._is_long and self._trade_count < self.max_trades and price - self._last_entry_price >= float(self.pip_step) * step:
self.SellMarket()
self._trade_count += 1
self._avg_price = (self._avg_price * (self._trade_count - 1) + price) / self._trade_count
self._last_entry_price = price
def _close_all(self):
pos = self.Position
if pos > 0:
for i in range(int(abs(pos))):
self.SellMarket()
elif pos < 0:
for i in range(int(abs(pos))):
self.BuyMarket()
self._reset_state()
def CreateClone(self):
return ilan_16_dynamic_strategy()