Qubx 0.6.54__tar.gz → 0.6.56__tar.gz
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.
- {qubx-0.6.54 → qubx-0.6.56}/PKG-INFO +1 -1
- {qubx-0.6.54 → qubx-0.6.56}/pyproject.toml +1 -1
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/runner.py +1 -1
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/basics.py +111 -5
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/helpers.py +18 -7
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/lookups.py +226 -304
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/mixins/processing.py +23 -3
- qubx-0.6.56/src/qubx/resources/crypto-fees.ini +98 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-binance-spot.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-binance.cm-future.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-binance.um-future.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-hyperliquid-spot.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-hyperliquid.f-perpetual.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-kraken-spot.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-kraken.f-future.json +1 -0
- qubx-0.6.56/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +1 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restorers/factory.py +3 -3
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/marketdata/ccxt.py +51 -6
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/marketdata/dukas.py +1 -1
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/runner/runner.py +1 -1
- qubx-0.6.54/src/qubx/resources/instruments/symbols-binance.cm.json +0 -1
- qubx-0.6.54/src/qubx/resources/instruments/symbols-binance.json +0 -1
- qubx-0.6.54/src/qubx/resources/instruments/symbols-binance.um.json +0 -1
- qubx-0.6.54/src/qubx/resources/instruments/symbols-bitfinex.f.json +0 -1
- qubx-0.6.54/src/qubx/resources/instruments/symbols-bitfinex.json +0 -1
- qubx-0.6.54/src/qubx/resources/instruments/symbols-kraken.f.json +0 -1
- qubx-0.6.54/src/qubx/resources/instruments/symbols-kraken.json +0 -1
- {qubx-0.6.54 → qubx-0.6.56}/LICENSE +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/README.md +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/build.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/_nb_magic.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/account.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/broker.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/data.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/management.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/ome.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/optimization.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/simulator.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/backtester/utils.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/cli/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/cli/commands.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/cli/deploy.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/cli/misc.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/cli/release.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/cli/tui.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/account.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/broker.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/data.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/reader.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/ccxt/utils.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/tardis/data.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/account.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/context.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/deque.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/errors.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/exceptions.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/initializer.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/interfaces.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/loggers.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/metrics.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/mixins/market.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/mixins/subscription.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/mixins/trading.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/mixins/universe.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/series.pxd +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/series.pyi +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/series.pyx +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/utils.pyi +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/core/utils.pyx +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/data/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/data/composite.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/data/helpers.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/data/hft.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/data/readers.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/data/registry.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/data/tardis.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/emitters/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/emitters/base.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/emitters/composite.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/emitters/csv.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/emitters/questdb.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/exporters/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/exporters/composite.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/exporters/formatters/base.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/exporters/formatters/incremental.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/exporters/slack.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/features/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/features/core.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/features/orderbook.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/features/price.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/features/trades.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/features/utils.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/gathering/simplest.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/health/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/health/base.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/loggers/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/loggers/csv.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/loggers/factory.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/loggers/mongo.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/math/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/math/stats.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/notifications/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/notifications/composite.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/notifications/slack.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/notifications/throttler.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/pandaz/ta.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/pandaz/utils.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/resources/_build.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restarts/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restarts/state_resolvers.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restorers/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restorers/balance.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restorers/position.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restorers/signal.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restorers/state.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/restorers/utils.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/ta/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/trackers/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/trackers/advanced.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/trackers/composite.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/trackers/sizers.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/collections.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/misc.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/ntp.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/orderbook.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/questdb.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/runner/configs.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/runner/factory.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/time.py +0 -0
- {qubx-0.6.54 → qubx-0.6.56}/src/qubx/utils/version.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "Qubx"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.56"
|
|
8
8
|
description = "Qubx - Quantitative Trading Framework"
|
|
9
9
|
authors = [ "Dmitry Marienko <dmitry.marienko@xlydian.com>", "Yuriy Arabskyy <yuriy.arabskyy@xlydian.com>",]
|
|
10
10
|
readme = "README.md"
|
|
@@ -439,7 +439,7 @@ class SimulationRunner:
|
|
|
439
439
|
if isinstance(commissions, (str, type(None))):
|
|
440
440
|
commissions = {e: commissions for e in exchanges}
|
|
441
441
|
for exchange in exchanges:
|
|
442
|
-
_exchange_to_tcc[exchange] = lookup.
|
|
442
|
+
_exchange_to_tcc[exchange] = lookup.find_fees(exchange.lower(), commissions.get(exchange))
|
|
443
443
|
return _exchange_to_tcc
|
|
444
444
|
|
|
445
445
|
def _construct_account_processor(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from dataclasses import dataclass, field
|
|
2
3
|
from datetime import datetime
|
|
3
4
|
from enum import StrEnum
|
|
@@ -176,6 +177,14 @@ class MarketType(StrEnum):
|
|
|
176
177
|
|
|
177
178
|
@dataclass
|
|
178
179
|
class Instrument:
|
|
180
|
+
"""
|
|
181
|
+
Instrument class.
|
|
182
|
+
|
|
183
|
+
- 2025-06-11: Important change for FUTURE type: now instrument's symbol contains delivery date in format YYYYMMDD.
|
|
184
|
+
So now for let's say september's BTCUSDT future, symbol would be BTCUSD.20250914
|
|
185
|
+
and full id is `BINANCE.UM:FUTURE:BTCUSD.20250914`
|
|
186
|
+
"""
|
|
187
|
+
|
|
179
188
|
symbol: str
|
|
180
189
|
asset_type: AssetType
|
|
181
190
|
market_type: MarketType
|
|
@@ -192,8 +201,10 @@ class Instrument:
|
|
|
192
201
|
maint_margin: float = 0.0 # maintenance margin
|
|
193
202
|
liquidation_fee: float = 0.0 # liquidation fee
|
|
194
203
|
contract_size: float = 1.0 # contract size
|
|
195
|
-
onboard_date: datetime | None = None
|
|
196
|
-
delivery_date: datetime | None = None
|
|
204
|
+
onboard_date: datetime | None = None # date when instrument was listed on the exchange
|
|
205
|
+
delivery_date: datetime | None = None # date when instrument is delivered
|
|
206
|
+
delist_date: datetime | None = None # date when instrument is delisted
|
|
207
|
+
inverse: bool = False # if true, then the future is inverse
|
|
197
208
|
|
|
198
209
|
@property
|
|
199
210
|
def price_precision(self):
|
|
@@ -437,9 +448,6 @@ class AssetBalance:
|
|
|
437
448
|
return self
|
|
438
449
|
|
|
439
450
|
|
|
440
|
-
MARKET_TYPE = Literal["SPOT", "MARGIN", "SWAP", "FUTURES", "OPTION"]
|
|
441
|
-
|
|
442
|
-
|
|
443
451
|
class Position:
|
|
444
452
|
instrument: Instrument # instrument for this position
|
|
445
453
|
quantity: float = 0.0 # quantity positive for long and negative for short
|
|
@@ -878,3 +886,101 @@ class RestoredState:
|
|
|
878
886
|
balances: dict[str, AssetBalance]
|
|
879
887
|
instrument_to_target_positions: dict[Instrument, list[TargetPosition]]
|
|
880
888
|
positions: dict[Instrument, Position]
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
class InstrumentsLookup:
|
|
892
|
+
def get_lookup(self) -> dict[str, Instrument]: ...
|
|
893
|
+
|
|
894
|
+
def find(
|
|
895
|
+
self,
|
|
896
|
+
exchange: str,
|
|
897
|
+
base: str,
|
|
898
|
+
quote: str,
|
|
899
|
+
settle: str | None = None,
|
|
900
|
+
market_type: MarketType | None = None,
|
|
901
|
+
) -> Instrument | None:
|
|
902
|
+
for i in self.get_lookup().values():
|
|
903
|
+
if (
|
|
904
|
+
i.exchange == exchange
|
|
905
|
+
and ((i.base == base and i.quote == quote) or (i.base == quote and i.quote == base))
|
|
906
|
+
and (market_type is None or i.market_type == market_type)
|
|
907
|
+
):
|
|
908
|
+
if settle is not None and i.settle is not None:
|
|
909
|
+
if i.settle == settle:
|
|
910
|
+
return i
|
|
911
|
+
else:
|
|
912
|
+
return i
|
|
913
|
+
return None
|
|
914
|
+
|
|
915
|
+
def find_symbol(self, exchange: str, symbol: str, market_type: MarketType | None = None) -> Instrument | None:
|
|
916
|
+
for i in self.get_lookup().values():
|
|
917
|
+
if (
|
|
918
|
+
(i.exchange == exchange)
|
|
919
|
+
and (i.symbol == symbol)
|
|
920
|
+
and (market_type is None or i.market_type == market_type)
|
|
921
|
+
):
|
|
922
|
+
return i
|
|
923
|
+
|
|
924
|
+
return None
|
|
925
|
+
|
|
926
|
+
def find_instruments(
|
|
927
|
+
self,
|
|
928
|
+
exchange: str,
|
|
929
|
+
quote: str | None = None,
|
|
930
|
+
market_type: MarketType | None = None,
|
|
931
|
+
as_of: str | pd.Timestamp | None = None,
|
|
932
|
+
) -> list[Instrument]:
|
|
933
|
+
"""
|
|
934
|
+
Find instruments by exchange, quote, market type and as of date.
|
|
935
|
+
If as_of is not None, then only instruments that are not delisted after as_of date will be returned.
|
|
936
|
+
- exchange: str - exchange name
|
|
937
|
+
- quote: str | None - quote currency
|
|
938
|
+
- market_type: MarketType | None - market type
|
|
939
|
+
- as_of is a string in format YYYY-MM-DD or pd.Timestamp or None
|
|
940
|
+
"""
|
|
941
|
+
_limit_time = pd.Timestamp(as_of) if as_of else None
|
|
942
|
+
return [
|
|
943
|
+
i
|
|
944
|
+
for i in self.get_lookup().values()
|
|
945
|
+
if i.exchange == exchange
|
|
946
|
+
and (quote is None or i.quote == quote)
|
|
947
|
+
and (market_type is None or i.market_type == market_type)
|
|
948
|
+
and (
|
|
949
|
+
_limit_time is None
|
|
950
|
+
or (i.delist_date is None)
|
|
951
|
+
or (pd.Timestamp(i.delist_date).tz_localize(None) >= _limit_time)
|
|
952
|
+
)
|
|
953
|
+
]
|
|
954
|
+
|
|
955
|
+
def find_aux_instrument_for(
|
|
956
|
+
self, instrument: Instrument, base_currency: str, market_type: MarketType | None = None
|
|
957
|
+
) -> Instrument | None:
|
|
958
|
+
"""
|
|
959
|
+
Tries to find aux instrument (for conversions to funded currency)
|
|
960
|
+
for example:
|
|
961
|
+
ETHBTC -> BTCUSDT for base_currency USDT
|
|
962
|
+
EURGBP -> GBPUSD for base_currency USD
|
|
963
|
+
...
|
|
964
|
+
"""
|
|
965
|
+
if market_type is None:
|
|
966
|
+
market_type = instrument.market_type
|
|
967
|
+
base_currency = base_currency.upper()
|
|
968
|
+
if instrument.quote != base_currency:
|
|
969
|
+
return self.find(instrument.exchange, instrument.quote, base_currency, market_type=market_type)
|
|
970
|
+
return None
|
|
971
|
+
|
|
972
|
+
def __getitem__(self, spath: str) -> list[Instrument]:
|
|
973
|
+
"""
|
|
974
|
+
Helper method for finding instruments by pattern.
|
|
975
|
+
It's convenient to use in research mode.
|
|
976
|
+
"""
|
|
977
|
+
res = []
|
|
978
|
+
c = re.compile(spath)
|
|
979
|
+
for k, v in self.get_lookup().items():
|
|
980
|
+
if re.match(c, k):
|
|
981
|
+
res.append(v)
|
|
982
|
+
return res
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
class FeesLookup:
|
|
986
|
+
def find_fees(self, exchange: str, spec: str | None) -> TransactionCostsCalculator: ...
|
|
@@ -6,15 +6,15 @@ import time
|
|
|
6
6
|
from collections import defaultdict, deque
|
|
7
7
|
from inspect import isbuiltin, isclass, isfunction, ismethod, ismethoddescriptor
|
|
8
8
|
from threading import Thread
|
|
9
|
-
from typing import Any, Callable
|
|
9
|
+
from typing import Any, Callable
|
|
10
10
|
|
|
11
11
|
import numpy as np
|
|
12
12
|
import pandas as pd
|
|
13
13
|
from croniter import croniter
|
|
14
14
|
|
|
15
15
|
from qubx import logger
|
|
16
|
-
from qubx.core.basics import SW, CtrlChannel, DataType, Instrument, Timestamped
|
|
17
|
-
from qubx.core.series import OHLCV, Bar, OrderBook, Quote, Trade
|
|
16
|
+
from qubx.core.basics import SW, CtrlChannel, DataType, Instrument, Timestamped, dt_64
|
|
17
|
+
from qubx.core.series import OHLCV, Bar, OrderBook, Quote, Trade, time_as_nsec
|
|
18
18
|
from qubx.utils.time import convert_seconds_to_str, convert_tf_str_td64, interval_to_cron
|
|
19
19
|
|
|
20
20
|
|
|
@@ -26,9 +26,9 @@ class CachedMarketDataHolder:
|
|
|
26
26
|
default_timeframe: np.timedelta64
|
|
27
27
|
_last_bar: dict[Instrument, Bar | None]
|
|
28
28
|
_ohlcvs: dict[Instrument, dict[np.timedelta64, OHLCV]]
|
|
29
|
-
_updates: dict[Instrument,
|
|
29
|
+
_updates: dict[Instrument, Bar | Quote | Trade]
|
|
30
30
|
|
|
31
|
-
_instr_to_sub_to_buffer:
|
|
31
|
+
_instr_to_sub_to_buffer: dict[Instrument, dict[str, deque]]
|
|
32
32
|
|
|
33
33
|
def __init__(self, default_timeframe: str | None = None, max_buffer_size: int = 10_000) -> None:
|
|
34
34
|
self._ohlcvs = dict()
|
|
@@ -93,7 +93,7 @@ class CachedMarketDataHolder:
|
|
|
93
93
|
|
|
94
94
|
return self._ohlcvs[instrument][tf]
|
|
95
95
|
|
|
96
|
-
def get_data(self, instrument: Instrument, event_type: str) ->
|
|
96
|
+
def get_data(self, instrument: Instrument, event_type: str) -> list[Any]:
|
|
97
97
|
return list(self._instr_to_sub_to_buffer[instrument][event_type])
|
|
98
98
|
|
|
99
99
|
def update(
|
|
@@ -126,7 +126,7 @@ class CachedMarketDataHolder:
|
|
|
126
126
|
pass
|
|
127
127
|
|
|
128
128
|
@SW.watch("CachedMarketDataHolder")
|
|
129
|
-
def update_by_bars(self, instrument: Instrument, timeframe: str | np.timedelta64, bars:
|
|
129
|
+
def update_by_bars(self, instrument: Instrument, timeframe: str | np.timedelta64, bars: list[Bar]) -> OHLCV:
|
|
130
130
|
"""
|
|
131
131
|
Update or create OHLCV series with the provided historical bars.
|
|
132
132
|
|
|
@@ -203,6 +203,17 @@ class CachedMarketDataHolder:
|
|
|
203
203
|
continue
|
|
204
204
|
ser.update(trade.time, trade.price, total_vol, bought_vol)
|
|
205
205
|
|
|
206
|
+
def finalize_all_ohlc(self, time: dt_64):
|
|
207
|
+
"""
|
|
208
|
+
Finalize all OHLCV series at the given time.
|
|
209
|
+
FIXME: (2025-06-17) This is part of urgent live fix and must be removed in future !!!.
|
|
210
|
+
"""
|
|
211
|
+
for instrument in self._ohlcvs.keys():
|
|
212
|
+
# - use most recent update
|
|
213
|
+
if (_u := self._updates.get(instrument)) is not None:
|
|
214
|
+
_px = extract_price(_u)
|
|
215
|
+
self.update_by_bar(instrument, Bar(time_as_nsec(time), _px, _px, _px, _px, 0, 0))
|
|
216
|
+
|
|
206
217
|
|
|
207
218
|
SPEC_REGEX = re.compile(
|
|
208
219
|
r"((?P<type>[A-Za-z]+)(\.?(?P<timeframe>[0-9A-Za-z]+))?\ *:)?"
|