Ilan14 策略
Ilan14 是一种对冲网格策略,同时开多单和空单。当市场朝某一方向不利移动指定点数时,策略会在该方向加仓,并将手数乘以 Lot Exponent。系统跟踪仓位的平均价格,当价格回撤到设定的 Take Profit 距离时,该方向上的所有订单都会被平仓获利。
参数:
- Pip Step – 网格订单之间的点差。
- Lot Exponent – 每次加仓的手数乘数。
- Max Trades – 每个方向的最大订单数量。
- Take Profit – 相对于平均价格的盈利点数。
- Initial Volume – 首次下单的手数。
- Candle Type – 订阅的K线周期。
该实现使用 StockSharp 的高级 API 并通过 K 线订阅处理数据,避免手动管理集合。两个方向的网格独立管理,从而在不利行情后价格回撤时获取收益。
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()