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>>();
35 #pragma warning disable CS1570
39 #pragma warning restore CS1570
40 private readonly Dictionary<string, Func<Security, CashAmount>> _futureFee =
56 ProcessForexRateSchedule(monthlyForexTradeAmountInUSDollars, out _forexCommissionRate, out _forexMinimumOrderFee);
57 Func<decimal, decimal, CashAmount> optionsCommissionFunc;
58 ProcessOptionsRateSchedule(monthlyOptionsTradeAmountInContracts, out optionsCommissionFunc);
60 _optionFee.Add(
Market.
USA, optionsCommissionFunc);
72 var order = parameters.
Order;
76 if (order.Type ==
OrderType.OptionExercise)
82 if (optionOrder.Symbol.ID.SecurityType ==
SecurityType.Option)
88 var quantity = order.AbsoluteQuantity;
91 var market = security.Symbol.ID.Market;
92 switch (security.Type)
96 var totalOrderValue = order.GetValue(security);
97 var fee = Math.Abs(_forexCommissionRate*totalOrderValue);
98 feeResult = Math.Max(_forexMinimumOrderFee, fee);
105 Func<decimal, decimal, CashAmount> optionsCommissionFunc;
106 if (!_optionFee.TryGetValue(market, out optionsCommissionFunc))
112 feeResult = optionFee.Amount;
113 feeCurrency = optionFee.Currency;
128 if (!_futureFee.TryGetValue(market, out var feeRatePerContractFunc))
133 var feeRatePerContract = feeRatePerContractFunc(security);
134 feeResult = quantity * feeRatePerContract.Amount;
135 feeCurrency = feeRatePerContract.Currency;
143 equityFee =
new EquityFee(
Currencies.
USD, feePerShare: 0.005m, minimumFee: 1, maximumFeeRate: 0.005m);
146 equityFee =
new EquityFee(
Currencies.
INR, feePerShare: 0.01m, minimumFee: 6, maximumFeeRate: 20);
151 var tradeValue = Math.Abs(order.GetValue(security));
154 var tradeFee = equityFee.FeePerShare * quantity;
158 var maximumPerOrder = equityFee.MaximumFeeRate * tradeValue;
159 if (tradeFee < equityFee.MinimumFee)
161 tradeFee = equityFee.MinimumFee;
163 else if (tradeFee > maximumPerOrder)
165 tradeFee = maximumPerOrder;
168 feeCurrency = equityFee.Currency;
170 feeResult = Math.Abs(tradeFee);
174 var value = Math.Abs(order.GetValue(security));
175 feeResult = 0.00002m * value;
176 feeCurrency = security.QuoteCurrency.Symbol;
178 var minimumFee = security.QuoteCurrency.Symbol
switch
184 feeResult = Math.Max(feeResult, minimumFee);
215 decimal securityPrice;
248 private static void ProcessForexRateSchedule(decimal monthlyForexTradeAmountInUSDollars, out decimal commissionRate, out decimal minimumOrderFee)
250 const decimal bp = 0.0001m;
251 if (monthlyForexTradeAmountInUSDollars <= 1000000000)
253 commissionRate = 0.20m * bp;
254 minimumOrderFee = 2.00m;
256 else if (monthlyForexTradeAmountInUSDollars <= 2000000000)
258 commissionRate = 0.15m * bp;
259 minimumOrderFee = 1.50m;
261 else if (monthlyForexTradeAmountInUSDollars <= 5000000000)
263 commissionRate = 0.10m * bp;
264 minimumOrderFee = 1.25m;
268 commissionRate = 0.08m * bp;
269 minimumOrderFee = 1.00m;
276 private static void ProcessOptionsRateSchedule(decimal monthlyOptionsTradeAmountInContracts, out Func<decimal, decimal, CashAmount> optionsCommissionFunc)
278 if (monthlyOptionsTradeAmountInContracts <= 10000)
280 optionsCommissionFunc = (orderSize, premium) =>
282 var commissionRate = premium >= 0.1m ?
284 (0.05m <= premium && premium < 0.1m ? 0.5m : 0.25m);
285 return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
288 else if (monthlyOptionsTradeAmountInContracts <= 50000)
290 optionsCommissionFunc = (orderSize, premium) =>
292 var commissionRate = premium >= 0.05m ? 0.5m : 0.25m;
293 return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
296 else if (monthlyOptionsTradeAmountInContracts <= 100000)
298 optionsCommissionFunc = (orderSize, premium) =>
300 var commissionRate = 0.25m;
301 return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
306 optionsCommissionFunc = (orderSize, premium) =>
308 var commissionRate = 0.15m;
309 return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
316 IDictionary<string, decimal> fees, exchangeFees;
317 decimal ibFeePerContract, exchangeFeePerContract;
323 fees = _usaFuturesFees;
324 exchangeFees = _usaFuturesExchangeFees;
328 fees = _usaFutureOptionsFees;
329 exchangeFees = _usaFutureOptionsExchangeFees;
333 throw new ArgumentException(Messages.InteractiveBrokersFeeModel.UnitedStatesFutureFeesUnsupportedSecurityType(security));
336 if (!fees.TryGetValue(symbol, out ibFeePerContract))
338 ibFeePerContract = 0.85m;
341 if (!exchangeFees.TryGetValue(symbol, out exchangeFeePerContract))
343 exchangeFeePerContract = 1.60m;
347 return new CashAmount(ibFeePerContract + exchangeFeePerContract + 0.02m, Currencies.USD);
355 if (security.
Symbol.
ID.
Symbol.Equals(
"HSI", StringComparison.InvariantCultureIgnoreCase))
358 return new CashAmount(30 + 10, Currencies.HKD);
361 decimal ibFeePerContract;
365 ibFeePerContract = 13;
368 ibFeePerContract = 20;
371 ibFeePerContract = 2.40m;
374 throw new ArgumentException(Messages.InteractiveBrokersFeeModel.HongKongFutureFeesUnexpectedQuoteCurrency(security));
383 IDictionary<string, decimal> fees, exchangeFees;
384 decimal ibFeePerContract, exchangeFeePerContract;
390 fees = _eurexFuturesFees;
391 exchangeFees = _eurexFuturesExchangeFees;
395 throw new ArgumentException(Messages.InteractiveBrokersFeeModel.EUREXFutureFeesUnsupportedSecurityType(security));
398 if (!fees.TryGetValue(symbol, out ibFeePerContract))
400 ibFeePerContract = 1.00m;
403 if (!exchangeFees.TryGetValue(symbol, out exchangeFeePerContract))
405 exchangeFeePerContract = 0.00m;
409 return new CashAmount(ibFeePerContract + exchangeFeePerContract + 0.02m, Currencies.EUR);
415 private static readonly Dictionary<string, decimal> _usaFuturesFees =
new()
418 {
"MYM", 0.25m }, {
"M2K", 0.25m }, {
"MES", 0.25m }, {
"MNQ", 0.25m }, {
"2YY", 0.25m }, {
"5YY", 0.25m }, {
"10Y", 0.25m },
419 {
"30Y", 0.25m }, {
"MCL", 0.25m }, {
"MGC", 0.25m }, {
"SIL", 0.25m },
421 {
"BTC", 5m }, {
"MBT", 2.25m }, {
"ETH", 3m }, {
"MET", 0.20m },
423 {
"E7", 0.50m }, {
"J7", 0.50m },
425 {
"M6E", 0.15m }, {
"M6A", 0.15m }, {
"M6B", 0.15m }, {
"MCD", 0.15m }, {
"MJY", 0.15m }, {
"MSF", 0.15m }, {
"M6J", 0.15m },
426 {
"MIR", 0.15m }, {
"M6C", 0.15m }, {
"M6S", 0.15m }, {
"MNH", 0.15m },
432 private static readonly Dictionary<string, decimal> _eurexFuturesFees =
new()
438 private static readonly Dictionary<string, decimal> _usaFutureOptionsFees =
new()
441 {
"MYM", 0.25m }, {
"M2K", 0.25m }, {
"MES", 0.25m }, {
"MNQ", 0.25m }, {
"2YY", 0.25m }, {
"5YY", 0.25m }, {
"10Y", 0.25m },
442 {
"30Y", 0.25m }, {
"MCL", 0.25m }, {
"MGC", 0.25m }, {
"SIL", 0.25m },
444 {
"BTC", 5m }, {
"MBT", 1.25m }, {
"ETH", 3m }, {
"MET", 0.10m },
447 private static readonly Dictionary<string, decimal> _usaFuturesExchangeFees =
new()
450 {
"ES", 1.28m }, {
"NQ", 1.28m }, {
"YM", 1.28m }, {
"RTY", 1.28m }, {
"EMD", 1.28m },
452 {
"MYM", 0.30m }, {
"M2K", 0.30m }, {
"MES", 0.30m }, {
"MNQ", 0.30m }, {
"2YY", 0.30m }, {
"5YY", 0.30m }, {
"10Y", 0.30m },
453 {
"30Y", 0.30m }, {
"MCL", 0.30m }, {
"MGC", 0.30m }, {
"SIL", 0.30m },
455 {
"BTC", 6m }, {
"MBT", 2.5m }, {
"ETH", 4m }, {
"MET", 0.20m },
457 {
"E7", 0.85m }, {
"J7", 0.85m },
459 {
"M6E", 0.24m }, {
"M6A", 0.24m }, {
"M6B", 0.24m }, {
"MCD", 0.24m }, {
"MJY", 0.24m }, {
"MSF", 0.24m }, {
"M6J", 0.24m },
460 {
"MIR", 0.24m }, {
"M6C", 0.24m }, {
"M6S", 0.24m }, {
"MNH", 0.24m },
463 private static readonly Dictionary<string, decimal> _eurexFuturesExchangeFees =
new()
469 private static readonly Dictionary<string, decimal> _usaFutureOptionsExchangeFees =
new()
472 {
"ES", 0.55m }, {
"NQ", 0.55m }, {
"YM", 0.55m }, {
"RTY", 0.55m }, {
"EMD", 0.55m },
474 {
"MYM", 0.20m }, {
"M2K", 0.20m }, {
"MES", 0.20m }, {
"MNQ", 0.20m }, {
"2YY", 0.20m }, {
"5YY", 0.20m }, {
"10Y", 0.20m },
475 {
"30Y", 0.20m }, {
"MCL", 0.20m }, {
"MGC", 0.20m }, {
"SIL", 0.20m },
477 {
"BTC", 5m }, {
"MBT", 2.5m }, {
"ETH", 4m }, {
"MET", 0.20m },
483 private class EquityFee
485 public string Currency {
get; }
486 public decimal FeePerShare {
get; }
487 public decimal MinimumFee {
get; }
488 public decimal MaximumFeeRate {
get; }
490 public EquityFee(
string currency,
493 decimal maximumFeeRate)
496 FeePerShare = feePerShare;
497 MinimumFee = minimumFee;
498 MaximumFeeRate = maximumFeeRate;