Ilan14 Strategy
Ilan14 is a hedging grid strategy that opens long and short positions simultaneously. When the market moves against one side by a user defined pip distance, the strategy adds a new order in that direction with its volume multiplied by the Lot Exponent. The average price of the position is tracked and once price reverts by the configured Take Profit distance, all orders on that side are closed.
Parameters:
- Pip Step – distance in pips between grid orders.
- Lot Exponent – multiplier applied to the volume of each additional order.
- Max Trades – maximum number of orders per direction.
- Take Profit – profit target in pips from the weighted average price.
- Initial Volume – volume of the first order.
- Candle Type – timeframe for candle subscription.
The implementation uses the high level StockSharp API with candle subscriptions and avoids manual data collections. Both sides of the grid are managed independently, allowing the strategy to capture rebounds after adverse moves.
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;
public class Ilan14Strategy : Strategy {
private readonly StrategyParam<decimal> _pipStep;
private readonly StrategyParam<decimal> _lotExponent;
private readonly StrategyParam<int> _maxTrades;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _initialVolume;
private readonly StrategyParam<DataType> _candleType;
private decimal _lastBuyPrice;
private decimal _lastSellPrice;
private decimal _lastBuyVolume;
private decimal _lastSellVolume;
private decimal _buyVolume;
private decimal _sellVolume;
private decimal _avgBuyPrice;
private decimal _avgSellPrice;
private int _buyCount;
private int _sellCount;
public decimal PipStep {
get => _pipStep.Value;
set => _pipStep.Value = value;
}
public decimal LotExponent {
get => _lotExponent.Value;
set => _lotExponent.Value = value;
}
public int MaxTrades {
get => _maxTrades.Value;
set => _maxTrades.Value = value;
}
public decimal TakeProfit {
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
public decimal InitialVolume {
get => _initialVolume.Value;
set => _initialVolume.Value = value;
}
public DataType CandleType {
get => _candleType.Value;
set => _candleType.Value = value;
}
public Ilan14Strategy() {
_pipStep =
Param(nameof(PipStep), 30m)
.SetGreaterThanZero()
.SetDisplay("Pip Step", "Distance in pips to add position",
"General")
;
_lotExponent =
Param(nameof(LotExponent), 1.667m)
.SetGreaterThanZero()
.SetDisplay("Lot Exponent",
"Volume multiplier for each additional order",
"General")
;
_maxTrades = Param(nameof(MaxTrades), 1)
.SetGreaterThanZero()
.SetDisplay("Max Trades",
"Maximum number of trades per direction",
"General");
_takeProfit =
Param(nameof(TakeProfit), 300m)
.SetGreaterThanZero()
.SetDisplay("Take Profit",
"Target profit in pips from average price",
"General")
;
_initialVolume = Param(nameof(InitialVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Initial Volume",
"Volume of first order", "General")
;
_candleType =
Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
public override IEnumerable<(Security sec, DataType dt)>
GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() {
base.OnReseted();
_lastBuyPrice = 0m;
_lastSellPrice = 0m;
_lastBuyVolume = 0m;
_lastSellVolume = 0m;
_buyVolume = 0m;
_sellVolume = 0m;
_avgBuyPrice = 0m;
_avgSellPrice = 0m;
_buyCount = 0;
_sellCount = 0;
}
protected override void OnStarted2(DateTime time) {
base.OnStarted2(time);
StartProtection(null, null);
var sub = SubscribeCandles(CandleType);
sub.Bind(Process).Start();
}
private void Process(ICandleMessage candle) {
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var step = Security.PriceStep ?? 1m;
var price = candle.ClosePrice;
if (_buyCount == 0 && _sellCount == 0) {
// Open initial hedge positions
BuyMarket(InitialVolume);
SellMarket(InitialVolume);
_lastBuyPrice = price;
_lastSellPrice = price;
_lastBuyVolume = InitialVolume;
_lastSellVolume = InitialVolume;
_buyVolume = InitialVolume;
_sellVolume = InitialVolume;
_avgBuyPrice = price;
_avgSellPrice = price;
_buyCount = 1;
_sellCount = 1;
return;
}
// --- Buy side management ---
if (_buyCount > 0) {
if (_buyCount < MaxTrades &&
price <= _lastBuyPrice - PipStep * step) {
var vol = _lastBuyVolume * LotExponent;
BuyMarket(vol);
_lastBuyVolume = vol;
_lastBuyPrice = price;
_avgBuyPrice = (_avgBuyPrice * _buyVolume + price * vol) /
(_buyVolume + vol);
_buyVolume += vol;
_buyCount++;
}
if (_buyVolume > 0 && price >= _avgBuyPrice + TakeProfit * step) {
// Close all buy positions when profit target is reached
SellMarket(_buyVolume);
_buyVolume = 0m;
_lastBuyPrice = 0m;
_lastBuyVolume = 0m;
_avgBuyPrice = 0m;
_buyCount = 0;
}
}
// --- Sell side management ---
if (_sellCount > 0) {
if (_sellCount < MaxTrades &&
price >= _lastSellPrice + PipStep * step) {
var vol = _lastSellVolume * LotExponent;
SellMarket(vol);
_lastSellVolume = vol;
_lastSellPrice = price;
_avgSellPrice = (_avgSellPrice * _sellVolume + price * vol) /
(_sellVolume + vol);
_sellVolume += vol;
_sellCount++;
}
if (_sellVolume > 0 && price <= _avgSellPrice - TakeProfit * step) {
// Close all sell positions when profit target is reached
BuyMarket(_sellVolume);
_sellVolume = 0m;
_lastSellPrice = 0m;
_lastSellVolume = 0m;
_avgSellPrice = 0m;
_sellCount = 0;
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class ilan14_strategy(Strategy):
def __init__(self):
super(ilan14_strategy, self).__init__()
self._pip_step = self.Param("PipStep", 30.0)
self._lot_exponent = self.Param("LotExponent", 1.667)
self._max_trades = self.Param("MaxTrades", 1)
self._take_profit = self.Param("TakeProfit", 300.0)
self._initial_volume = self.Param("InitialVolume", 0.1)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._last_buy_price = 0.0
self._last_sell_price = 0.0
self._last_buy_volume = 0.0
self._last_sell_volume = 0.0
self._buy_volume = 0.0
self._sell_volume = 0.0
self._avg_buy_price = 0.0
self._avg_sell_price = 0.0
self._buy_count = 0
self._sell_count = 0
@property
def PipStep(self):
return self._pip_step.Value
@PipStep.setter
def PipStep(self, value):
self._pip_step.Value = value
@property
def LotExponent(self):
return self._lot_exponent.Value
@LotExponent.setter
def LotExponent(self, value):
self._lot_exponent.Value = value
@property
def MaxTrades(self):
return self._max_trades.Value
@MaxTrades.setter
def MaxTrades(self, value):
self._max_trades.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def InitialVolume(self):
return self._initial_volume.Value
@InitialVolume.setter
def InitialVolume(self, value):
self._initial_volume.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(ilan14_strategy, self).OnStarted2(time)
self._last_buy_price = 0.0
self._last_sell_price = 0.0
self._last_buy_volume = 0.0
self._last_sell_volume = 0.0
self._buy_volume = 0.0
self._sell_volume = 0.0
self._avg_buy_price = 0.0
self._avg_sell_price = 0.0
self._buy_count = 0
self._sell_count = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(None, None)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
price = float(candle.ClosePrice)
pip_step = float(self.PipStep)
lot_exp = float(self.LotExponent)
max_trades = int(self.MaxTrades)
tp = float(self.TakeProfit)
init_vol = float(self.InitialVolume)
if self._buy_count == 0 and self._sell_count == 0:
self.BuyMarket(init_vol)
self.SellMarket(init_vol)
self._last_buy_price = price
self._last_sell_price = price
self._last_buy_volume = init_vol
self._last_sell_volume = init_vol
self._buy_volume = init_vol
self._sell_volume = init_vol
self._avg_buy_price = price
self._avg_sell_price = price
self._buy_count = 1
self._sell_count = 1
return
if self._buy_count > 0:
if self._buy_count < max_trades and price <= self._last_buy_price - pip_step * step:
vol = self._last_buy_volume * lot_exp
self.BuyMarket(vol)
self._last_buy_volume = vol
self._last_buy_price = price
self._avg_buy_price = (self._avg_buy_price * self._buy_volume + price * vol) / (self._buy_volume + vol)
self._buy_volume += vol
self._buy_count += 1
if self._buy_volume > 0.0 and price >= self._avg_buy_price + tp * step:
self.SellMarket(self._buy_volume)
self._buy_volume = 0.0
self._last_buy_price = 0.0
self._last_buy_volume = 0.0
self._avg_buy_price = 0.0
self._buy_count = 0
if self._sell_count > 0:
if self._sell_count < max_trades and price >= self._last_sell_price + pip_step * step:
vol = self._last_sell_volume * lot_exp
self.SellMarket(vol)
self._last_sell_volume = vol
self._last_sell_price = price
self._avg_sell_price = (self._avg_sell_price * self._sell_volume + price * vol) / (self._sell_volume + vol)
self._sell_volume += vol
self._sell_count += 1
if self._sell_volume > 0.0 and price <= self._avg_sell_price - tp * step:
self.BuyMarket(self._sell_volume)
self._sell_volume = 0.0
self._last_sell_price = 0.0
self._last_sell_volume = 0.0
self._avg_sell_price = 0.0
self._sell_count = 0
def OnReseted(self):
super(ilan14_strategy, self).OnReseted()
self._last_buy_price = 0.0
self._last_sell_price = 0.0
self._last_buy_volume = 0.0
self._last_sell_volume = 0.0
self._buy_volume = 0.0
self._sell_volume = 0.0
self._avg_buy_price = 0.0
self._avg_sell_price = 0.0
self._buy_count = 0
self._sell_count = 0
def CreateClone(self):
return ilan14_strategy()