19 using System.Collections.Generic;
28 private readonly decimal _forexCommissionRate;
29 private readonly decimal _forexMinimumOrderFee;
32 private readonly Dictionary<string, Func<decimal, decimal, CashAmount>> _optionFee =
33 new Dictionary<string, Func<decimal, decimal, CashAmount>>();
38 private readonly Dictionary<string, Func<Security, CashAmount>> _futureFee =
53 ProcessForexRateSchedule(monthlyForexTradeAmountInUSDollars, out _forexCommissionRate, out _forexMinimumOrderFee);
54 Func<decimal, decimal, CashAmount> optionsCommissionFunc;
55 ProcessOptionsRateSchedule(monthlyOptionsTradeAmountInContracts, out optionsCommissionFunc);
57 _optionFee.Add(
Market.
USA, optionsCommissionFunc);
69 var order = parameters.
Order;
73 if (order.Type ==
OrderType.OptionExercise)
79 if (optionOrder.Symbol.ID.SecurityType ==
SecurityType.Option)
85 var quantity = order.AbsoluteQuantity;
88 var market = security.Symbol.ID.Market;
89 switch (security.Type)
93 var totalOrderValue = order.GetValue(security);
94 var fee = Math.Abs(_forexCommissionRate*totalOrderValue);
95 feeResult = Math.Max(_forexMinimumOrderFee, fee);
102 Func<decimal, decimal, CashAmount> optionsCommissionFunc;
103 if (!_optionFee.TryGetValue(market, out optionsCommissionFunc))
109 feeResult = optionFee.Amount;
110 feeCurrency = optionFee.Currency;
125 if (!_futureFee.TryGetValue(market, out var feeRatePerContractFunc))
130 var feeRatePerContract = feeRatePerContractFunc(security);
131 feeResult = quantity * feeRatePerContract.Amount;
132 feeCurrency = feeRatePerContract.Currency;
140 equityFee =
new EquityFee(
Currencies.
USD, feePerShare: 0.005m, minimumFee: 1, maximumFeeRate: 0.005m);
143 equityFee =
new EquityFee(
Currencies.
INR, feePerShare: 0.01m, minimumFee: 6, maximumFeeRate: 20);
148 var tradeValue = Math.Abs(order.GetValue(security));
151 var tradeFee = equityFee.FeePerShare * quantity;
155 var maximumPerOrder = equityFee.MaximumFeeRate * tradeValue;
156 if (tradeFee < equityFee.MinimumFee)
158 tradeFee = equityFee.MinimumFee;
160 else if (tradeFee > maximumPerOrder)
162 tradeFee = maximumPerOrder;
165 feeCurrency = equityFee.Currency;
167 feeResult = Math.Abs(tradeFee);
171 var value = Math.Abs(order.GetValue(security));
172 feeResult = 0.00002m * value;
173 feeCurrency = security.QuoteCurrency.Symbol;
175 var minimumFee = security.QuoteCurrency.Symbol
switch
181 feeResult = Math.Max(feeResult, minimumFee);
186 throw new ArgumentException(
Messages.
FeeModel.UnsupportedSecurityType(security));
212 decimal securityPrice;
245 private static void ProcessForexRateSchedule(decimal monthlyForexTradeAmountInUSDollars, out decimal commissionRate, out decimal minimumOrderFee)
247 const decimal bp = 0.0001m;
248 if (monthlyForexTradeAmountInUSDollars <= 1000000000)
250 commissionRate = 0.20m * bp;
251 minimumOrderFee = 2.00m;
253 else if (monthlyForexTradeAmountInUSDollars <= 2000000000)
255 commissionRate = 0.15m * bp;
256 minimumOrderFee = 1.50m;
258 else if (monthlyForexTradeAmountInUSDollars <= 5000000000)
260 commissionRate = 0.10m * bp;
261 minimumOrderFee = 1.25m;
265 commissionRate = 0.08m * bp;
266 minimumOrderFee = 1.00m;
273 private static void ProcessOptionsRateSchedule(decimal monthlyOptionsTradeAmountInContracts, out Func<decimal, decimal, CashAmount> optionsCommissionFunc)
275 if (monthlyOptionsTradeAmountInContracts <= 10000)
277 optionsCommissionFunc = (orderSize, premium) =>
279 var commissionRate = premium >= 0.1m ?
281 (0.05m <= premium && premium < 0.1m ? 0.5m : 0.25m);
282 return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
285 else if (monthlyOptionsTradeAmountInContracts <= 50000)
287 optionsCommissionFunc = (orderSize, premium) =>
289 var commissionRate = premium >= 0.05m ? 0.5m : 0.25m;
290 return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
293 else if (monthlyOptionsTradeAmountInContracts <= 100000)
295 optionsCommissionFunc = (orderSize, premium) =>
297 var commissionRate = 0.25m;
298 return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
303 optionsCommissionFunc = (orderSize, premium) =>
305 var commissionRate = 0.15m;
306 return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
313 IDictionary<string, decimal> fees, exchangeFees;
314 decimal ibFeePerContract, exchangeFeePerContract;
320 fees = _usaFuturesFees;
321 exchangeFees = _usaFuturesExchangeFees;
325 fees = _usaFutureOptionsFees;
326 exchangeFees = _usaFutureOptionsExchangeFees;
330 throw new ArgumentException(Messages.InteractiveBrokersFeeModel.UnitedStatesFutureFeesUnsupportedSecurityType(security));
333 if (!fees.TryGetValue(symbol, out ibFeePerContract))
335 ibFeePerContract = 0.85m;
338 if (!exchangeFees.TryGetValue(symbol, out exchangeFeePerContract))
340 exchangeFeePerContract = 1.60m;
344 return new CashAmount(ibFeePerContract + exchangeFeePerContract + 0.02m, Currencies.USD);
352 if (security.
Symbol.
ID.
Symbol.Equals(
"HSI", StringComparison.InvariantCultureIgnoreCase))
355 return new CashAmount(30 + 10, Currencies.HKD);
358 decimal ibFeePerContract;
362 ibFeePerContract = 13;
365 ibFeePerContract = 20;
368 ibFeePerContract = 2.40m;
371 throw new ArgumentException(Messages.InteractiveBrokersFeeModel.HongKongFutureFeesUnexpectedQuoteCurrency(security));
381 private static readonly Dictionary<string, decimal> _usaFuturesFees =
new()
384 {
"MYM", 0.25m }, {
"M2K", 0.25m }, {
"MES", 0.25m }, {
"MNQ", 0.25m }, {
"2YY", 0.25m }, {
"5YY", 0.25m }, {
"10Y", 0.25m },
385 {
"30Y", 0.25m }, {
"MCL", 0.25m }, {
"MGC", 0.25m }, {
"SIL", 0.25m },
387 {
"BTC", 5m }, {
"MBT", 2.25m }, {
"ETH", 3m }, {
"MET", 0.20m },
389 {
"E7", 0.50m }, {
"J7", 0.50m },
391 {
"M6E", 0.15m }, {
"M6A", 0.15m }, {
"M6B", 0.15m }, {
"MCD", 0.15m }, {
"MJY", 0.15m }, {
"MSF", 0.15m }, {
"M6J", 0.15m },
392 {
"MIR", 0.15m }, {
"M6C", 0.15m }, {
"M6S", 0.15m }, {
"MNH", 0.15m },
395 private static readonly Dictionary<string, decimal> _usaFutureOptionsFees =
new()
398 {
"MYM", 0.25m }, {
"M2K", 0.25m }, {
"MES", 0.25m }, {
"MNQ", 0.25m }, {
"2YY", 0.25m }, {
"5YY", 0.25m }, {
"10Y", 0.25m },
399 {
"30Y", 0.25m }, {
"MCL", 0.25m }, {
"MGC", 0.25m }, {
"SIL", 0.25m },
401 {
"BTC", 5m }, {
"MBT", 1.25m }, {
"ETH", 3m }, {
"MET", 0.10m },
404 private static readonly Dictionary<string, decimal> _usaFuturesExchangeFees =
new()
407 {
"ES", 1.28m }, {
"NQ", 1.28m }, {
"YM", 1.28m }, {
"RTY", 1.28m }, {
"EMD", 1.28m },
409 {
"MYM", 0.30m }, {
"M2K", 0.30m }, {
"MES", 0.30m }, {
"MNQ", 0.30m }, {
"2YY", 0.30m }, {
"5YY", 0.30m }, {
"10Y", 0.30m },
410 {
"30Y", 0.30m }, {
"MCL", 0.30m }, {
"MGC", 0.30m }, {
"SIL", 0.30m },
412 {
"BTC", 6m }, {
"MBT", 2.5m }, {
"ETH", 4m }, {
"MET", 0.20m },
414 {
"E7", 0.85m }, {
"J7", 0.85m },
416 {
"M6E", 0.24m }, {
"M6A", 0.24m }, {
"M6B", 0.24m }, {
"MCD", 0.24m }, {
"MJY", 0.24m }, {
"MSF", 0.24m }, {
"M6J", 0.24m },
417 {
"MIR", 0.24m }, {
"M6C", 0.24m }, {
"M6S", 0.24m }, {
"MNH", 0.24m },
420 private static readonly Dictionary<string, decimal> _usaFutureOptionsExchangeFees =
new()
423 {
"ES", 0.55m }, {
"NQ", 0.55m }, {
"YM", 0.55m }, {
"RTY", 0.55m }, {
"EMD", 0.55m },
425 {
"MYM", 0.20m }, {
"M2K", 0.20m }, {
"MES", 0.20m }, {
"MNQ", 0.20m }, {
"2YY", 0.20m }, {
"5YY", 0.20m }, {
"10Y", 0.20m },
426 {
"30Y", 0.20m }, {
"MCL", 0.20m }, {
"MGC", 0.20m }, {
"SIL", 0.20m },
428 {
"BTC", 5m }, {
"MBT", 2.5m }, {
"ETH", 4m }, {
"MET", 0.20m },
434 private class EquityFee
436 public string Currency {
get; }
437 public decimal FeePerShare {
get; }
438 public decimal MinimumFee {
get; }
439 public decimal MaximumFeeRate {
get; }
441 public EquityFee(
string currency,
444 decimal maximumFeeRate)
447 FeePerShare = feePerShare;
448 MinimumFee = minimumFee;
449 MaximumFeeRate = maximumFeeRate;