diff --git a/Common/Brokerages/InteractiveBrokersBrokerageModel.cs b/Common/Brokerages/InteractiveBrokersBrokerageModel.cs
index b93d1f3bd2cd..7fa60bc6191d 100644
--- a/Common/Brokerages/InteractiveBrokersBrokerageModel.cs
+++ b/Common/Brokerages/InteractiveBrokersBrokerageModel.cs
@@ -18,8 +18,6 @@
using System.Linq;
using QuantConnect.Util;
using QuantConnect.Benchmarks;
-using QuantConnect.Data.Shortable;
-using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.TimeInForces;
@@ -109,6 +107,21 @@ public override IFeeModel GetFeeModel(Security security)
return new InteractiveBrokersFeeModel();
}
+ ///
+ /// Gets the brokerage's leverage for the specified security
+ ///
+ /// The security's whose leverage we seek
+ /// The leverage for the specified security
+ public override decimal GetLeverage(Security security)
+ {
+ if (AccountType == AccountType.Cash)
+ {
+ return 1m;
+ }
+
+ return security.Type == SecurityType.Cfd ? 10m : base.GetLeverage(security);
+ }
+
///
/// Returns true if the brokerage could accept this order. This takes into account
/// order type, security type, and order size limits.
@@ -147,7 +160,8 @@ public override bool CanSubmitOrder(Security security, Order order, out Brokerag
security.Type != SecurityType.Future &&
security.Type != SecurityType.FutureOption &&
security.Type != SecurityType.Index &&
- security.Type != SecurityType.IndexOption)
+ security.Type != SecurityType.IndexOption &&
+ security.Type != SecurityType.Cfd)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
diff --git a/Common/Orders/Fees/InteractiveBrokersFeeModel.cs b/Common/Orders/Fees/InteractiveBrokersFeeModel.cs
index a21347b48765..f538ee0fb5dc 100644
--- a/Common/Orders/Fees/InteractiveBrokersFeeModel.cs
+++ b/Common/Orders/Fees/InteractiveBrokersFeeModel.cs
@@ -167,6 +167,12 @@ public override OrderFee GetOrderFee(OrderFeeParameters parameters)
feeResult = Math.Abs(tradeFee);
break;
+ case SecurityType.Cfd:
+ var value = Math.Abs(order.GetValue(security));
+ feeResult = Math.Max(0.00002m * value, 1); // 0.002% or 1USD minimum
+ feeCurrency = security.QuoteCurrency.Symbol;
+ break;
+
default:
// unsupported security type
throw new ArgumentException(Messages.FeeModel.UnsupportedSecurityType(security));
diff --git a/Tests/Common/Brokerages/InteractiveBrokersBrokerageModelTests.cs b/Tests/Common/Brokerages/InteractiveBrokersBrokerageModelTests.cs
index 5eb901f05c0d..e3baded8f5b1 100644
--- a/Tests/Common/Brokerages/InteractiveBrokersBrokerageModelTests.cs
+++ b/Tests/Common/Brokerages/InteractiveBrokersBrokerageModelTests.cs
@@ -27,9 +27,8 @@
using QuantConnect.Data;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
-using QuantConnect.Securities.IndexOption;
using QuantConnect.Tests.Engine.DataFeeds;
-using QuantConnect.Securities.FutureOption;
+using QuantConnect.Securities.Cfd;
namespace QuantConnect.Tests.Common.Brokerages
{
@@ -113,6 +112,39 @@ public void CannotSubmitMOCOrdersForOptions(string ticker, SecurityType security
Assert.AreEqual(expectedMessage, message.Message);
}
+ [TestCase(AccountType.Cash, 1)]
+ [TestCase(AccountType.Margin, 10)]
+ public void GetsCorrectLeverageForCfds(AccountType accounType, decimal expectedLeverage)
+ {
+ var brokerageModel = new InteractiveBrokersBrokerageModel(accounType);
+ var security = new Cfd(Symbols.DE10YBEUR,
+ SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork),
+ new Cash("USD", 0, 0),
+ SymbolProperties.GetDefault("USD"),
+ ErrorCurrencyConverter.Instance,
+ RegisteredSecurityDataTypesProvider.Null,
+ new SecurityCache());
+
+ Assert.AreEqual(expectedLeverage, brokerageModel.GetLeverage(security));
+ }
+
+ [Test]
+ public void CanSubmitCfdOrder()
+ {
+ var security = new Cfd(Symbols.DE10YBEUR,
+ SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork),
+ new Cash("USD", 0, 0),
+ SymbolProperties.GetDefault("USD"),
+ ErrorCurrencyConverter.Instance,
+ RegisteredSecurityDataTypesProvider.Null,
+ new SecurityCache());
+ var order = new MarketOrder(security.Symbol, 1, new DateTime(2023, 1, 20));
+
+ var canSubmit = _interactiveBrokersBrokerageModel.CanSubmitOrder(security, order, out var message);
+
+ Assert.IsTrue(canSubmit);
+ }
+
private static List GetUnsupportedOptions()
{
// Index option
diff --git a/Tests/Common/Orders/Fees/InteractiveBrokersFeeModelTests.cs b/Tests/Common/Orders/Fees/InteractiveBrokersFeeModelTests.cs
index d17252b5cf2f..f5e1d3eb7944 100644
--- a/Tests/Common/Orders/Fees/InteractiveBrokersFeeModelTests.cs
+++ b/Tests/Common/Orders/Fees/InteractiveBrokersFeeModelTests.cs
@@ -23,6 +23,7 @@
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Securities.Cfd;
+using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.FutureOption;
@@ -99,6 +100,30 @@ public void USAFutureFee(Symbol symbol, decimal expectedFee)
Assert.AreEqual(1000 * expectedFee, fee.Value.Amount);
}
+ [TestCase(10000, 1)] // The calculated fee will be under 1, but the minimum fee is 1
+ [TestCase(70000, 0.00002 * 70000)]
+ [TestCase(100000, 0.00002 * 100000)]
+ public void CalculatesCFDFee(decimal price, decimal expectedFee)
+ {
+ var security = new Cfd(Symbols.DE10YBEUR,
+ SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork),
+ new Cash("USD", 0, 0),
+ SymbolProperties.GetDefault("USD"),
+ ErrorCurrencyConverter.Instance,
+ RegisteredSecurityDataTypesProvider.Null,
+ new SecurityCache());
+ security.QuoteCurrency.ConversionRate = 1;
+
+
+ security.SetMarketPrice(new Tick(DateTime.UtcNow, security.Symbol, price, price));
+
+ var order = new MarketOrder(security.Symbol, 1, DateTime.UtcNow);
+ var fee = _feeModel.GetOrderFee(new OrderFeeParameters(security, order));
+
+ Assert.AreEqual(Currencies.USD, fee.Value.Currency);
+ Assert.AreEqual(expectedFee, fee.Value.Amount);
+ }
+
[TestCase(OrderType.ComboMarket, 0.01, 250)]
[TestCase(OrderType.ComboLimit, 0.01, 250)]
[TestCase(OrderType.ComboLegLimit, 0.01, 250)]
@@ -256,13 +281,15 @@ public void GetOrderFeeThrowsForUnsupportedSecurityType()
() =>
{
var tz = TimeZones.NewYork;
- var security = new Cfd(
+ var security = new Crypto(
+ Symbols.BTCUSD,
SecurityExchangeHours.AlwaysOpen(tz),
- new Cash("EUR", 0, 0),
- new SubscriptionDataConfig(typeof(QuoteBar), Symbols.DE30EUR, Resolution.Minute, tz, tz, true, false, false),
- new SymbolProperties("DE30EUR", "EUR", 1, 0.01m, 1m, string.Empty),
+ new Cash("USD", 0, 0),
+ new Cash("BTC", 0, 0),
+ SymbolProperties.GetDefault("USD"),
ErrorCurrencyConverter.Instance,
- RegisteredSecurityDataTypesProvider.Null
+ RegisteredSecurityDataTypesProvider.Null,
+ new SecurityCache()
);
security.SetMarketPrice(new Tick(DateTime.UtcNow, security.Symbol, 12000, 12000));