Skip to content

Commit 0bfd8f3

Browse files
committed
Add support for inverse futures
1 parent a55a80b commit 0bfd8f3

8 files changed

+290
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using NUnit.Framework;
18+
19+
namespace QuantConnect.BybitBrokerage.Tests
20+
{
21+
[TestFixture, Explicit("Requires valid credentials to be setup and run outside USA")]
22+
public class BybitInverseFuturesBrokerageHistoryProviderTests : BybitBrokerageHistoryProviderTests
23+
{
24+
private static readonly Symbol ETHUSD = Symbol.Create("ETHUSDT", SecurityType.CryptoFuture, Market.Bybit);
25+
26+
27+
private static TestCaseData[] ValidHistory
28+
{
29+
get
30+
{
31+
return new[]
32+
{
33+
// valid
34+
new TestCaseData(ETHUSD, Resolution.Tick, Time.OneMinute, TickType.Trade, false),
35+
new TestCaseData(ETHUSD, Resolution.Minute, Time.OneHour, TickType.Trade, false),
36+
new TestCaseData(ETHUSD, Resolution.Hour, Time.OneDay, TickType.Trade, false),
37+
new TestCaseData(ETHUSD, Resolution.Daily, TimeSpan.FromDays(15), TickType.Trade, false),
38+
new TestCaseData(ETHUSD, Resolution.Hour, Time.OneDay, TickType.OpenInterest, false)
39+
};
40+
}
41+
}
42+
43+
44+
[Test, TestCaseSource(nameof(ValidHistory))]
45+
public override void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, TickType tickType,
46+
bool throwsException)
47+
{
48+
base.GetsHistory(symbol, resolution, period, tickType, throwsException);
49+
}
50+
51+
[Ignore("Same as base")]
52+
public override void GetEmptyHistory(Symbol symbol, Resolution resolution, TimeSpan period, TickType tickType,
53+
bool throwsException)
54+
{
55+
base.GetEmptyHistory(symbol, resolution, period, tickType, throwsException);
56+
}
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using NUnit.Framework;
17+
18+
namespace QuantConnect.BybitBrokerage.Tests
19+
{
20+
[TestFixture]
21+
public partial class BybitInverseFuturesBrokerageTests
22+
{
23+
private static TestCaseData[] TestParameters
24+
{
25+
get
26+
{
27+
return new[]
28+
{
29+
// valid parameters, for example
30+
new TestCaseData(BTCUSD, Resolution.Tick, false),
31+
new TestCaseData(BTCUSD, Resolution.Minute, true),
32+
new TestCaseData(BTCUSD, Resolution.Second, true),
33+
};
34+
}
35+
}
36+
37+
[Test, TestCaseSource(nameof(TestParameters))]
38+
public override void StreamsData(Symbol symbol, Resolution resolution, bool throwsException)
39+
{
40+
base.StreamsData(symbol, resolution, throwsException);
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using NUnit.Framework;
17+
using QuantConnect.BybitBrokerage.Models.Enums;
18+
using QuantConnect.Tests.Brokerages;
19+
20+
namespace QuantConnect.BybitBrokerage.Tests;
21+
22+
[TestFixture, Explicit("Requires valid credentials to be setup and run outside USA")]
23+
public partial class BybitInverseFuturesBrokerageTests : BybitBrokerageTests
24+
{
25+
private static Symbol BTCUSD = Symbol.Create("BTCUSD", SecurityType.CryptoFuture, "bybit");
26+
protected override Symbol Symbol { get; } = BTCUSD;
27+
28+
protected override SecurityType SecurityType => SecurityType.Future;
29+
protected override BybitProductCategory Category => BybitProductCategory.Inverse;
30+
protected override decimal TakerFee => 0.00025m;
31+
32+
protected override decimal GetDefaultQuantity() => 10m;
33+
34+
35+
/// <summary>
36+
/// Provides the data required to test each order type in various cases
37+
/// </summary>
38+
private static TestCaseData[] OrderParameters()
39+
{
40+
return new[]
41+
{
42+
new TestCaseData(new MarketOrderTestParameters(BTCUSD)).SetName("MarketOrder"),
43+
new TestCaseData(new LimitOrderTestParameters(BTCUSD, 50000m, 10000m)).SetName("LimitOrder"),
44+
new TestCaseData(new StopMarketOrderTestParameters(BTCUSD, 50000m, 10000m)).SetName("StopMarketOrder"),
45+
new TestCaseData(new StopLimitOrderTestParameters(BTCUSD, 50000m, 10000m)).SetName("StopLimitOrder"),
46+
new TestCaseData(new LimitIfTouchedOrderTestParameters(BTCUSD, 50000m, 20000)).SetName(
47+
"LimitIfTouchedOrder")
48+
};
49+
}
50+
51+
52+
[Test, TestCaseSource(nameof(OrderParameters))]
53+
public override void CancelOrders(OrderTestParameters parameters)
54+
{
55+
base.CancelOrders(parameters);
56+
}
57+
58+
[Test, TestCaseSource(nameof(OrderParameters))]
59+
public override void LongFromZero(OrderTestParameters parameters)
60+
{
61+
base.LongFromZero(parameters);
62+
}
63+
64+
[Test, TestCaseSource(nameof(OrderParameters))]
65+
public override void CloseFromLong(OrderTestParameters parameters)
66+
{
67+
base.CloseFromLong(parameters);
68+
}
69+
70+
[Test, TestCaseSource(nameof(OrderParameters))]
71+
public override void ShortFromZero(OrderTestParameters parameters)
72+
{
73+
base.ShortFromZero(parameters);
74+
}
75+
76+
[Test, TestCaseSource(nameof(OrderParameters))]
77+
public override void CloseFromShort(OrderTestParameters parameters)
78+
{
79+
base.CloseFromShort(parameters);
80+
}
81+
82+
[Test, TestCaseSource(nameof(OrderParameters))]
83+
public override void ShortFromLong(OrderTestParameters parameters)
84+
{
85+
base.ShortFromLong(parameters);
86+
}
87+
88+
[Test, TestCaseSource(nameof(OrderParameters))]
89+
public override void LongFromShort(OrderTestParameters parameters)
90+
{
91+
base.LongFromShort(parameters);
92+
}
93+
94+
[Ignore("Base")]
95+
public override void GetAccountHoldings()
96+
{
97+
base.GetAccountHoldings();
98+
}
99+
}

Diff for: QuantConnect.BybitBrokerage/Api/BybitPositionApiEndpoint.cs

+26-3
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,33 @@ public IEnumerable<BybitPositionInfo> GetPositions(BybitProductCategory category
4949
{
5050
if (category == BybitProductCategory.Spot) return Array.Empty<BybitPositionInfo>();
5151

52-
var parameters = new KeyValuePair<string, string>[]
52+
var parameters = new List<KeyValuePair<string, string>>();
53+
54+
if (category == BybitProductCategory.Linear)
5355
{
54-
new("settleCoin", "USDT")
55-
};
56+
parameters.Add(KeyValuePair.Create("settleCoin", "USDT"));
57+
}
58+
5659
return FetchAll<BybitPositionInfo>("/position/list", category, 200, parameters, true);
5760
}
61+
62+
/// <summary>
63+
/// It supports to switch the position mode for USDT perpetual and Inverse futures.
64+
/// If you are in one-way Mode, you can only open one position on Buy or Sell side. If you are in hedge mode, you can open both Buy and Sell side positions simultaneously.
65+
/// </summary>
66+
/// <param name="category">The product category</param>
67+
/// <param name="symbol">The symbol for which the mode should be changed</param>
68+
/// <param name="mode">The mode which should be set</param>
69+
public void SwitchPositionMode(BybitProductCategory category, Symbol symbol, PositionMode mode)
70+
{
71+
var ticker = SymbolMapper.GetBrokerageSymbol(symbol);
72+
var requestBody = new
73+
{
74+
category,
75+
mode = (int)mode,
76+
symbol = ticker
77+
};
78+
79+
ExecutePostRequest<ByBitResponse>("/position/switch-mode", requestBody);
80+
}
5881
}

Diff for: QuantConnect.BybitBrokerage/BybitBrokerage.Messaging.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ private void HandleOrderExecution(JToken message)
147147
var currency = tradeUpdate.Category switch
148148
{
149149
BybitProductCategory.Linear => "USDT",
150-
BybitProductCategory.Inverse => GetBaseCurrency(symbol),
150+
BybitProductCategory.Inverse => GetBaseCurrency(leanSymbol),
151151
BybitProductCategory.Spot => GetSpotFeeCurrency(leanSymbol, tradeUpdate),
152152
_ => throw new NotSupportedException($"category {tradeUpdate.Category} not implemented")
153153
};
@@ -178,7 +178,7 @@ static string GetSpotFeeCurrency(Symbol symbol, BybitTradeUpdate tradeUpdate)
178178
return tradeUpdate.Side == OrderSide.Buy ? quote : @base;
179179
}
180180

181-
static string GetBaseCurrency(string pair)
181+
static string GetBaseCurrency(Symbol pair)
182182
{
183183
CurrencyPairUtil.DecomposeCurrencyPair(pair, out var baseCurrency, out _);
184184
return baseCurrency;

Diff for: QuantConnect.BybitBrokerage/BybitBrokerage.cs

+13-8
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ namespace QuantConnect.BybitBrokerage;
4848
[BrokerageFactory(typeof(BybitBrokerageFactory))]
4949
public partial class BybitBrokerage : BaseWebsocketsBrokerage, IDataQueueHandler
5050
{
51-
private static readonly List<BybitProductCategory> SupportedBybitProductCategories = new() { BybitProductCategory.Spot, BybitProductCategory.Linear };
51+
private static readonly List<BybitProductCategory> SupportedBybitProductCategories = new() { BybitProductCategory.Spot, BybitProductCategory.Linear, BybitProductCategory.Inverse };
5252

5353
private static readonly List<SecurityType> SuppotedSecurityTypes = new() { SecurityType.Crypto, SecurityType.CryptoFuture };
5454

@@ -307,8 +307,8 @@ protected virtual bool CanSubscribe(Symbol symbol)
307307

308308
if (baseCanSubscribe && symbol.SecurityType == SecurityType.CryptoFuture)
309309
{
310-
//Can only subscribe to non-inverse pairs
311-
return CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out _, out var quoteCurrency) && quoteCurrency == "USDT";
310+
//Can only subscribe to non-future pairs
311+
return CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out _, out var quoteCurrency) && quoteCurrency is "USDT" or "USD";
312312
}
313313

314314
return baseCanSubscribe;
@@ -483,13 +483,18 @@ private static BybitProductCategory GetBybitProductCategory(Symbol symbol)
483483
return BybitProductCategory.Spot;
484484

485485
case SecurityType.CryptoFuture:
486-
if (!CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out _, out var quoteCurrency) ||
487-
quoteCurrency != "USDT")
486+
if (CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out _, out var quoteCurrency))
488487
{
489-
throw new ArgumentException($"Invalid symbol: {symbol}. Only linear futures are supported.");
488+
if (quoteCurrency == "USDT")
489+
{
490+
return BybitProductCategory.Linear;
491+
}
492+
if (quoteCurrency == "USD")
493+
{
494+
return BybitProductCategory.Inverse;
495+
}
490496
}
491-
492-
return BybitProductCategory.Linear;
497+
throw new ArgumentException($"Invalid symbol: {symbol}. Only linear futures are supported.");
493498

