Qubx 0.6.48__tar.gz → 0.6.49__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.48 → qubx-0.6.49}/PKG-INFO +1 -1
- {qubx-0.6.48 → qubx-0.6.49}/pyproject.toml +1 -1
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/mixins/universe.py +13 -20
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/data/readers.py +103 -17
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/runner/runner.py +5 -1
- {qubx-0.6.48 → qubx-0.6.49}/LICENSE +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/README.md +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/build.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/_nb_magic.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/account.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/broker.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/data.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/management.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/ome.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/optimization.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/runner.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/simulator.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/backtester/utils.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/cli/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/cli/commands.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/cli/deploy.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/cli/misc.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/cli/release.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/cli/tui.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/account.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/broker.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/data.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/reader.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/ccxt/utils.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/tardis/data.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/account.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/basics.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/context.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/deque.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/errors.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/exceptions.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/helpers.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/initializer.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/interfaces.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/loggers.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/lookups.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/metrics.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/mixins/market.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/mixins/processing.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/mixins/subscription.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/mixins/trading.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/series.pxd +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/series.pyi +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/series.pyx +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/utils.pyi +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/core/utils.pyx +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/data/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/data/composite.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/data/helpers.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/data/hft.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/data/registry.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/data/tardis.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/emitters/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/emitters/base.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/emitters/composite.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/emitters/csv.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/emitters/questdb.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/exporters/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/exporters/composite.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/exporters/formatters/base.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/exporters/formatters/incremental.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/exporters/slack.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/features/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/features/core.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/features/orderbook.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/features/price.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/features/trades.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/features/utils.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/gathering/simplest.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/health/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/health/base.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/loggers/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/loggers/csv.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/loggers/factory.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/loggers/mongo.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/math/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/math/stats.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/notifications/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/notifications/composite.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/notifications/slack.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/notifications/throttler.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/pandaz/ta.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/pandaz/utils.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/resources/_build.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/resources/instruments/symbols-binance.cm.json +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/resources/instruments/symbols-binance.json +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/resources/instruments/symbols-binance.um.json +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/resources/instruments/symbols-bitfinex.f.json +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/resources/instruments/symbols-bitfinex.json +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/resources/instruments/symbols-kraken.f.json +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/resources/instruments/symbols-kraken.json +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restarts/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restarts/state_resolvers.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restorers/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restorers/balance.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restorers/factory.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restorers/position.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restorers/signal.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restorers/state.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/restorers/utils.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/ta/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/trackers/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/trackers/advanced.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/trackers/composite.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/trackers/sizers.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/collections.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/misc.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/ntp.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/orderbook.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/questdb.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/runner/configs.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/runner/factory.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/src/qubx/utils/time.py +0 -0
- {qubx-0.6.48 → qubx-0.6.49}/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.49"
|
|
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"
|
|
@@ -2,8 +2,6 @@ from qubx.core.basics import DataType, Instrument, TargetPosition
|
|
|
2
2
|
from qubx.core.helpers import CachedMarketDataHolder
|
|
3
3
|
from qubx.core.interfaces import (
|
|
4
4
|
IAccountProcessor,
|
|
5
|
-
IBroker,
|
|
6
|
-
IDataProvider,
|
|
7
5
|
IPositionGathering,
|
|
8
6
|
IStrategy,
|
|
9
7
|
IStrategyContext,
|
|
@@ -14,7 +12,6 @@ from qubx.core.interfaces import (
|
|
|
14
12
|
RemovalPolicy,
|
|
15
13
|
)
|
|
16
14
|
from qubx.core.loggers import StrategyLogging
|
|
17
|
-
from qubx.core.lookups import lookup
|
|
18
15
|
|
|
19
16
|
|
|
20
17
|
class UniverseManager(IUniverseManager):
|
|
@@ -50,7 +47,7 @@ class UniverseManager(IUniverseManager):
|
|
|
50
47
|
self._time_provider = time_provider
|
|
51
48
|
self._account = account
|
|
52
49
|
self._position_gathering = position_gathering
|
|
53
|
-
self._instruments =
|
|
50
|
+
self._instruments = set()
|
|
54
51
|
self._removal_queue = {}
|
|
55
52
|
|
|
56
53
|
def _has_position(self, instrument: Instrument) -> bool:
|
|
@@ -72,7 +69,7 @@ class UniverseManager(IUniverseManager):
|
|
|
72
69
|
), "Invalid if_has_position_then policy"
|
|
73
70
|
|
|
74
71
|
new_set = set(instruments)
|
|
75
|
-
prev_set =
|
|
72
|
+
prev_set = self._instruments.copy()
|
|
76
73
|
|
|
77
74
|
# - determine instruments to remove depending on if_has_position_then policy
|
|
78
75
|
may_be_removed = list(prev_set - new_set)
|
|
@@ -93,9 +90,7 @@ class UniverseManager(IUniverseManager):
|
|
|
93
90
|
self._subscription_manager.commit() # apply pending changes
|
|
94
91
|
|
|
95
92
|
# set new instruments
|
|
96
|
-
self._instruments
|
|
97
|
-
self._instruments.extend(instruments)
|
|
98
|
-
self._instruments.extend(to_keep)
|
|
93
|
+
self._instruments = new_set | set(to_keep)
|
|
99
94
|
|
|
100
95
|
def _get_what_can_be_removed_or_kept(
|
|
101
96
|
self, may_be_removed: list[Instrument], skip_callback: bool, if_has_position_then: RemovalPolicy
|
|
@@ -105,12 +100,11 @@ class UniverseManager(IUniverseManager):
|
|
|
105
100
|
for instr in may_be_removed:
|
|
106
101
|
if immediately_close:
|
|
107
102
|
to_remove.append(instr)
|
|
103
|
+
elif self._has_position(instr):
|
|
104
|
+
self._removal_queue[instr] = (if_has_position_then, skip_callback)
|
|
105
|
+
to_keep.append(instr)
|
|
108
106
|
else:
|
|
109
|
-
|
|
110
|
-
self._removal_queue[instr] = (if_has_position_then, skip_callback)
|
|
111
|
-
to_keep.append(instr)
|
|
112
|
-
else:
|
|
113
|
-
to_remove.append(instr)
|
|
107
|
+
to_remove.append(instr)
|
|
114
108
|
return to_remove, to_keep
|
|
115
109
|
|
|
116
110
|
def __cleanup_removal_queue(self, instruments: list[Instrument]):
|
|
@@ -124,7 +118,7 @@ class UniverseManager(IUniverseManager):
|
|
|
124
118
|
self.__cleanup_removal_queue(instruments)
|
|
125
119
|
self._strategy.on_universe_change(self._context, instruments, [])
|
|
126
120
|
self._subscription_manager.commit()
|
|
127
|
-
self._instruments.
|
|
121
|
+
self._instruments.update(instruments)
|
|
128
122
|
|
|
129
123
|
def remove_instruments(
|
|
130
124
|
self,
|
|
@@ -146,12 +140,11 @@ class UniverseManager(IUniverseManager):
|
|
|
146
140
|
self._subscription_manager.commit()
|
|
147
141
|
|
|
148
142
|
# - update instruments list
|
|
149
|
-
self._instruments =
|
|
150
|
-
self._instruments.extend(to_keep)
|
|
143
|
+
self._instruments = (self._instruments - set(to_remove)) | set(to_keep)
|
|
151
144
|
|
|
152
145
|
@property
|
|
153
146
|
def instruments(self) -> list[Instrument]:
|
|
154
|
-
return self._instruments
|
|
147
|
+
return list(self._instruments)
|
|
155
148
|
|
|
156
149
|
def __do_remove_instruments(self, instruments: list[Instrument]):
|
|
157
150
|
"""
|
|
@@ -227,7 +220,7 @@ class UniverseManager(IUniverseManager):
|
|
|
227
220
|
# if aux is not None:
|
|
228
221
|
# instrument._aux_instrument = aux
|
|
229
222
|
# instruments.append(aux)
|
|
230
|
-
# _ = self.
|
|
223
|
+
# _ = self._account.get_position(aux)
|
|
231
224
|
|
|
232
225
|
def on_alter_position(self, instrument: Instrument) -> None:
|
|
233
226
|
"""
|
|
@@ -247,7 +240,7 @@ class UniverseManager(IUniverseManager):
|
|
|
247
240
|
|
|
248
241
|
# - commit changes and remove instrument from the universe
|
|
249
242
|
self._subscription_manager.commit()
|
|
250
|
-
self._instruments.
|
|
243
|
+
self._instruments.discard(instrument)
|
|
251
244
|
|
|
252
245
|
def is_trading_allowed(self, instrument: Instrument) -> bool:
|
|
253
246
|
if instrument in self._removal_queue:
|
|
@@ -261,7 +254,7 @@ class UniverseManager(IUniverseManager):
|
|
|
261
254
|
|
|
262
255
|
# - commit changes and remove instrument from the universe
|
|
263
256
|
self._subscription_manager.commit()
|
|
264
|
-
self._instruments.
|
|
257
|
+
self._instruments.discard(instrument)
|
|
265
258
|
return False
|
|
266
259
|
|
|
267
260
|
return True
|
|
@@ -1054,7 +1054,7 @@ def _retry(fn):
|
|
|
1054
1054
|
# print(x, cls._reconnect_tries)
|
|
1055
1055
|
try:
|
|
1056
1056
|
return fn(*args, **kw)
|
|
1057
|
-
except (pg.InterfaceError, pg.OperationalError, AttributeError)
|
|
1057
|
+
except (pg.InterfaceError, pg.OperationalError, AttributeError):
|
|
1058
1058
|
logger.debug("Database Connection [InterfaceError or OperationalError]")
|
|
1059
1059
|
# print ("Idle for %s seconds" % (cls._reconnect_idle))
|
|
1060
1060
|
# time.sleep(cls._reconnect_idle)
|
|
@@ -1063,6 +1063,75 @@ def _retry(fn):
|
|
|
1063
1063
|
return wrapper
|
|
1064
1064
|
|
|
1065
1065
|
|
|
1066
|
+
def _calculate_max_candles_chunksize(timeframe: str | None) -> int:
|
|
1067
|
+
"""
|
|
1068
|
+
Calculate maximum chunksize for candles data based on timeframe.
|
|
1069
|
+
Limits to at most 1 week of data for candles_1m data type.
|
|
1070
|
+
|
|
1071
|
+
Args:
|
|
1072
|
+
timeframe: Timeframe string (e.g., "1m", "5m", "1h", "1d")
|
|
1073
|
+
|
|
1074
|
+
Returns:
|
|
1075
|
+
Maximum number of candles representing 1 week of data
|
|
1076
|
+
"""
|
|
1077
|
+
if not timeframe:
|
|
1078
|
+
return 10080 # Default to 1 week of 1-minute candles
|
|
1079
|
+
|
|
1080
|
+
try:
|
|
1081
|
+
# Convert timeframe to pandas Timedelta
|
|
1082
|
+
tf_delta = pd.Timedelta(timeframe)
|
|
1083
|
+
|
|
1084
|
+
# Calculate how many candles fit in 1 week
|
|
1085
|
+
one_week = pd.Timedelta("7d")
|
|
1086
|
+
max_candles = int(one_week / tf_delta)
|
|
1087
|
+
|
|
1088
|
+
# Ensure we don't return 0 or negative values
|
|
1089
|
+
return max(1, max_candles)
|
|
1090
|
+
except (ValueError, TypeError):
|
|
1091
|
+
# If timeframe can't be parsed, default to 1 week of 1-minute candles
|
|
1092
|
+
return 10080
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def _calculate_time_windows_for_chunking(
|
|
1096
|
+
start: str | None, end: str | None, timeframe: str, chunksize: int
|
|
1097
|
+
) -> list[tuple[pd.Timestamp, pd.Timestamp]]:
|
|
1098
|
+
"""
|
|
1099
|
+
Calculate time windows for efficient chunking based on timeframe and chunksize.
|
|
1100
|
+
|
|
1101
|
+
Args:
|
|
1102
|
+
start: Start time string
|
|
1103
|
+
end: End time string
|
|
1104
|
+
timeframe: Timeframe string (e.g., "1m", "5m", "1h")
|
|
1105
|
+
chunksize: Number of candles per chunk
|
|
1106
|
+
|
|
1107
|
+
Returns:
|
|
1108
|
+
List of (start_time, end_time) tuples for each chunk
|
|
1109
|
+
"""
|
|
1110
|
+
if not start or not end:
|
|
1111
|
+
return []
|
|
1112
|
+
|
|
1113
|
+
start_dt = pd.Timestamp(start)
|
|
1114
|
+
end_dt = pd.Timestamp(end)
|
|
1115
|
+
|
|
1116
|
+
try:
|
|
1117
|
+
# Calculate time period per chunk based on timeframe and chunksize
|
|
1118
|
+
tf_delta = pd.Timedelta(timeframe)
|
|
1119
|
+
chunk_duration = tf_delta * chunksize
|
|
1120
|
+
|
|
1121
|
+
windows = []
|
|
1122
|
+
current_start = start_dt
|
|
1123
|
+
|
|
1124
|
+
while current_start < end_dt:
|
|
1125
|
+
current_end = min(current_start + chunk_duration, end_dt)
|
|
1126
|
+
windows.append((current_start, current_end))
|
|
1127
|
+
current_start = current_end
|
|
1128
|
+
|
|
1129
|
+
return windows
|
|
1130
|
+
except (ValueError, TypeError):
|
|
1131
|
+
# If timeframe can't be parsed, fall back to single window
|
|
1132
|
+
return [(start_dt, end_dt)]
|
|
1133
|
+
|
|
1134
|
+
|
|
1066
1135
|
class QuestDBSqlBuilder:
|
|
1067
1136
|
"""
|
|
1068
1137
|
Generic sql builder for QuestDB data
|
|
@@ -1387,28 +1456,46 @@ class QuestDBConnector(DataReader):
|
|
|
1387
1456
|
data_type: str,
|
|
1388
1457
|
builder: QuestDBSqlBuilder,
|
|
1389
1458
|
) -> Any:
|
|
1390
|
-
|
|
1391
|
-
|
|
1459
|
+
# Apply maximum chunksize limits for candles_1m data type when timeframe is provided
|
|
1460
|
+
if chunksize > 0 and data_type == "candles_1m" and timeframe:
|
|
1461
|
+
max_chunksize = _calculate_max_candles_chunksize(timeframe)
|
|
1462
|
+
chunksize = min(chunksize, max_chunksize)
|
|
1392
1463
|
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1464
|
+
start, end = handle_start_stop(start, stop)
|
|
1465
|
+
# If timeframe is not specified, assume 1 minute as default
|
|
1466
|
+
effective_timeframe = timeframe or "1m"
|
|
1396
1467
|
|
|
1397
1468
|
if chunksize > 0:
|
|
1469
|
+
# Use efficient chunking with multiple smaller queries
|
|
1470
|
+
def _iter_efficient_chunks():
|
|
1471
|
+
time_windows = _calculate_time_windows_for_chunking(start, end, effective_timeframe, chunksize)
|
|
1472
|
+
_cursor = self._connection.cursor() # type: ignore
|
|
1398
1473
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
break
|
|
1405
|
-
transform.start_transform(data_id, names, start=start, stop=stop)
|
|
1406
|
-
transform.process_data(records)
|
|
1407
|
-
yield transform.collect()
|
|
1474
|
+
try:
|
|
1475
|
+
for window_start, window_end in time_windows:
|
|
1476
|
+
_req = builder.prepare_data_sql(
|
|
1477
|
+
data_id, str(window_start), str(window_end), effective_timeframe, data_type
|
|
1478
|
+
)
|
|
1408
1479
|
|
|
1409
|
-
|
|
1480
|
+
_cursor.execute(_req) # type: ignore
|
|
1481
|
+
names = [d.name for d in _cursor.description] # type: ignore
|
|
1482
|
+
records = _cursor.fetchall()
|
|
1483
|
+
|
|
1484
|
+
if records:
|
|
1485
|
+
transform.start_transform(data_id, names, start=start, stop=stop)
|
|
1486
|
+
transform.process_data(records)
|
|
1487
|
+
yield transform.collect()
|
|
1488
|
+
finally:
|
|
1489
|
+
_cursor.close()
|
|
1410
1490
|
|
|
1491
|
+
return _iter_efficient_chunks()
|
|
1492
|
+
|
|
1493
|
+
# No chunking requested - return all data at once
|
|
1494
|
+
_req = builder.prepare_data_sql(data_id, start, end, effective_timeframe, data_type)
|
|
1495
|
+
_cursor = self._connection.cursor() # type: ignore
|
|
1411
1496
|
try:
|
|
1497
|
+
_cursor.execute(_req) # type: ignore
|
|
1498
|
+
names = [d.name for d in _cursor.description] # type: ignore
|
|
1412
1499
|
records = _cursor.fetchall()
|
|
1413
1500
|
if not records:
|
|
1414
1501
|
return None
|
|
@@ -1481,7 +1568,6 @@ class QuestDBSqlOrderBookBuilder(QuestDBSqlCandlesBuilder):
|
|
|
1481
1568
|
if not start or not end:
|
|
1482
1569
|
raise ValueError("Start and end dates must be provided for orderbook data!")
|
|
1483
1570
|
start_dt, end_dt = pd.Timestamp(start), pd.Timestamp(end)
|
|
1484
|
-
delta = end_dt - start_dt
|
|
1485
1571
|
|
|
1486
1572
|
raw_start_dt = start_dt.floor(self.SNAPSHOT_DELTA) - self.MIN_DELTA
|
|
1487
1573
|
|
|
@@ -681,11 +681,15 @@ def _run_warmup(
|
|
|
681
681
|
# - create a restored state based on warmup runner context
|
|
682
682
|
warmup_account = warmup_runner.ctx.account
|
|
683
683
|
|
|
684
|
+
# - get the instruments from the warmup runner context
|
|
685
|
+
_instruments = warmup_runner.ctx.instruments
|
|
684
686
|
_positions = warmup_account.get_positions()
|
|
687
|
+
_positions = {k: v for k, v in _positions.items() if k in _instruments}
|
|
685
688
|
_orders = warmup_account.get_orders()
|
|
686
689
|
instrument_to_orders = defaultdict(list)
|
|
687
690
|
for o in _orders.values():
|
|
688
|
-
|
|
691
|
+
if o.instrument in _instruments:
|
|
692
|
+
instrument_to_orders[o.instrument].append(o)
|
|
689
693
|
|
|
690
694
|
# - set the warmup positions and orders
|
|
691
695
|
ctx.set_warmup_positions(_positions)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|