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 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
@@ -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: dict = Field(default_factory=dict)
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)
@@ -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, WarmupConfig
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(warmup: WarmupConfig | None) -> dict[str, DataReader]:
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 warmup configuration.
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
- warmup: The warmup configuration containing reader definitions.
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 warmup is None:
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 warmup.readers:
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]
@@ -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
- data_i = {}
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 "aux_data" in sim_params:
768
- aux_data = sim_params.pop("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 <g>{s_path}</g> ...")
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 <g>{s_path}</g> ...")
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: Qubx
3
- Version: 0.6.38
3
+ Version: 0.6.40
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  Author: Dmitry Marienko
6
6
  Author-email: dmitry.marienko@xlydian.com
@@ -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=WfrEJ4n_e_xB6e1JstP2Rb4EGIxCftqG_flT03fjPXE,7595
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=Gq3Ultwn5meICfyauBUJrBS4nffSxFVH3OF6N1Y0xgo,58664
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=oCjBv31hRr3bJ47EucFuzDzwStG05dUWTNyAXBg2HwQ,978280
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=jpJmqz2ebCWD6_Wb8-IkGvGLhhgT2Gk0f2EmL8bkUC8,86568
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=EAm2KZhw7wkpqvoEQRH9a3Z2enYqYQkk5EdVVOPuB6s,654440
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=4lonQgksh4wDygsN67lIidVRIUksskWuhL25A2IZwho,3694
158
- qubx/utils/runner/factory.py,sha256=vQ2dBTbrQE9YH__-TvuFzGF-E1li-vt_qQum9GHa11g,11666
159
- qubx/utils/runner/runner.py,sha256=dyrwFiVmU3nkgGiDRIg6cxhikf3KJ4ylEdojUdf9WaQ,29070
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.38.dist-info/LICENSE,sha256=qwMHOSJ2TD0nx6VUJvFhu1ynJdBfNozRMt6tnSul-Ts,35140
163
- qubx-0.6.38.dist-info/METADATA,sha256=_71dUs78KDAbSy2GWSHBg_oTYYZnugqV8e8oACi9t-4,4492
164
- qubx-0.6.38.dist-info/WHEEL,sha256=XjdW4AGUgFDhpG9b3b2KPhtR_JLZvHyfemLgJJwcqOI,110
165
- qubx-0.6.38.dist-info/entry_points.txt,sha256=VqilDTe8mVuV9SbR-yVlZJBTjbkHIL2JBgXfQw076HY,47
166
- qubx-0.6.38.dist-info/RECORD,,
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