494499
default:
495500
throw new ArgumentOutOfRangeException(nameof(symbol), symbol, "Not supported security type");

Diff for: QuantConnect.BybitBrokerage/Models/ByBitResponse.cs

+16-8
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ namespace QuantConnect.BybitBrokerage.Models
2222
/// <summary>
2323
/// Bybits default http response message
2424
/// </summary>
25-
/// <typeparam name="T"></typeparam>
26-
public class ByBitResponse<T>
25+
public class ByBitResponse
2726
{
2827
/// <summary>
2928
/// Success/Error code
@@ -43,16 +42,25 @@ public class ByBitResponse<T>
4342
/// </summary>
4443
[JsonProperty("retExtInfo")]
4544
public object ExtendedInfo { get; set; }
46-
47-
/// <summary>
48-
/// Business data result
49-
/// </summary>
50-
public T Result { get; set; }
51-
45+
5246
/// <summary>
5347
/// Current time
5448
/// </summary>
5549
[JsonConverter(typeof(BybitTimeConverter))]
5650
public DateTime Time { get; set; }
5751
}
52+
53+
/// <summary>
54+
/// Bybits default http data response message
55+
/// </summary>
56+
/// <typeparam name="T"></typeparam>
57+
public class ByBitResponse<T> : ByBitResponse
58+
{
59+
/// <summary>
60+
/// Business data result
61+
/// </summary>
62+
public T Result { get; set; }
63+
64+
}
65+
5866
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Runtime.Serialization;
17+
18+
namespace QuantConnect.BybitBrokerage.Models.Enums;
19+
20+
/// <summary>
21+
/// Bybit position mode
22+
/// </summary>
23+
public enum PositionMode
24+
{
25+
/// <summary>
26+
/// One way mode
27+
/// </summary>
28+
MergedSingle = 0,
29+
/// <summary>
30+
/// Hedge mode
31+
/// </summary>
32+
BothSides = 3,
33+
}

0 commit comments

Comments
 (0)