From daea69496822e962daceb49b6568c6a0577ac0ae Mon Sep 17 00:00:00 2001 From: Romazes Date: Thu, 5 Mar 2026 22:39:39 +0200 Subject: [PATCH 1/3] feat: add Bybit Inverse Futures brokerage model and tests Introduced BybitInverseFuturesBrokerageModel to support COIN-Margined contracts. Updated BrokerageName enum and factory logic for model creation and identification. Added unit tests for fee model, leverage, and integration. Enables correct handling of Bybit inverse futures in Lean. --- Common/Brokerages/BrokerageName.cs | 5 ++ .../BybitInverseFuturesBrokerageModel.cs | 56 +++++++++++++++ Common/Brokerages/IBrokerageModel.cs | 6 ++ .../BybitInverseFuturesBrokerageModelTests.cs | 72 +++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 Common/Brokerages/BybitInverseFuturesBrokerageModel.cs create mode 100644 Tests/Common/Brokerages/BybitInverseFuturesBrokerageModelTests.cs diff --git a/Common/Brokerages/BrokerageName.cs b/Common/Brokerages/BrokerageName.cs index 7f6edaab7a5e..5c3dd2705629 100644 --- a/Common/Brokerages/BrokerageName.cs +++ b/Common/Brokerages/BrokerageName.cs @@ -154,6 +154,11 @@ public enum BrokerageName /// Bybit, + /// + /// Bybit Inverse Futures COIN-Margined contracts are settled and collateralized in their base cryptocurrency. + /// + BybitInverseFutures, + /// /// Transaction and submit/execution rules will use Eze models /// diff --git a/Common/Brokerages/BybitInverseFuturesBrokerageModel.cs b/Common/Brokerages/BybitInverseFuturesBrokerageModel.cs new file mode 100644 index 000000000000..be93f4e28187 --- /dev/null +++ b/Common/Brokerages/BybitInverseFuturesBrokerageModel.cs @@ -0,0 +1,56 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +using QuantConnect.Benchmarks; +using QuantConnect.Orders.Fees; +using QuantConnect.Securities; + +namespace QuantConnect.Brokerages; + +/// +/// Provides Bybit Inverse Futures specific properties. +/// Inverse (COIN-Margined) contracts are settled and collateralized in their base cryptocurrency (e.g. BTC for BTCUSD). +/// +public class BybitInverseFuturesBrokerageModel : BybitBrokerageModel +{ + /// + /// Initializes a new instance of the class + /// + /// The type of account to be modeled, defaults to + public BybitInverseFuturesBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType) + { + } + + /// + /// Get the benchmark for this model + /// + /// SecurityService to create the security with if needed + /// The benchmark for this brokerage + public override IBenchmark GetBenchmark(SecurityManager securities) + { + var symbol = Symbol.Create("BTCUSD", SecurityType.CryptoFuture, MarketName); + return SecurityBenchmark.CreateInstance(securities, symbol); + } + + /// + /// Provides Bybit Inverse Futures fee model + /// + /// The security to get a fee model for + /// The new fee model for this brokerage + public override IFeeModel GetFeeModel(Security security) + { + return new BybitFuturesFeeModel(); + } +} diff --git a/Common/Brokerages/IBrokerageModel.cs b/Common/Brokerages/IBrokerageModel.cs index 4544c388d8c1..246f37140551 100644 --- a/Common/Brokerages/IBrokerageModel.cs +++ b/Common/Brokerages/IBrokerageModel.cs @@ -276,6 +276,9 @@ public static IBrokerageModel Create(IOrderProvider orderProvider, BrokerageName case BrokerageName.Bybit: return new BybitBrokerageModel(accountType); + case BrokerageName.BybitInverseFutures: + return new BybitInverseFuturesBrokerageModel(accountType); + case BrokerageName.Eze: return new EzeBrokerageModel(accountType); @@ -379,6 +382,9 @@ public static BrokerageName GetBrokerageName(IBrokerageModel brokerageModel) case RBIBrokerageModel _: return BrokerageName.RBI; + case BybitInverseFuturesBrokerageModel _: + return BrokerageName.BybitInverseFutures; + case BybitBrokerageModel _: return BrokerageName.Bybit; diff --git a/Tests/Common/Brokerages/BybitInverseFuturesBrokerageModelTests.cs b/Tests/Common/Brokerages/BybitInverseFuturesBrokerageModelTests.cs new file mode 100644 index 000000000000..b4a5acf9f93f --- /dev/null +++ b/Tests/Common/Brokerages/BybitInverseFuturesBrokerageModelTests.cs @@ -0,0 +1,72 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using NUnit.Framework; +using QuantConnect.Brokerages; +using QuantConnect.Orders.Fees; +using QuantConnect.Tests.Brokerages; + +namespace QuantConnect.Tests.Common.Brokerages +{ + [TestFixture, Parallelizable(ParallelScope.All)] + public class BybitInverseFuturesBrokerageModelTests + { + private static readonly Symbol BTCUSD_Future = Symbol.Create("BTCUSD", SecurityType.CryptoFuture, Market.Bybit); + private static readonly BybitInverseFuturesBrokerageModel Model = new(); + + [Test] + public void DefaultAccountTypeIsMargin() + { + Assert.AreEqual(AccountType.Margin, Model.AccountType); + } + + [Test] + public void GetFeeModelReturnsBybitFuturesFeeModel_ForCryptoFuture() + { + var security = TestsHelpers.GetSecurity(symbol: BTCUSD_Future.Value, + securityType: SecurityType.CryptoFuture, + market: Market.Bybit, + quoteCurrency: "USD"); + + Assert.IsInstanceOf(Model.GetFeeModel(security)); + } + + [Test] + public void GetBrokerageNameReturnsBybitInverseFutures() + { + Assert.AreEqual(BrokerageName.BybitInverseFutures, BrokerageModel.GetBrokerageName(new BybitInverseFuturesBrokerageModel())); + } + + [Test] + public void GetBrokerageModelReturnsInverseFuturesModel() + { + var model = BrokerageModel.Create(null, BrokerageName.BybitInverseFutures, AccountType.Margin); + Assert.IsInstanceOf(model); + } + + [TestCase(AccountType.Cash, 1)] + [TestCase(AccountType.Margin, 10)] + public void GetLeverageReturnsCorrectValue(AccountType accountType, decimal expectedLeverage) + { + var security = TestsHelpers.GetSecurity(symbol: BTCUSD_Future.Value, + securityType: SecurityType.CryptoFuture, + market: Market.Bybit, + quoteCurrency: "USD"); + + var model = new BybitInverseFuturesBrokerageModel(accountType); + Assert.AreEqual(expectedLeverage, model.GetLeverage(security)); + } + } +} From 1ff230cf365d8b2e6780d6811a650db446203d7a Mon Sep 17 00:00:00 2001 From: Romazes Date: Fri, 6 Mar 2026 19:51:26 +0200 Subject: [PATCH 2/3] feat:test: add test for USD collateral in Bybit inverse futures --- .../BybitInverseFuturesBrokerageModel.cs | 15 ++++++++ .../BybitInverseFuturesMarginModel.cs | 32 +++++++++++++++++ .../CryptoFuture/CryptoFutureMarginModel.cs | 2 +- .../BybitInverseFuturesBrokerageModelTests.cs | 36 +++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 Common/Securities/CryptoFuture/BybitInverseFuturesMarginModel.cs diff --git a/Common/Brokerages/BybitInverseFuturesBrokerageModel.cs b/Common/Brokerages/BybitInverseFuturesBrokerageModel.cs index be93f4e28187..11d9425654e3 100644 --- a/Common/Brokerages/BybitInverseFuturesBrokerageModel.cs +++ b/Common/Brokerages/BybitInverseFuturesBrokerageModel.cs @@ -16,6 +16,7 @@ using QuantConnect.Benchmarks; using QuantConnect.Orders.Fees; using QuantConnect.Securities; +using QuantConnect.Securities.CryptoFuture; namespace QuantConnect.Brokerages; @@ -53,4 +54,18 @@ public override IFeeModel GetFeeModel(Security security) { return new BybitFuturesFeeModel(); } + + /// + /// Gets a new buying power model for the security + /// + /// The security to get a buying power model for + /// The buying power model for this brokerage/security + public override IBuyingPowerModel GetBuyingPowerModel(Security security) + { + if (security.Type == SecurityType.CryptoFuture) + { + return new BybitInverseFuturesMarginModel(GetLeverage(security)); + } + return base.GetBuyingPowerModel(security); + } } diff --git a/Common/Securities/CryptoFuture/BybitInverseFuturesMarginModel.cs b/Common/Securities/CryptoFuture/BybitInverseFuturesMarginModel.cs new file mode 100644 index 000000000000..84b729e95734 --- /dev/null +++ b/Common/Securities/CryptoFuture/BybitInverseFuturesMarginModel.cs @@ -0,0 +1,32 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +namespace QuantConnect.Securities.CryptoFuture +{ + /// + /// Margin model for Bybit Inverse Futures using the Unified Trading Account (UTA). + /// In UTA, reports TotalAvailableBalance as USD, + /// so we use the quote currency (USD) as collateral instead of the base crypto (e.g. ADA). + /// + public class BybitInverseFuturesMarginModel : CryptoFutureMarginModel + { + public BybitInverseFuturesMarginModel(decimal leverage) : base(leverage) { } + + private protected override Cash GetCollateralCash(Security security) + { + return (security as CryptoFuture).QuoteCurrency; + } + } +} diff --git a/Common/Securities/CryptoFuture/CryptoFutureMarginModel.cs b/Common/Securities/CryptoFuture/CryptoFutureMarginModel.cs index 0b6ce4528adf..f8f2080c019c 100644 --- a/Common/Securities/CryptoFuture/CryptoFutureMarginModel.cs +++ b/Common/Securities/CryptoFuture/CryptoFutureMarginModel.cs @@ -142,7 +142,7 @@ protected override decimal GetMarginRemaining(SecurityPortfolioManager portfolio /// /// Helper method to determine what's the collateral currency for the given crypto future /// - private static Cash GetCollateralCash(Security security) + private protected virtual Cash GetCollateralCash(Security security) { var cryptoFuture = (CryptoFuture)security; diff --git a/Tests/Common/Brokerages/BybitInverseFuturesBrokerageModelTests.cs b/Tests/Common/Brokerages/BybitInverseFuturesBrokerageModelTests.cs index b4a5acf9f93f..f01ded481cd9 100644 --- a/Tests/Common/Brokerages/BybitInverseFuturesBrokerageModelTests.cs +++ b/Tests/Common/Brokerages/BybitInverseFuturesBrokerageModelTests.cs @@ -13,10 +13,15 @@ * limitations under the License. */ +using System; using NUnit.Framework; using QuantConnect.Brokerages; +using QuantConnect.Data.Market; +using QuantConnect.Orders; using QuantConnect.Orders.Fees; +using QuantConnect.Securities; using QuantConnect.Tests.Brokerages; +using QuantConnect.Tests.Engine.DataFeeds; namespace QuantConnect.Tests.Common.Brokerages { @@ -68,5 +73,36 @@ public void GetLeverageReturnsCorrectValue(AccountType accountType, decimal expe var model = new BybitInverseFuturesBrokerageModel(accountType); Assert.AreEqual(expectedLeverage, model.GetLeverage(security)); } + + [TestCase(10, 0.40, Description = "leverage=10 => initialMargin ≈ 4 / 0.267 / 10 * 0.267 = 0.40 USD")] + [TestCase(25, 0.16, Description = "leverage=25 => initialMargin ≈ 4 / 0.267 / 25 * 0.267 = 0.16 USD")] + public void GetBuyingPowerUsesUsdBalance_WithDifferentLeverage(decimal leverage, double expectedInitialMarginUsd) + { + // Reproduces the live trading scenario: Bybit UTA reports TotalAvailableBalance as USD + // (no ADA in account), so the margin model must use USD as collateral. + var algo = new AlgorithmStub(); + algo.SetBrokerageModel(BrokerageName.BybitInverseFutures, AccountType.Margin); + algo.SetFinishedWarmingUp(); + + var adaUsd = algo.AddCryptoFuture("ADAUSD"); + adaUsd.SetLeverage(leverage); + + const decimal adaPrice = 0.267m; + const decimal usdBalance = 100m; + + adaUsd.QuoteCurrency.SetAmount(usdBalance); // USD = 100 (from GetCashBalance) + adaUsd.BaseCurrency.SetAmount(0m); // ADA = 0 (no base asset in account) + adaUsd.BaseCurrency.ConversionRate = adaPrice; + adaUsd.QuoteCurrency.ConversionRate = 1m; + adaUsd.SetMarketPrice(new TradeBar(new DateTime(2026, 1, 1), adaUsd.Symbol, adaPrice, adaPrice, adaPrice, adaPrice, volume: 1m)); + + // Buying power = USD balance regardless of leverage + var buyingPower = adaUsd.BuyingPowerModel.GetBuyingPower(new BuyingPowerParameters(algo.Portfolio, adaUsd, OrderDirection.Buy)); + Assert.AreEqual((double)usdBalance, (double)buyingPower.Value, delta: 0.01); + + // Initial margin scales inversely with leverage + var initialMargin = adaUsd.BuyingPowerModel.GetInitialMarginRequirement(new InitialMarginParameters(adaUsd, 4)); + Assert.AreEqual(expectedInitialMarginUsd, (double)initialMargin.Value, delta: 0.05); + } } } From b4467fcb371fb704e5b69585c5cf5674ff4e6fc5 Mon Sep 17 00:00:00 2001 From: Romazes Date: Tue, 10 Mar 2026 15:44:34 +0200 Subject: [PATCH 3/3] fix: reorder BybitInverseFutures enum entry and fix DYDX --- Common/Brokerages/BrokerageName.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Common/Brokerages/BrokerageName.cs b/Common/Brokerages/BrokerageName.cs index 5c3dd2705629..e31cb11a6ff8 100644 --- a/Common/Brokerages/BrokerageName.cs +++ b/Common/Brokerages/BrokerageName.cs @@ -154,11 +154,6 @@ public enum BrokerageName /// Bybit, - /// - /// Bybit Inverse Futures COIN-Margined contracts are settled and collateralized in their base cryptocurrency. - /// - BybitInverseFutures, - /// /// Transaction and submit/execution rules will use Eze models /// @@ -202,6 +197,11 @@ public enum BrokerageName /// /// Transaction and submit/execution rules will use dYdX models /// - DYDX + DYDX, + + /// + /// Bybit Inverse Futures COIN-Margined contracts are settled and collateralized in their base cryptocurrency. + /// + BybitInverseFutures, } }