Qubx 0.5.7__cp312-cp312-manylinux_2_39_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Qubx might be problematic. Click here for more details.

Files changed (100) hide show
  1. qubx/__init__.py +207 -0
  2. qubx/_nb_magic.py +100 -0
  3. qubx/backtester/__init__.py +5 -0
  4. qubx/backtester/account.py +145 -0
  5. qubx/backtester/broker.py +87 -0
  6. qubx/backtester/data.py +296 -0
  7. qubx/backtester/management.py +378 -0
  8. qubx/backtester/ome.py +296 -0
  9. qubx/backtester/optimization.py +201 -0
  10. qubx/backtester/simulated_data.py +558 -0
  11. qubx/backtester/simulator.py +362 -0
  12. qubx/backtester/utils.py +780 -0
  13. qubx/cli/__init__.py +0 -0
  14. qubx/cli/commands.py +67 -0
  15. qubx/connectors/ccxt/__init__.py +0 -0
  16. qubx/connectors/ccxt/account.py +495 -0
  17. qubx/connectors/ccxt/broker.py +132 -0
  18. qubx/connectors/ccxt/customizations.py +193 -0
  19. qubx/connectors/ccxt/data.py +612 -0
  20. qubx/connectors/ccxt/exceptions.py +17 -0
  21. qubx/connectors/ccxt/factory.py +93 -0
  22. qubx/connectors/ccxt/utils.py +307 -0
  23. qubx/core/__init__.py +0 -0
  24. qubx/core/account.py +251 -0
  25. qubx/core/basics.py +850 -0
  26. qubx/core/context.py +420 -0
  27. qubx/core/exceptions.py +38 -0
  28. qubx/core/helpers.py +480 -0
  29. qubx/core/interfaces.py +1150 -0
  30. qubx/core/loggers.py +514 -0
  31. qubx/core/lookups.py +475 -0
  32. qubx/core/metrics.py +1512 -0
  33. qubx/core/mixins/__init__.py +13 -0
  34. qubx/core/mixins/market.py +94 -0
  35. qubx/core/mixins/processing.py +428 -0
  36. qubx/core/mixins/subscription.py +203 -0
  37. qubx/core/mixins/trading.py +88 -0
  38. qubx/core/mixins/universe.py +270 -0
  39. qubx/core/series.cpython-312-x86_64-linux-gnu.so +0 -0
  40. qubx/core/series.pxd +125 -0
  41. qubx/core/series.pyi +118 -0
  42. qubx/core/series.pyx +988 -0
  43. qubx/core/utils.cpython-312-x86_64-linux-gnu.so +0 -0
  44. qubx/core/utils.pyi +6 -0
  45. qubx/core/utils.pyx +62 -0
  46. qubx/data/__init__.py +25 -0
  47. qubx/data/helpers.py +416 -0
  48. qubx/data/readers.py +1562 -0
  49. qubx/data/tardis.py +100 -0
  50. qubx/gathering/simplest.py +88 -0
  51. qubx/math/__init__.py +3 -0
  52. qubx/math/stats.py +129 -0
  53. qubx/pandaz/__init__.py +23 -0
  54. qubx/pandaz/ta.py +2757 -0
  55. qubx/pandaz/utils.py +638 -0
  56. qubx/resources/instruments/symbols-binance.cm.json +1 -0
  57. qubx/resources/instruments/symbols-binance.json +1 -0
  58. qubx/resources/instruments/symbols-binance.um.json +1 -0
  59. qubx/resources/instruments/symbols-bitfinex.f.json +1 -0
  60. qubx/resources/instruments/symbols-bitfinex.json +1 -0
  61. qubx/resources/instruments/symbols-kraken.f.json +1 -0
  62. qubx/resources/instruments/symbols-kraken.json +1 -0
  63. qubx/ta/__init__.py +0 -0
  64. qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so +0 -0
  65. qubx/ta/indicators.pxd +149 -0
  66. qubx/ta/indicators.pyi +41 -0
  67. qubx/ta/indicators.pyx +787 -0
  68. qubx/trackers/__init__.py +3 -0
  69. qubx/trackers/abvanced.py +236 -0
  70. qubx/trackers/composite.py +146 -0
  71. qubx/trackers/rebalancers.py +129 -0
  72. qubx/trackers/riskctrl.py +641 -0
  73. qubx/trackers/sizers.py +235 -0
  74. qubx/utils/__init__.py +5 -0
  75. qubx/utils/_pyxreloader.py +281 -0
  76. qubx/utils/charting/lookinglass.py +1057 -0
  77. qubx/utils/charting/mpl_helpers.py +1183 -0
  78. qubx/utils/marketdata/binance.py +284 -0
  79. qubx/utils/marketdata/ccxt.py +90 -0
  80. qubx/utils/marketdata/dukas.py +130 -0
  81. qubx/utils/misc.py +541 -0
  82. qubx/utils/ntp.py +63 -0
  83. qubx/utils/numbers_utils.py +7 -0
  84. qubx/utils/orderbook.py +491 -0
  85. qubx/utils/plotting/__init__.py +0 -0
  86. qubx/utils/plotting/dashboard.py +150 -0
  87. qubx/utils/plotting/data.py +137 -0
  88. qubx/utils/plotting/interfaces.py +25 -0
  89. qubx/utils/plotting/renderers/__init__.py +0 -0
  90. qubx/utils/plotting/renderers/plotly.py +0 -0
  91. qubx/utils/runner/__init__.py +1 -0
  92. qubx/utils/runner/_jupyter_runner.pyt +60 -0
  93. qubx/utils/runner/accounts.py +88 -0
  94. qubx/utils/runner/configs.py +65 -0
  95. qubx/utils/runner/runner.py +470 -0
  96. qubx/utils/time.py +312 -0
  97. qubx-0.5.7.dist-info/METADATA +105 -0
  98. qubx-0.5.7.dist-info/RECORD +100 -0
  99. qubx-0.5.7.dist-info/WHEEL +4 -0
  100. qubx-0.5.7.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,193 @@
