Qubx 0.6.38__cp312-cp312-manylinux_2_39_x86_64.whl → 0.6.40__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.
- qubx/cli/deploy.py +27 -3
- qubx/core/metrics.py +1 -1
- qubx/core/series.cpython-312-x86_64-linux-gnu.so +0 -0
- qubx/core/utils.cpython-312-x86_64-linux-gnu.so +0 -0
- qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so +0 -0
- qubx/utils/runner/configs.py +2 -1
- qubx/utils/runner/factory.py +6 -6
- qubx/utils/runner/runner.py +10 -19
- {qubx-0.6.38.dist-info → qubx-0.6.40.dist-info}/METADATA +1 -1
- {qubx-0.6.38.dist-info → qubx-0.6.40.dist-info}/RECORD +13 -13
- {qubx-0.6.38.dist-info → qubx-0.6.40.dist-info}/LICENSE +0 -0
- {qubx-0.6.38.dist-info → qubx-0.6.40.dist-info}/WHEEL +0 -0
- {qubx-0.6.38.dist-info → qubx-0.6.40.dist-info}/entry_points.txt +0 -0
qubx/cli/deploy.py
CHANGED
|
@@ -182,6 +182,29 @@ def setup_poetry_environment(output_dir: str) -> bool:
|
|
|
182
182
|
return False
|
|
183
183
|
|
|
184
184
|
|
|
185
|
+
def create_strategy_runners(output_dir: str):
|
|
186
|
+
"""
|
|
187
|
+
Creates a strategy runner script in the output_dir
|
|
188
|
+
"""
|
|
189
|
+
import sys
|
|
190
|
+
|
|
191
|
+
if sys.platform == "win32":
|
|
192
|
+
_pfx = ""
|
|
193
|
+
_f_name = os.path.join(output_dir, "run_paper.bat")
|
|
194
|
+
else:
|
|
195
|
+
_pfx = "#!/bin/bash\n"
|
|
196
|
+
_f_name = os.path.join(output_dir, "run_paper.sh")
|
|
197
|
+
|
|
198
|
+
logger.info(f"Creating strategy paper runner script: {_f_name}")
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
with open(_f_name, "w") as f:
|
|
202
|
+
f.write(f"{_pfx}poetry run qubx run config.yml --paper -j")
|
|
203
|
+
os.chmod(_f_name, 0o755)
|
|
204
|
+
except Exception as e:
|
|
205
|
+
logger.error(f"Failed to create strategy paper runner script: {e}")
|
|
206
|
+
|
|
207
|
+
|
|
185
208
|
def deploy_strategy(zip_file: str, output_dir: str | None, force: bool) -> bool:
|
|
186
209
|
"""
|
|
187
210
|
Deploys a strategy from a zip file created by the release command.
|
|
@@ -222,9 +245,10 @@ def deploy_strategy(zip_file: str, output_dir: str | None, force: bool) -> bool:
|
|
|
222
245
|
if not setup_poetry_environment(resolved_output_dir):
|
|
223
246
|
return False
|
|
224
247
|
|
|
248
|
+
# Create the strategy runners
|
|
249
|
+
create_strategy_runners(resolved_output_dir)
|
|
250
|
+
|
|
225
251
|
# Success messages
|
|
226
252
|
logger.info(f"Strategy deployed successfully to {resolved_output_dir}")
|
|
227
|
-
logger.info(
|
|
228
|
-
f"To run the strategy (paper mode): <cyan>cd {resolved_output_dir} && poetry run qubx run config.yml --paper</cyan>"
|
|
229
|
-
)
|
|
253
|
+
logger.info(f" -> To run the strategy (in paper mode): <cyan>cd {resolved_output_dir} && ./run_paper.sh</cyan>")
|
|
230
254
|
return True
|
qubx/core/metrics.py
CHANGED
|
@@ -884,7 +884,7 @@ class TradingSessionResult:
|
|
|
884
884
|
_perf = info.pop("performance", None)
|
|
885
885
|
info["instruments"] = info.pop("symbols")
|
|
886
886
|
# - fix for old versions
|
|
887
|
-
_exch = info.pop("exchange")
|
|
887
|
+
_exch = info.pop("exchange") if "exchange" in info else info.pop("exchanges")
|
|
888
888
|
info["exchanges"] = _exch if isinstance(_exch, list) else [_exch]
|
|
889
889
|
tsr = TradingSessionResult(**info, portfolio_log=portfolio, executions_log=executions, signals_log=signals)
|
|
890
890
|
tsr.qubx_version = _qbx_version
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
qubx/utils/runner/configs.py
CHANGED
|
@@ -121,7 +121,8 @@ def load_strategy_config_from_yaml(path: Path | str, key: str | None = None) ->
|
|
|
121
121
|
class StrategySimulationConfig(BaseModel):
|
|
122
122
|
strategy: str | list[str]
|
|
123
123
|
parameters: dict = Field(default_factory=dict)
|
|
124
|
-
data:
|
|
124
|
+
data: list[TypedReaderConfig] = Field(default_factory=list)
|
|
125
|
+
aux: ReaderConfig | None = None
|
|
125
126
|
simulation: dict = Field(default_factory=dict)
|
|
126
127
|
description: str | list[str] | None = None
|
|
127
128
|
variate: dict = Field(default_factory=dict)
|
qubx/utils/runner/factory.py
CHANGED
|
@@ -12,7 +12,7 @@ from qubx.data.composite import CompositeReader
|
|
|
12
12
|
from qubx.data.readers import DataReader
|
|
13
13
|
from qubx.emitters.composite import CompositeMetricEmitter
|
|
14
14
|
from qubx.utils.misc import class_import
|
|
15
|
-
from qubx.utils.runner.configs import EmissionConfig, ExporterConfig, NotifierConfig, ReaderConfig,
|
|
15
|
+
from qubx.utils.runner.configs import EmissionConfig, ExporterConfig, NotifierConfig, ReaderConfig, TypedReaderConfig
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def resolve_env_vars(value: str | Any) -> str | Any:
|
|
@@ -118,27 +118,27 @@ def create_metric_emitters(emission_config: EmissionConfig, strategy_name: str)
|
|
|
118
118
|
return CompositeMetricEmitter(emitters, stats_interval=stats_interval)
|
|
119
119
|
|
|
120
120
|
|
|
121
|
-
def create_data_type_readers(
|
|
121
|
+
def create_data_type_readers(readers_configs: list[TypedReaderConfig] | None) -> dict[str, DataReader]:
|
|
122
122
|
"""
|
|
123
|
-
Create a dictionary mapping data types to readers based on the
|
|
123
|
+
Create a dictionary mapping data types to readers based on the readers list.
|
|
124
124
|
|
|
125
125
|
This function ensures that identical reader configurations are only instantiated once,
|
|
126
126
|
and multiple data types can share the same reader instance if they have identical configurations.
|
|
127
127
|
|
|
128
128
|
Args:
|
|
129
|
-
|
|
129
|
+
readers_configs: The readers list containing reader definitions.
|
|
130
130
|
|
|
131
131
|
Returns:
|
|
132
132
|
A dictionary mapping data types to reader instances.
|
|
133
133
|
"""
|
|
134
|
-
if
|
|
134
|
+
if readers_configs is None:
|
|
135
135
|
return {}
|
|
136
136
|
|
|
137
137
|
# First, create unique readers to avoid duplicate instantiation
|
|
138
138
|
unique_readers = {} # Maps reader config hash to reader instance
|
|
139
139
|
data_type_to_reader = {} # Maps data type to reader instance
|
|
140
140
|
|
|
141
|
-
for typed_reader_config in
|
|
141
|
+
for typed_reader_config in readers_configs:
|
|
142
142
|
data_types = typed_reader_config.data_type
|
|
143
143
|
if isinstance(data_types, str):
|
|
144
144
|
data_types = [data_types]
|
qubx/utils/runner/runner.py
CHANGED
|
@@ -49,7 +49,7 @@ from qubx.loggers import create_logs_writer
|
|
|
49
49
|
from qubx.restarts.state_resolvers import StateResolver
|
|
50
50
|
from qubx.restarts.time_finders import TimeFinder
|
|
51
51
|
from qubx.restorers import create_state_restorer
|
|
52
|
-
from qubx.utils.misc import class_import, makedirs, red
|
|
52
|
+
from qubx.utils.misc import class_import, green, makedirs, red
|
|
53
53
|
from qubx.utils.runner.configs import (
|
|
54
54
|
ExchangeConfig,
|
|
55
55
|
LoggingConfig,
|
|
@@ -533,9 +533,7 @@ def _create_broker(
|
|
|
533
533
|
secret=creds.secret,
|
|
534
534
|
enable_mm=_enable_mm,
|
|
535
535
|
)
|
|
536
|
-
return get_ccxt_broker(
|
|
537
|
-
exchange_name, exchange, channel, time_provider, account, data_provider, **params
|
|
538
|
-
)
|
|
536
|
+
return get_ccxt_broker(exchange_name, exchange, channel, time_provider, account, data_provider, **params)
|
|
539
537
|
case "paper":
|
|
540
538
|
assert isinstance(account, SimulatedAccountProcessor)
|
|
541
539
|
return SimulatedBroker(channel=channel, account=account, simulated_exchange=account._exchange)
|
|
@@ -597,7 +595,7 @@ def _run_warmup(
|
|
|
597
595
|
logger.info(f"<yellow>Warmup start time: {warmup_start_time}</yellow>")
|
|
598
596
|
|
|
599
597
|
# - construct warmup readers
|
|
600
|
-
data_type_to_reader = create_data_type_readers(warmup)
|
|
598
|
+
data_type_to_reader = create_data_type_readers(warmup.readers) if warmup else {}
|
|
601
599
|
|
|
602
600
|
if not data_type_to_reader:
|
|
603
601
|
logger.warning("<yellow>No readers were created for warmup</yellow>")
|
|
@@ -745,10 +743,8 @@ def simulate_strategy(
|
|
|
745
743
|
experiments = {simulation_name: strategy}
|
|
746
744
|
_n_jobs = 1
|
|
747
745
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
for k, v in cfg.data.items():
|
|
751
|
-
data_i[k] = eval(v)
|
|
746
|
+
# - resolve data readers
|
|
747
|
+
data_i = create_data_type_readers(cfg.data) if cfg.data else {}
|
|
752
748
|
|
|
753
749
|
sim_params = cfg.simulation
|
|
754
750
|
for mp in ["instruments", "capital", "commissions", "start", "stop"]:
|
|
@@ -764,18 +760,13 @@ def simulate_strategy(
|
|
|
764
760
|
logger.info(f"Stop date set to {stop}")
|
|
765
761
|
|
|
766
762
|
# - check for aux_data parameter
|
|
767
|
-
if
|
|
768
|
-
aux_data =
|
|
769
|
-
if aux_data is not None:
|
|
770
|
-
try:
|
|
771
|
-
sim_params["aux_data"] = eval(aux_data)
|
|
772
|
-
except Exception as e:
|
|
773
|
-
raise ValueError(f"Invalid aux_data parameter: {aux_data}") from e
|
|
763
|
+
if cfg.aux is not None:
|
|
764
|
+
sim_params["aux_data"] = construct_reader(cfg.aux)
|
|
774
765
|
|
|
775
766
|
# - run simulation
|
|
776
767
|
print(f" > Run simulation for [{red(simulation_name)}] ::: {sim_params['start']} - {sim_params['stop']}")
|
|
777
768
|
sim_params["n_jobs"] = sim_params.get("n_jobs", _n_jobs)
|
|
778
|
-
test_res = simulate(experiments, data=data_i, **sim_params)
|
|
769
|
+
test_res = simulate(experiments, data=data_i, **sim_params) # type: ignore
|
|
779
770
|
|
|
780
771
|
_where_to_save = save_path if save_path is not None else Path("results/")
|
|
781
772
|
s_path = Path(makedirs(str(_where_to_save))) / simulation_name
|
|
@@ -791,13 +782,13 @@ def simulate_strategy(
|
|
|
791
782
|
if len(test_res) > 1:
|
|
792
783
|
# - TODO: think how to deal with variations !
|
|
793
784
|
s_path = s_path / f"variations.{_v_id}"
|
|
794
|
-
print(f" > Saving variations results to
|
|
785
|
+
print(f" > Saving variations results to {green(s_path)} ...")
|
|
795
786
|
for k, t in enumerate(test_res):
|
|
796
787
|
# - set variation name
|
|
797
788
|
t.variation_name = f"{simulation_name}.{_v_id}"
|
|
798
789
|
t.to_file(str(s_path), description=_descr, suffix=f".{k}", attachments=[str(config_file)])
|
|
799
790
|
else:
|
|
800
|
-
print(f" > Saving simulation results to
|
|
791
|
+
print(f" > Saving simulation results to {green(s_path)} ...")
|
|
801
792
|
test_res[0].to_file(str(s_path), description=_descr, attachments=[str(config_file)])
|
|
802
793
|
|
|
803
794
|
return test_res
|
|
@@ -14,7 +14,7 @@ qubx/backtester/simulator.py,sha256=cSbW42X-YlAutZlOQ3Y4mAJWXr_1WomYprtWZVMe3Uk,
|
|
|
14
14
|
qubx/backtester/utils.py,sha256=nHrgKcIkyp5gz8wrPwMp1fRItUtQfvOPjxZhcaCwN-o,32729
|
|
15
15
|
qubx/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
qubx/cli/commands.py,sha256=EwGqbNvY5VRCEO9T1w0GgqtcEvPFYMW96KzC-FPUvDM,7259
|
|
17
|
-
qubx/cli/deploy.py,sha256=
|
|
17
|
+
qubx/cli/deploy.py,sha256=pQ9FPOsywDyy8jOjLfrgYTTkKQ-MCixCzbgsG68Q3_0,8319
|
|
18
18
|
qubx/cli/misc.py,sha256=tP28QxLEzuP8R2xnt8g3JTs9Z7aYy4iVWY4g3VzKTsQ,14777
|
|
19
19
|
qubx/cli/release.py,sha256=JYdNt_ZM9jarmYiRDtKqbRqqllzm2Qwi6VggokB2j8A,28167
|
|
20
20
|
qubx/connectors/ccxt/__init__.py,sha256=HEQ7lM9HS8sED_zfsAHrhFT7F9E7NFGAecwZwNr-TDE,65
|
|
@@ -45,18 +45,18 @@ qubx/core/initializer.py,sha256=PUiD_cIjvGpuPjYyRpUjpwm3xNQ2Kipa8bAhbtxCQRo,3935
|
|
|
45
45
|
qubx/core/interfaces.py,sha256=CzIl8tB6ImQkDcZEmhpstwHPOCY8NhZxXmBHLQUAieI,58253
|
|
46
46
|
qubx/core/loggers.py,sha256=0g33jfipGFShSMrXBoYVzL0GfTzI36mwBJqHNUHmhdo,13342
|
|
47
47
|
qubx/core/lookups.py,sha256=n5ZjjEhhRvmidCB-Cubr1b0Opm6lf_QVZNEWa_BOQG0,19376
|
|
48
|
-
qubx/core/metrics.py,sha256=
|
|
48
|
+
qubx/core/metrics.py,sha256=3zB4XISayK_rF4RkOB3QMMshg8IFq_Z0Xqwcf_Y5bhg,58713
|
|
49
49
|
qubx/core/mixins/__init__.py,sha256=AMCLvfNuIb1kkQl3bhCj9jIOEl2eKcVPJeyLgrkB-rk,329
|
|
50
50
|
qubx/core/mixins/market.py,sha256=lBappEimPhIuI0vmUvwVlIztkYjlEjJBpP-AdpfudII,3948
|
|
51
51
|
qubx/core/mixins/processing.py,sha256=dqehukrfqcLy5BeILKnkpHCvva4SbLKj1ZbQdnByu1k,24552
|
|
52
52
|
qubx/core/mixins/subscription.py,sha256=V_g9wCPQ8S5SHkU-qOZ84cV5nReAUrV7DoSNAGG0LPY,10372
|
|
53
53
|
qubx/core/mixins/trading.py,sha256=idfRPaqrvkfMxzu9mXr9i_xfqLee-ZAOrERxkxv6Ruo,7256
|
|
54
54
|
qubx/core/mixins/universe.py,sha256=L3s2Jw46_J1iDh4622Gk_LvCjol4W7mflBwEHrLfZEw,9899
|
|
55
|
-
qubx/core/series.cpython-312-x86_64-linux-gnu.so,sha256=
|
|
55
|
+
qubx/core/series.cpython-312-x86_64-linux-gnu.so,sha256=vh0iqqRGDBZy1F9LjMMjhF3txG_zcB7J4pu0XPYmDdc,978280
|
|
56
56
|
qubx/core/series.pxd,sha256=jBdMwgO8J4Zrue0e_xQ5RlqTXqihpzQNu6V3ckZvvpY,3978
|
|
57
57
|
qubx/core/series.pyi,sha256=RaHm_oHHiWiNUMJqVfx5FXAXniGLsHxUFOUpacn7GC0,4604
|
|
58
58
|
qubx/core/series.pyx,sha256=7cM3zZThW59waHiYcZmMxvYj-HYD7Ej_l7nKA4emPjE,46477
|
|
59
|
-
qubx/core/utils.cpython-312-x86_64-linux-gnu.so,sha256=
|
|
59
|
+
qubx/core/utils.cpython-312-x86_64-linux-gnu.so,sha256=9Wj1t1JoF9F3ic-tCyzlC2F8m1nF_HOIHIqHdLjywwo,86568
|
|
60
60
|
qubx/core/utils.pyi,sha256=a-wS13V2p_dM1CnGq40JVulmiAhixTwVwt0ah5By0Hc,348
|
|
61
61
|
qubx/core/utils.pyx,sha256=k5QHfEFvqhqWfCob89ANiJDKNG8gGbOh-O4CVoneZ8M,1696
|
|
62
62
|
qubx/data/__init__.py,sha256=ELZykvpPGWc5rX7QoNyNQwMLgdKMG8MACOByA4pM5hA,549
|
|
@@ -122,7 +122,7 @@ qubx/restorers/signal.py,sha256=9TAaJOEKPjZXuciFFVn6Z8a-Z8CfVSjRGFRcwEgbPLY,1074
|
|
|
122
122
|
qubx/restorers/state.py,sha256=dLaVnUwRCNRkUqbYyi0RfZs3Q3AdglkI_qTtQ8GDD5Y,7289
|
|
123
123
|
qubx/restorers/utils.py,sha256=We2gfqwQKWziUYhuUnjb-xo-5tSlbuHWpPQn0CEMTn0,1155
|
|
124
124
|
qubx/ta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
125
|
-
qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so,sha256=
|
|
125
|
+
qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so,sha256=dr8KjRec6PNcjTCGOzlnt-G6e5xCMw0jEoNtP4RbYI4,654440
|
|
126
126
|
qubx/ta/indicators.pxd,sha256=Goo0_N0Xnju8XGo3Xs-3pyg2qr_0Nh5C-_26DK8U_IE,4224
|
|
127
127
|
qubx/ta/indicators.pyi,sha256=19W0uERft49In5bf9jkJHkzJYEyE9gzudN7_DJ5Vdv8,1963
|
|
128
128
|
qubx/ta/indicators.pyx,sha256=Xgpew46ZxSXsdfSEWYn3A0Q35MLsopB9n7iyCsXTufs,25969
|
|
@@ -154,13 +154,13 @@ qubx/utils/questdb.py,sha256=TdjmlGPoZXdjidZ_evcBIkFtoL4nGQXPR4IQSUc6IvA,2509
|
|
|
154
154
|
qubx/utils/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
155
155
|
qubx/utils/runner/_jupyter_runner.pyt,sha256=fDj4AUs25jsdGmY9DDeSFufH1JkVhLFwy0BOmVO7nIU,9609
|
|
156
156
|
qubx/utils/runner/accounts.py,sha256=mpiv6oxr5z97zWt7STYyARMhWQIpc_XFKungb_pX38U,3270
|
|
157
|
-
qubx/utils/runner/configs.py,sha256=
|
|
158
|
-
qubx/utils/runner/factory.py,sha256=
|
|
159
|
-
qubx/utils/runner/runner.py,sha256=
|
|
157
|
+
qubx/utils/runner/configs.py,sha256=nxIelzfHtv7GagkEHBJ6mRm_30jmBa6pSPujL-k0uqo,3749
|
|
158
|
+
qubx/utils/runner/factory.py,sha256=EwtM1mE-w2IwWcWoazNITsPPl9Q_5K9IDtxfQc4BaZA,11694
|
|
159
|
+
qubx/utils/runner/runner.py,sha256=egv4OtPwEIwG0-Ia3ai9E9MYQIaslZLyoT4tL1FxQcY,28891
|
|
160
160
|
qubx/utils/time.py,sha256=J0ZFGjzFL5T6GA8RPAel8hKG0sg2LZXeQ5YfDCfcMHA,10055
|
|
161
161
|
qubx/utils/version.py,sha256=e52fIHyxzCiIuH7svCF6pkHuDlqL64rklqz-2XjWons,5309
|
|
162
|
-
qubx-0.6.
|
|
163
|
-
qubx-0.6.
|
|
164
|
-
qubx-0.6.
|
|
165
|
-
qubx-0.6.
|
|
166
|
-
qubx-0.6.
|
|
162
|
+
qubx-0.6.40.dist-info/LICENSE,sha256=qwMHOSJ2TD0nx6VUJvFhu1ynJdBfNozRMt6tnSul-Ts,35140
|
|
163
|
+
qubx-0.6.40.dist-info/METADATA,sha256=V2Xo_isQ1pWzbVKAKgUI8mTZDok7ZCy4UxuF3QuS_nI,4492
|
|
164
|
+
qubx-0.6.40.dist-info/WHEEL,sha256=XjdW4AGUgFDhpG9b3b2KPhtR_JLZvHyfemLgJJwcqOI,110
|
|
165
|
+
qubx-0.6.40.dist-info/entry_points.txt,sha256=VqilDTe8mVuV9SbR-yVlZJBTjbkHIL2JBgXfQw076HY,47
|
|
166
|
+
qubx-0.6.40.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|