1
+ from typing import Dict, List
2
+
3
+ import ccxt.pro as cxp
4
+ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheByTimestamp
5
+ from ccxt.async_support.base.ws.client import Client
6
+
7
+
8
+ class BinanceQV(cxp.binance):
9
+ """
10
+ Extended binance exchange to provide quote asset volumes support
11
+ """
12
+
13
+ def describe(self):
14
+ """
15
+ Overriding watchTrades to use aggTrade instead of trade.
16
+ """
17
+ return self.deep_extend(
18
+ super().describe(),
19
+ {
20
+ "options": {
21
+ "watchTrades": {
22
+ "name": "aggTrade",
23
+ }
24
+ }
25
+ },
26
+ )
27
+
28
+ def parse_ohlcv(self, ohlcv, market=None):
29
+ """
30
+ [
31
+ 1499040000000, // Kline open time 0
32
+ "0.01634790", // Open price 1
33
+ "0.80000000", // High price 2
34
+ "0.01575800", // Low price 3
35
+ "0.01577100", // Close price 4
36
+ "148976.11427815", // Volume 5
37
+ 1499644799999, // Kline Close time 6
38
+ "2434.19055334", // Quote asset volume 7
39
+ 308, // Number of trades 8
40
+ "1756.87402397", // Taker buy base asset volume 9
41
+ "28.46694368", // Taker buy quote asset volume 10
42
+ "0" // Unused field, ignore.
43
+ ]
44
+ """
45
+ return [
46
+ self.safe_integer(ohlcv, 0),
47
+ self.safe_number(ohlcv, 1),
48
+ self.safe_number(ohlcv, 2),
49
+ self.safe_number(ohlcv, 3),
50
+ self.safe_number(ohlcv, 4),
51
+ self.safe_number(ohlcv, 5),
52
+ self.safe_number(ohlcv, 7), # Quote asset volume
53
+ self.safe_number(ohlcv, 10), # Taker buy quote asset volume
54
+ ]
55
+
56
+ def handle_ohlcv(self, client: Client, message):
57
+ event = self.safe_string(message, "e")
58
+ eventMap: dict = {
59
+ "indexPrice_kline": "indexPriceKline",
60
+ "markPrice_kline": "markPriceKline",
61
+ }
62
+ event = self.safe_string(eventMap, event, event)
63
+ kline = self.safe_value(message, "k")
64
+ marketId = self.safe_string_2(kline, "s", "ps")
65
+ if event == "indexPriceKline":
66
+ # indexPriceKline doesn't have the _PERP suffix
67
+ marketId = self.safe_string(message, "ps")
68
+ interval = self.safe_string(kline, "i")
69
+ # use a reverse lookup in a static map instead
70
+ unifiedTimeframe = self.find_timeframe(interval)
71
+ parsed = [
72
+ self.safe_integer(kline, "t"),
73
+ self.safe_float(kline, "o"),
74
+ self.safe_float(kline, "h"),
75
+ self.safe_float(kline, "l"),
76
+ self.safe_float(kline, "c"),
77
+ self.safe_float(kline, "v"),
78
+ # - additional fields
79
+ self.safe_float(kline, "q"), # - quote asset volume
80
+ self.safe_float(kline, "Q"), # - taker buy quote asset volume
81
+ # self.safe_integer(message, "E")
82
+ ]
83
+ isSpot = (client.url.find("/stream") > -1) or (client.url.find("/testnet.binance") > -1)
84
+ marketType = "spot" if (isSpot) else "contract"
85
+ symbol = self.safe_symbol(marketId, None, None, marketType)
86
+ messageHash = "ohlcv::" + symbol + "::" + unifiedTimeframe
87
+ self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
88
+ stored = self.safe_value(self.ohlcvs[symbol], unifiedTimeframe)
89
+ if stored is None:
90
+ limit = self.safe_integer(self.options, "OHLCVLimit", 1000)
91
+ stored = ArrayCacheByTimestamp(limit)
92
+ self.ohlcvs[symbol][unifiedTimeframe] = stored
93
+ stored.append(parsed)
94
+ resolveData = [symbol, unifiedTimeframe, stored]
95
+ client.resolve(resolveData, messageHash)
96
+
97
+ def handle_trade(self, client: Client, message):
98
+ """
99
+ There is a custom trade handler implementation, because Binance sends
100
+ some trades marked with "X" field, which is "MARKET" for market trades
101
+ and "INSURANCE_FUND" for insurance fund trades. We are interested only
102
+ in market trades, so we filter the rest out.
103
+
104
+ Update 07072024: Apparently insurance fund trades not aggregated so
105
+ we don't need to filter via "X" field, but let's keep it just in case.
106
+ """
107
+ # the trade streams push raw trade information in real-time
108
+ # each trade has a unique buyer and seller
109
+ isSpot = (client.url.find("wss://stream.binance.com") > -1) or (client.url.find("/testnet.binance") > -1)
110
+ marketType = "spot" if (isSpot) else "contract"
111
+ marketId = self.safe_string(message, "s")
112
+ market = self.safe_market(marketId, None, None, marketType)
113
+ symbol = market["symbol"]
114
+ messageHash = "trade::" + symbol
115
+ executionType = self.safe_string(message, "X")
116
+ if executionType == "INSURANCE_FUND":
117
+ return
118
+ trade = self.parse_ws_trade(message, market)
119
+ tradesArray = self.safe_value(self.trades, symbol)
120
+ if tradesArray is None:
121
+ limit = self.safe_integer(self.options, "tradesLimit", 1000)
122
+ tradesArray = ArrayCache(limit)
123
+ tradesArray.append(trade)
124
+ self.trades[symbol] = tradesArray
125
+ client.resolve(tradesArray, messageHash)
126
+
127
+
128
+ class BinanceQVUSDM(cxp.binanceusdm, BinanceQV):
129
+ """
130
+ The order of inheritance is important here, because we want
131
+ binanceusdm to take precedence over binanceqv. And this is how MRO is defined
132
+ in Python.
133
+
134
+ Describe method needs to be overriden, because of the way super is called in binanceusdm.
135
+ """
136
+
137
+ _funding_intervals: Dict[str, str]
138
+
139
+ def __init__(self, *args, **kwargs):
140
+ super().__init__(*args, **kwargs)
141
+ self._funding_intervals = {}
142
+
143
+ def describe(self):
144
+ """
145
+ Overriding watchTrades to use aggTrade instead of trade.
146
+ """
147
+ return self.deep_extend(
148
+ super().describe(),
149
+ {
150
+ "options": {
151
+ "watchTrades": {
152
+ "name": "aggTrade",
153
+ }
154
+ }
155
+ },
156
+ )
157
+
158
+ async def watch_funding_rates(self, symbols: List[str] | None = None):
159
+ await self.load_markets()
160
+ await self._update_funding_intervals()
161
+ mark_prices = await self.watch_mark_prices(symbols)
162
+ funding_rates = {}
163
+ for symbol, info in mark_prices.items():
164
+ interval = self._funding_intervals.get(symbol, "8h")
165
+ funding_rates[symbol] = {
166
+ "timestamp": info["timestamp"],
167
+ "interval": interval,
168
+ "fundingRate": float(info["info"]["r"]),
169
+ "nextFundingTime": info["info"]["T"],
170
+ "markPrice": info["markPrice"],
171
+ "indexPrice": info["indexPrice"],
172
+ }
173
+ return funding_rates
174
+
175
+ async def _update_funding_intervals(self):
176
+ if self._funding_intervals:
177
+ return
178
+ symbol_to_info = await self.fetch_funding_intervals()
179
+ self._funding_intervals = {str(s): str(info["interval"]) for s, info in symbol_to_info.items()}
180
+
181
+
182
+ class BinancePortfolioMargin(BinanceQVUSDM):
183
+ def describe(self):
184
+ return self.deep_extend(
185
+ super().describe(),
186
+ {
187
+ "options": {
188
+ "defaultType": "margin",
189
+ "portfolioMargin": True,
190
+ "defaultSubType": None,
191
+ }
192
+ },
193
+ )