openbacktesting 0.2.0__py3-none-any.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.
Files changed (350) hide show
  1. openbacktesting/__init__.py +85 -0
  2. openbacktesting/aggregation.py +8 -0
  3. openbacktesting/broker/__init__.py +54 -0
  4. openbacktesting/broker/commission.py +57 -0
  5. openbacktesting/broker/core.py +1122 -0
  6. openbacktesting/broker/execution.py +62 -0
  7. openbacktesting/broker/instrument.py +106 -0
  8. openbacktesting/broker/ledger.py +60 -0
  9. openbacktesting/broker/models.py +236 -0
  10. openbacktesting/broker/observations.py +99 -0
  11. openbacktesting/broker/requests.py +192 -0
  12. openbacktesting/broker/sizing.py +76 -0
  13. openbacktesting/colors.py +175 -0
  14. openbacktesting/data/__init__.py +20 -0
  15. openbacktesting/data/aggregation.py +39 -0
  16. openbacktesting/data/context.py +30 -0
  17. openbacktesting/data/core.py +218 -0
  18. openbacktesting/data/generators.py +54 -0
  19. openbacktesting/data/strategy_data.py +103 -0
  20. openbacktesting/data/timeframe.py +69 -0
  21. openbacktesting/engine.py +34 -0
  22. openbacktesting/helpers.py +77 -0
  23. openbacktesting/indicator_api.py +10 -0
  24. openbacktesting/indicator_methods.py +123 -0
  25. openbacktesting/indicator_types.py +19 -0
  26. openbacktesting/indicators/__init__.py +7 -0
  27. openbacktesting/indicators/api.py +97 -0
  28. openbacktesting/indicators/calculus.py +75 -0
  29. openbacktesting/indicators/candles/__init__.py +0 -0
  30. openbacktesting/indicators/candles/cdl_doji.py +17 -0
  31. openbacktesting/indicators/candles/cdl_engulfing.py +14 -0
  32. openbacktesting/indicators/candles/cdl_eveningstar.py +15 -0
  33. openbacktesting/indicators/candles/cdl_hammer.py +14 -0
  34. openbacktesting/indicators/candles/cdl_harami.py +14 -0
  35. openbacktesting/indicators/candles/cdl_inside.py +14 -0
  36. openbacktesting/indicators/candles/cdl_marubozu.py +14 -0
  37. openbacktesting/indicators/candles/cdl_morningstar.py +15 -0
  38. openbacktesting/indicators/candles/cdl_shootingstar.py +14 -0
  39. openbacktesting/indicators/candles/cdl_z.py +28 -0
  40. openbacktesting/indicators/candles/ha.py +19 -0
  41. openbacktesting/indicators/catalog.json +8348 -0
  42. openbacktesting/indicators/common.py +94 -0
  43. openbacktesting/indicators/cycles/__init__.py +0 -0
  44. openbacktesting/indicators/cycles/dsp.py +10 -0
  45. openbacktesting/indicators/cycles/ebsw.py +10 -0
  46. openbacktesting/indicators/cycles/ht_dcperiod.py +10 -0
  47. openbacktesting/indicators/cycles/ht_dcphase.py +10 -0
  48. openbacktesting/indicators/cycles/ht_phasor.py +17 -0
  49. openbacktesting/indicators/cycles/ht_sine.py +17 -0
  50. openbacktesting/indicators/cycles/ht_trendmode.py +10 -0
  51. openbacktesting/indicators/cycles/msw.py +19 -0
  52. openbacktesting/indicators/methods.py +5762 -0
  53. openbacktesting/indicators/momentum/__init__.py +0 -0
  54. openbacktesting/indicators/momentum/ac.py +9 -0
  55. openbacktesting/indicators/momentum/ao.py +10 -0
  56. openbacktesting/indicators/momentum/apo.py +10 -0
  57. openbacktesting/indicators/momentum/bias.py +10 -0
  58. openbacktesting/indicators/momentum/bop.py +10 -0
  59. openbacktesting/indicators/momentum/brar.py +26 -0
  60. openbacktesting/indicators/momentum/cci.py +10 -0
  61. openbacktesting/indicators/momentum/cfo.py +10 -0
  62. openbacktesting/indicators/momentum/cg.py +10 -0
  63. openbacktesting/indicators/momentum/cmo.py +10 -0
  64. openbacktesting/indicators/momentum/coppock.py +10 -0
  65. openbacktesting/indicators/momentum/crsi.py +28 -0
  66. openbacktesting/indicators/momentum/cti.py +10 -0
  67. openbacktesting/indicators/momentum/dm.py +17 -0
  68. openbacktesting/indicators/momentum/er.py +10 -0
  69. openbacktesting/indicators/momentum/eri.py +17 -0
  70. openbacktesting/indicators/momentum/fisher.py +17 -0
  71. openbacktesting/indicators/momentum/fosc.py +10 -0
  72. openbacktesting/indicators/momentum/gator.py +13 -0
  73. openbacktesting/indicators/momentum/ibs.py +7 -0
  74. openbacktesting/indicators/momentum/inertia.py +22 -0
  75. openbacktesting/indicators/momentum/kdj.py +18 -0
  76. openbacktesting/indicators/momentum/kst.py +30 -0
  77. openbacktesting/indicators/momentum/lrsi.py +10 -0
  78. openbacktesting/indicators/momentum/macd.py +27 -0
  79. openbacktesting/indicators/momentum/macdext.py +27 -0
  80. openbacktesting/indicators/momentum/macdfix.py +18 -0
  81. openbacktesting/indicators/momentum/mom.py +10 -0
  82. openbacktesting/indicators/momentum/pgo.py +10 -0
  83. openbacktesting/indicators/momentum/pmo.py +13 -0
  84. openbacktesting/indicators/momentum/po.py +10 -0
  85. openbacktesting/indicators/momentum/ppo.py +20 -0
  86. openbacktesting/indicators/momentum/psl.py +12 -0
  87. openbacktesting/indicators/momentum/pvo.py +20 -0
  88. openbacktesting/indicators/momentum/qqe.py +31 -0
  89. openbacktesting/indicators/momentum/rci.py +20 -0
  90. openbacktesting/indicators/momentum/rmi.py +7 -0
  91. openbacktesting/indicators/momentum/roc.py +10 -0
  92. openbacktesting/indicators/momentum/rocp.py +10 -0
  93. openbacktesting/indicators/momentum/rocr.py +10 -0
  94. openbacktesting/indicators/momentum/rocr100.py +10 -0
  95. openbacktesting/indicators/momentum/rsi.py +10 -0
  96. openbacktesting/indicators/momentum/rsx.py +10 -0
  97. openbacktesting/indicators/momentum/rvgi.py +26 -0
  98. openbacktesting/indicators/momentum/slope.py +17 -0
  99. openbacktesting/indicators/momentum/smc_sweep.py +18 -0
  100. openbacktesting/indicators/momentum/smi.py +21 -0
  101. openbacktesting/indicators/momentum/smi_stochastic.py +15 -0
  102. openbacktesting/indicators/momentum/squeeze.py +47 -0
  103. openbacktesting/indicators/momentum/squeeze_pro.py +53 -0
  104. openbacktesting/indicators/momentum/stc.py +20 -0
  105. openbacktesting/indicators/momentum/stoch.py +19 -0
  106. openbacktesting/indicators/momentum/stochf.py +19 -0
  107. openbacktesting/indicators/momentum/stochrsi.py +19 -0
  108. openbacktesting/indicators/momentum/td_seq.py +17 -0
  109. openbacktesting/indicators/momentum/trix.py +19 -0
  110. openbacktesting/indicators/momentum/trixh.py +20 -0
  111. openbacktesting/indicators/momentum/tsi.py +26 -0
  112. openbacktesting/indicators/momentum/uo.py +22 -0
  113. openbacktesting/indicators/momentum/vwmacd.py +20 -0
  114. openbacktesting/indicators/momentum/wavetrend.py +15 -0
  115. openbacktesting/indicators/momentum/willr.py +10 -0
  116. openbacktesting/indicators/overlap/__init__.py +0 -0
  117. openbacktesting/indicators/overlap/alligator.py +13 -0
  118. openbacktesting/indicators/overlap/alma.py +33 -0
  119. openbacktesting/indicators/overlap/anchored_vwap.py +50 -0
  120. openbacktesting/indicators/overlap/anchored_vwap_bands.py +13 -0
  121. openbacktesting/indicators/overlap/avgprice.py +10 -0
  122. openbacktesting/indicators/overlap/dema.py +10 -0
  123. openbacktesting/indicators/overlap/ema.py +12 -0
  124. openbacktesting/indicators/overlap/frama.py +29 -0
  125. openbacktesting/indicators/overlap/fwma.py +10 -0
  126. openbacktesting/indicators/overlap/hilo.py +26 -0
  127. openbacktesting/indicators/overlap/hl2.py +10 -0
  128. openbacktesting/indicators/overlap/hlc3.py +10 -0
  129. openbacktesting/indicators/overlap/hma.py +10 -0
  130. openbacktesting/indicators/overlap/ht_trendline.py +10 -0
  131. openbacktesting/indicators/overlap/hwma.py +10 -0
  132. openbacktesting/indicators/overlap/ichimoku.py +44 -0
  133. openbacktesting/indicators/overlap/jma.py +10 -0
  134. openbacktesting/indicators/overlap/kama.py +10 -0
  135. openbacktesting/indicators/overlap/linreg.py +15 -0
  136. openbacktesting/indicators/overlap/linregangle.py +10 -0
  137. openbacktesting/indicators/overlap/linregintercept.py +10 -0
  138. openbacktesting/indicators/overlap/linregslope.py +10 -0
  139. openbacktesting/indicators/overlap/ma.py +10 -0
  140. openbacktesting/indicators/overlap/ma_envelopes.py +16 -0
  141. openbacktesting/indicators/overlap/mama.py +17 -0
  142. openbacktesting/indicators/overlap/mavp.py +17 -0
  143. openbacktesting/indicators/overlap/mcgd.py +10 -0
  144. openbacktesting/indicators/overlap/medprice.py +10 -0
  145. openbacktesting/indicators/overlap/midpoint.py +10 -0
  146. openbacktesting/indicators/overlap/midprice.py +10 -0
  147. openbacktesting/indicators/overlap/mmar.py +21 -0
  148. openbacktesting/indicators/overlap/ohlc4.py +10 -0
  149. openbacktesting/indicators/overlap/pwma.py +10 -0
  150. openbacktesting/indicators/overlap/rainbow.py +25 -0
  151. openbacktesting/indicators/overlap/rma.py +10 -0
  152. openbacktesting/indicators/overlap/sinwma.py +10 -0
  153. openbacktesting/indicators/overlap/sma.py +14 -0
  154. openbacktesting/indicators/overlap/ssf.py +10 -0
  155. openbacktesting/indicators/overlap/supertrend.py +21 -0
  156. openbacktesting/indicators/overlap/swma.py +10 -0
  157. openbacktesting/indicators/overlap/t3.py +10 -0
  158. openbacktesting/indicators/overlap/tema.py +10 -0
  159. openbacktesting/indicators/overlap/trima.py +10 -0
  160. openbacktesting/indicators/overlap/tsf.py +10 -0
  161. openbacktesting/indicators/overlap/typprice.py +10 -0
  162. openbacktesting/indicators/overlap/vidya.py +10 -0
  163. openbacktesting/indicators/overlap/vwap.py +10 -0
  164. openbacktesting/indicators/overlap/vwma.py +10 -0
  165. openbacktesting/indicators/overlap/wcp.py +10 -0
  166. openbacktesting/indicators/overlap/wma.py +10 -0
  167. openbacktesting/indicators/overlap/zlma.py +10 -0
  168. openbacktesting/indicators/performance/__init__.py +0 -0
  169. openbacktesting/indicators/performance/drawdown.py +18 -0
  170. openbacktesting/indicators/performance/log_return.py +10 -0
  171. openbacktesting/indicators/performance/percent_return.py +10 -0
  172. openbacktesting/indicators/predicates.py +88 -0
  173. openbacktesting/indicators/registry.py +571 -0
  174. openbacktesting/indicators/runtime.py +290 -0
  175. openbacktesting/indicators/statistics/__init__.py +0 -0
  176. openbacktesting/indicators/statistics/beta.py +10 -0
  177. openbacktesting/indicators/statistics/correl.py +10 -0
  178. openbacktesting/indicators/statistics/entropy.py +10 -0
  179. openbacktesting/indicators/statistics/kurtosis.py +10 -0
  180. openbacktesting/indicators/statistics/mad.py +10 -0
  181. openbacktesting/indicators/statistics/md.py +10 -0
  182. openbacktesting/indicators/statistics/median.py +10 -0
  183. openbacktesting/indicators/statistics/mode.py +48 -0
  184. openbacktesting/indicators/statistics/quantile.py +10 -0
  185. openbacktesting/indicators/statistics/skew.py +10 -0
  186. openbacktesting/indicators/statistics/stderr.py +10 -0
  187. openbacktesting/indicators/statistics/stdev.py +10 -0
  188. openbacktesting/indicators/statistics/variance.py +10 -0
  189. openbacktesting/indicators/statistics/zscore.py +10 -0
  190. openbacktesting/indicators/trend/__init__.py +0 -0
  191. openbacktesting/indicators/trend/adx.py +28 -0
  192. openbacktesting/indicators/trend/adxr.py +28 -0
  193. openbacktesting/indicators/trend/amat.py +19 -0
  194. openbacktesting/indicators/trend/aroon.py +18 -0
  195. openbacktesting/indicators/trend/atr_stop.py +33 -0
  196. openbacktesting/indicators/trend/chop.py +20 -0
  197. openbacktesting/indicators/trend/cksp.py +17 -0
  198. openbacktesting/indicators/trend/cpr.py +57 -0
  199. openbacktesting/indicators/trend/decay.py +10 -0
  200. openbacktesting/indicators/trend/decreasing.py +18 -0
  201. openbacktesting/indicators/trend/dpo.py +12 -0
  202. openbacktesting/indicators/trend/dx.py +19 -0
  203. openbacktesting/indicators/trend/edecay.py +10 -0
  204. openbacktesting/indicators/trend/increasing.py +18 -0
  205. openbacktesting/indicators/trend/long_run.py +10 -0
  206. openbacktesting/indicators/trend/minus_dm.py +10 -0
  207. openbacktesting/indicators/trend/pivot_points.py +89 -0
  208. openbacktesting/indicators/trend/pivothigh.py +23 -0
  209. openbacktesting/indicators/trend/pivotlow.py +23 -0
  210. openbacktesting/indicators/trend/plus_dm.py +10 -0
  211. openbacktesting/indicators/trend/pmax.py +18 -0
  212. openbacktesting/indicators/trend/psar.py +19 -0
  213. openbacktesting/indicators/trend/qstick.py +10 -0
  214. openbacktesting/indicators/trend/sarext.py +35 -0
  215. openbacktesting/indicators/trend/short_run.py +10 -0
  216. openbacktesting/indicators/trend/tsignals.py +26 -0
  217. openbacktesting/indicators/trend/ttm_trend.py +16 -0
  218. openbacktesting/indicators/trend/vhf.py +10 -0
  219. openbacktesting/indicators/trend/volatility_stop.py +33 -0
  220. openbacktesting/indicators/trend/vortex.py +17 -0
  221. openbacktesting/indicators/trend/xsignals.py +29 -0
  222. openbacktesting/indicators/types.py +344 -0
  223. openbacktesting/indicators/utility/__init__.py +0 -0
  224. openbacktesting/indicators/utility/barssince.py +21 -0
  225. openbacktesting/indicators/utility/change.py +7 -0
  226. openbacktesting/indicators/utility/cross.py +12 -0
  227. openbacktesting/indicators/utility/crossover.py +11 -0
  228. openbacktesting/indicators/utility/crossunder.py +11 -0
  229. openbacktesting/indicators/utility/cum.py +7 -0
  230. openbacktesting/indicators/utility/falling.py +10 -0
  231. openbacktesting/indicators/utility/highest.py +7 -0
  232. openbacktesting/indicators/utility/highestbars.py +29 -0
  233. openbacktesting/indicators/utility/lowest.py +7 -0
  234. openbacktesting/indicators/utility/lowestbars.py +29 -0
  235. openbacktesting/indicators/utility/percentile_linear_interpolation.py +12 -0
  236. openbacktesting/indicators/utility/percentile_nearest_rank.py +16 -0
  237. openbacktesting/indicators/utility/percentrank.py +7 -0
  238. openbacktesting/indicators/utility/range.py +8 -0
  239. openbacktesting/indicators/utility/rising.py +10 -0
  240. openbacktesting/indicators/utility/rolling_count.py +12 -0
  241. openbacktesting/indicators/utility/rolling_sum.py +7 -0
  242. openbacktesting/indicators/utility/running_max.py +7 -0
  243. openbacktesting/indicators/utility/running_min.py +7 -0
  244. openbacktesting/indicators/utility/shift.py +7 -0
  245. openbacktesting/indicators/utility/valuewhen.py +25 -0
  246. openbacktesting/indicators/volatility/__init__.py +0 -0
  247. openbacktesting/indicators/volatility/aberration.py +21 -0
  248. openbacktesting/indicators/volatility/accbands.py +20 -0
  249. openbacktesting/indicators/volatility/adr.py +7 -0
  250. openbacktesting/indicators/volatility/atr.py +12 -0
  251. openbacktesting/indicators/volatility/avolume.py +10 -0
  252. openbacktesting/indicators/volatility/bbands.py +20 -0
  253. openbacktesting/indicators/volatility/ce.py +25 -0
  254. openbacktesting/indicators/volatility/cvi.py +10 -0
  255. openbacktesting/indicators/volatility/donchian.py +20 -0
  256. openbacktesting/indicators/volatility/hvol.py +10 -0
  257. openbacktesting/indicators/volatility/hwc.py +27 -0
  258. openbacktesting/indicators/volatility/kc.py +27 -0
  259. openbacktesting/indicators/volatility/massi.py +10 -0
  260. openbacktesting/indicators/volatility/natr.py +19 -0
  261. openbacktesting/indicators/volatility/pdist.py +10 -0
  262. openbacktesting/indicators/volatility/rvi.py +21 -0
  263. openbacktesting/indicators/volatility/starc.py +11 -0
  264. openbacktesting/indicators/volatility/thermo.py +28 -0
  265. openbacktesting/indicators/volatility/true_range.py +31 -0
  266. openbacktesting/indicators/volatility/ui.py +10 -0
  267. openbacktesting/indicators/volume/__init__.py +0 -0
  268. openbacktesting/indicators/volume/ad.py +10 -0
  269. openbacktesting/indicators/volume/adosc.py +19 -0
  270. openbacktesting/indicators/volume/aobv.py +31 -0
  271. openbacktesting/indicators/volume/cmf.py +12 -0
  272. openbacktesting/indicators/volume/efi.py +12 -0
  273. openbacktesting/indicators/volume/emv.py +10 -0
  274. openbacktesting/indicators/volume/eom.py +19 -0
  275. openbacktesting/indicators/volume/iii.py +9 -0
  276. openbacktesting/indicators/volume/kvo.py +28 -0
  277. openbacktesting/indicators/volume/marketfi.py +10 -0
  278. openbacktesting/indicators/volume/mfi.py +12 -0
  279. openbacktesting/indicators/volume/nvi.py +26 -0
  280. openbacktesting/indicators/volume/obv.py +10 -0
  281. openbacktesting/indicators/volume/pvi.py +26 -0
  282. openbacktesting/indicators/volume/pvol.py +10 -0
  283. openbacktesting/indicators/volume/pvr.py +10 -0
  284. openbacktesting/indicators/volume/pvt.py +10 -0
  285. openbacktesting/indicators/volume/rvol.py +8 -0
  286. openbacktesting/indicators/volume/vfi.py +18 -0
  287. openbacktesting/indicators/volume/vosc.py +10 -0
  288. openbacktesting/indicators/volume/wad.py +10 -0
  289. openbacktesting/indicators/volume/wvad.py +9 -0
  290. openbacktesting/inputs.py +51 -0
  291. openbacktesting/lib.py +31 -0
  292. openbacktesting/logs.py +129 -0
  293. openbacktesting/optimization.py +8 -0
  294. openbacktesting/optimizer/__init__.py +25 -0
  295. openbacktesting/optimizer/_semantics.py +155 -0
  296. openbacktesting/optimizer/identity.py +45 -0
  297. openbacktesting/optimizer/models.py +92 -0
  298. openbacktesting/optimizer/plan.py +129 -0
  299. openbacktesting/optimizer/portfolio-result.schema.json +4984 -0
  300. openbacktesting/optimizer/query.py +257 -0
  301. openbacktesting/optimizer/runner.py +98 -0
  302. openbacktesting/optimizer/storage.py +261 -0
  303. openbacktesting/optimizer/validation.py +64 -0
  304. openbacktesting/parallel.py +9 -0
  305. openbacktesting/plotting/__init__.py +23 -0
  306. openbacktesting/plotting/collector.py +509 -0
  307. openbacktesting/plotting/encoding.py +102 -0
  308. openbacktesting/plotting/types.py +92 -0
  309. openbacktesting/portfolio/__init__.py +15 -0
  310. openbacktesting/portfolio/analysis.py +245 -0
  311. openbacktesting/portfolio/builder.py +381 -0
  312. openbacktesting/portfolio/catalog.json +1404 -0
  313. openbacktesting/portfolio/helpers.py +92 -0
  314. openbacktesting/portfolio/metrics.py +186 -0
  315. openbacktesting/portfolio/models.py +63 -0
  316. openbacktesting/portfolio/periods.py +164 -0
  317. openbacktesting/portfolio/rates.py +14 -0
  318. openbacktesting/portfolio/snapshot.py +111 -0
  319. openbacktesting/portfolio/stats.py +272 -0
  320. openbacktesting/portfolio/trades.py +136 -0
  321. openbacktesting/portfolio/trading.py +215 -0
  322. openbacktesting/py.typed +0 -0
  323. openbacktesting/runtime.py +330 -0
  324. openbacktesting/simulation/__init__.py +17 -0
  325. openbacktesting/simulation/collection.py +13 -0
  326. openbacktesting/simulation/config.py +241 -0
  327. openbacktesting/simulation/core.py +528 -0
  328. openbacktesting/simulation/optimization.py +326 -0
  329. openbacktesting/simulation/parallel.py +145 -0
  330. openbacktesting/simulation/variants.py +107 -0
  331. openbacktesting/strategy/__init__.py +19 -0
  332. openbacktesting/strategy/bar_state.py +14 -0
  333. openbacktesting/strategy/base.py +810 -0
  334. openbacktesting/strategy/declaration.py +90 -0
  335. openbacktesting/strategy/helpers.py +198 -0
  336. openbacktesting/strategy/inputs.py +460 -0
  337. openbacktesting/strategy/sizing.py +80 -0
  338. openbacktesting/strategy/templates.py +160 -0
  339. openbacktesting/strategy_data.py +10 -0
  340. openbacktesting/test/BTCUSD.csv +157 -0
  341. openbacktesting/test/EURUSD.csv +5001 -0
  342. openbacktesting/test/GOOG.csv +2149 -0
  343. openbacktesting/test/__init__.py +29 -0
  344. openbacktesting/test/__main__.py +11 -0
  345. openbacktesting/test/_test.py +1312 -0
  346. openbacktesting/util.py +31 -0
  347. openbacktesting-0.2.0.dist-info/METADATA +151 -0
  348. openbacktesting-0.2.0.dist-info/RECORD +350 -0
  349. openbacktesting-0.2.0.dist-info/WHEEL +4 -0
  350. openbacktesting-0.2.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,290 @@
1
+ """Built-in indicator catalog, pure computation entry points, and bounded computation reuse."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections import OrderedDict
7
+ from concurrent.futures import Future
8
+ from contextlib import contextmanager
9
+ from copy import deepcopy
10
+ from hashlib import sha256
11
+ from importlib.metadata import version
12
+ from threading import RLock
13
+ from uuid import uuid4
14
+
15
+ import numpy as np
16
+ import pandas as pd
17
+
18
+ from .common import SPECS, to_arrays
19
+ from .registry import execute, validate_backend
20
+ from .types import IndicatorCursor, IndicatorError, IndicatorHandle
21
+
22
+ DEFAULT_CACHE_BYTES = 64 * 1024 * 1024
23
+ _CATALOG = {spec.id: spec for spec in SPECS}
24
+ _COLUMNS = {"open_": "Open", "high": "High", "low": "Low", "close": "Close", "volume": "Volume"}
25
+
26
+
27
+ def catalog():
28
+ """Return a tuple of all reviewed definitions, including deferred entries that cannot be called.
29
+
30
+ Returns:
31
+ tuple: Indicator definitions; check status before calling, and expect the count to vary with the reviewed catalog.
32
+ """
33
+ return tuple(_CATALOG.values())
34
+
35
+
36
+ def describe(indicator):
37
+ """Look up an indicator definition by stable ID without computing it.
38
+
39
+ Args:
40
+ indicator: Full catalog ID, such as ta.ema.
41
+
42
+ Returns:
43
+ IndicatorSpec: Definition of parameter domains, outputs, status, and semantic version.
44
+
45
+ Raises:
46
+ IndicatorError: code is unknown-indicator when the ID does not exist.
47
+ """
48
+ try:
49
+ return _CATALOG[indicator]
50
+ except KeyError as error:
51
+ raise IndicatorError("unknown-indicator", indicator, "Unregistered indicator") from error
52
+
53
+
54
+ class IndicatorCache:
55
+ """The budget limits cache retention; active strategies' holdings are not bounded by this budget."""
56
+
57
+ def __init__(self, budget=DEFAULT_CACHE_BYTES):
58
+ if isinstance(budget, bool) or not isinstance(budget, int) or budget < 0:
59
+ raise ValueError("Cache budget must be a nonnegative integer")
60
+ self.budget = budget
61
+ self.bytes = 0
62
+ self.hits = self.misses = 0
63
+ self._entries = OrderedDict()
64
+ self._pending = {}
65
+ self._lock = RLock()
66
+
67
+ def get(self, key, calculate):
68
+ """Look up key in the cache or run calculate; coalesce concurrent identical keys, do not cache failures, and return read-only templates."""
69
+ if not self.budget:
70
+ self.misses += 1
71
+ return calculate()
72
+ with self._lock:
73
+ if key in self._entries:
74
+ self.hits += 1
75
+ self._entries.move_to_end(key)
76
+ return self._entries[key][0]
77
+ if key in self._pending:
78
+ future = self._pending[key]
79
+ owner = False
80
+ else:
81
+ future = self._pending[key] = Future()
82
+ self.misses += 1
83
+ owner = True
84
+ if not owner:
85
+ return future.result()
86
+ try:
87
+ value = calculate()
88
+ size = (
89
+ value._values.nbytes
90
+ + value._valid.nbytes
91
+ + value._ready.nbytes
92
+ + value._gating.nbytes
93
+ + value._opts["index"].nbytes
94
+ )
95
+ with self._lock:
96
+ if self.budget and size <= self.budget:
97
+ while self.bytes + size > self.budget:
98
+ _, (_, removed) = self._entries.popitem(last=False)
99
+ self.bytes -= removed
100
+ self._entries[key] = (value, size)
101
+ self.bytes += size
102
+ future.set_result(value)
103
+ return value
104
+ except BaseException as error:
105
+ future.set_exception(error)
106
+ raise
107
+ finally:
108
+ with self._lock:
109
+ del self._pending[key]
110
+
111
+ def clear(self):
112
+ """Clear retained entries and reset retained bytes to zero; do not revoke active result references."""
113
+ with self._lock:
114
+ self._entries.clear()
115
+ self.bytes = 0
116
+
117
+
118
+ def _data_identity(frame):
119
+ digest = sha256(pd.util.hash_pandas_object(frame, index=True).values.tobytes())
120
+ digest.update(
121
+ repr(
122
+ (
123
+ tuple(frame.columns),
124
+ tuple(map(str, frame.dtypes)),
125
+ str(frame.index.dtype),
126
+ frame.index.name,
127
+ )
128
+ ).encode()
129
+ )
130
+ return digest.hexdigest()
131
+
132
+
133
+ def _prepare_inputs(spec, frame, inputs, cursor):
134
+ if not isinstance(frame, pd.DataFrame) or not frame.index.is_unique:
135
+ raise IndicatorError("index-mismatch", spec.id, "Input must be a DataFrame with a unique index")
136
+ mapping = dict(inputs or {})
137
+ if mapping.keys() - set(spec.inputs):
138
+ raise IndicatorError("missing-input", spec.id, "Input mapping contains undeclared fields")
139
+ columns = {}
140
+ dtypes = {}
141
+ defaults = dict(spec.input_defaults)
142
+ for name in spec.inputs:
143
+ source = mapping.get(name, defaults.get(name, _COLUMNS.get(name, name)))
144
+ if isinstance(source, IndicatorHandle):
145
+ if cursor is None or source._cursor is not cursor or source.ndim != 1:
146
+ raise IndicatorError("index-mismatch", spec.id, "Indicator inputs must come from a single channel in this run")
147
+ series = source.s.where(source.valid)
148
+ else:
149
+ if not isinstance(source, str) or source not in frame:
150
+ raise IndicatorError("missing-input", spec.id, f"Missing input: {name}")
151
+ series = frame[source]
152
+ if not series.index.equals(frame.index):
153
+ raise IndicatorError("index-mismatch", spec.id, "Input index does not match")
154
+ if not pd.api.types.is_numeric_dtype(series.dtype):
155
+ raise IndicatorError("missing-input", spec.id, f"{name} must be numeric")
156
+ dtypes[name] = str(series.dtype)
157
+ columns[name] = series.to_numpy(dtype=float, copy=False)
158
+ prepared = pd.DataFrame(columns, index=frame.index, copy=True)
159
+ prepared.attrs["input_dtypes"] = dtypes
160
+ return prepared
161
+
162
+
163
+ def compute(
164
+ indicator,
165
+ frame,
166
+ *,
167
+ parameters=None,
168
+ backend=None,
169
+ inputs=None,
170
+ cache=None,
171
+ cursor=None,
172
+ required_outputs=None,
173
+ ):
174
+ """Compute reviewed indicators in bulk on explicit market data and return read-only result handles.
175
+
176
+ Args:
177
+ indicator: Full catalog ID.
178
+ frame: pandas DataFrame; index and inputs must satisfy the indicator contract.
179
+ parameters: Parameter mapping; reject unknown keys, invalid types, and invalid relationships.
180
+ backend: Backend identifier; None uses the catalog default without silent fallback.
181
+ inputs: Mapping from indicator inputs to market-data column names or single channels from the same run.
182
+ cache: Optional IndicatorCache; None recomputes each time.
183
+ cursor: Internal framework run cursor; None for standalone computation.
184
+ required_outputs: Canonical required output names; an empty list disables warmup gating.
185
+
186
+ Returns:
187
+ IndicatorHandle: Single- or multi-channel handle with valid/ready masks and provenance metadata.
188
+
189
+ Raises:
190
+ IndicatorError: Failure in ID, parameters, backend, inputs, outputs, or computation.
191
+ """
192
+ spec = describe(indicator)
193
+ parameters = spec.normalize(dict(parameters or {}))
194
+ backend = spec.default_backend if backend is None else backend
195
+ binding = validate_backend(spec, backend)
196
+ if spec.status != "available":
197
+ raise IndicatorError("unavailable-indicator", indicator, spec.reason)
198
+ required = spec.outputs if spec.required is None else spec.required
199
+ required = tuple(required if required_outputs is None else required_outputs)
200
+ if not set(required) <= set(spec.outputs):
201
+ raise IndicatorError("output-contract", indicator, "Unknown required channel")
202
+ data = _prepare_inputs(spec, frame, inputs, cursor)
203
+ metadata = {
204
+ "id": indicator,
205
+ "semantic_version": spec.version,
206
+ "implementation_version": binding.implementation_version,
207
+ "parameters": parameters,
208
+ "data_identity": _data_identity(data),
209
+ "input_dtypes": data.attrs["input_dtypes"],
210
+ "backend": backend,
211
+ "versions": {name: version(name) for name in ("pandas-ta-classic", "pandas", "numpy")},
212
+ "dtype": spec.dtype,
213
+ "kind": spec.kind,
214
+ "validity": spec.validity,
215
+ "missing_policy": spec.missing_policy,
216
+ "readiness": "history-and-finite-v1",
217
+ "unit": spec.unit,
218
+ "engine_version": version("openbacktesting"),
219
+ "inputs": {
220
+ name: (source.metadata | {"channel": source.name})
221
+ if isinstance(source, IndicatorHandle)
222
+ else source
223
+ for name, source in (inputs or {}).items()
224
+ },
225
+ "fixed": dict(spec.fixed),
226
+ }
227
+ key = json.dumps(metadata, sort_keys=True)
228
+
229
+ def calculate():
230
+ try:
231
+ result = execute(spec, {name: data[name] for name in spec.inputs}, parameters, backend)
232
+ except IndicatorError:
233
+ raise
234
+ except Exception as error:
235
+ raise IndicatorError(
236
+ "calculation-failed", indicator, str(error), backend=backend
237
+ ) from error
238
+ values, valid = to_arrays(spec, result, data.index, parameters)
239
+ ready = np.arange(len(data)) >= spec.history_required(parameters) - 1
240
+ current_valid = np.isfinite(data.to_numpy()).all(axis=1)
241
+ valid &= ready
242
+ if spec.validity == "current-inputs":
243
+ valid &= current_valid
244
+ # Unready values are not used for trading; preserve native values for diagnostics rather than filling zeros to feign validity.
245
+ if spec.dtype != "float64":
246
+ values = np.nan_to_num(values).astype(spec.dtype)
247
+ if len(spec.outputs) == 1:
248
+ values, valid = values[0], valid[0]
249
+ return IndicatorHandle(
250
+ values,
251
+ name=spec.outputs[0] if len(spec.outputs) == 1 else spec.outputs,
252
+ index=data.index,
253
+ valid=valid,
254
+ metadata=metadata,
255
+ ready=ready,
256
+ )
257
+
258
+ template = calculate() if cache is None else cache.get(key, calculate)
259
+ handle = object.__new__(IndicatorHandle)
260
+ handle.__dict__ = template.__dict__.copy()
261
+ handle._metadata = deepcopy(template._metadata)
262
+ handle._cursor = cursor or IndicatorCursor(len(frame), "complete")
263
+ handle._set_required(spec.outputs.index(name) for name in required)
264
+ handle._opts = template._opts.copy()
265
+ handle._metadata["required_outputs"] = list(required)
266
+ return handle
267
+
268
+
269
+ _OPTIMIZATION_CACHES = {}
270
+ _OPTIMIZATION_LOCK = RLock()
271
+
272
+
273
+ def optimization_cache(token, budget):
274
+ """Reuse within a process; token is serializable, while caches and locks are excluded from task messages."""
275
+ with _OPTIMIZATION_LOCK:
276
+ if token not in _OPTIMIZATION_CACHES:
277
+ _OPTIMIZATION_CACHES[token] = IndicatorCache(budget)
278
+ return _OPTIMIZATION_CACHES[token]
279
+
280
+
281
+ @contextmanager
282
+ def optimization_scope(budget):
283
+ """The parent process/thread releases in finally; child caches are released when the Pool owned by this run exits."""
284
+ token = uuid4().hex
285
+ optimization_cache(token, budget)
286
+ try:
287
+ yield token
288
+ finally:
289
+ with _OPTIMIZATION_LOCK:
290
+ _OPTIMIZATION_CACHES.pop(token).clear()
File without changes
@@ -0,0 +1,10 @@
1
+ """ta.beta: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.beta import beta as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, benchmark, length, talib):
9
+ result = _calculate(close=close, benchmark=benchmark, length=length, talib=talib)
10
+ return named_series(result, "ta.beta", "beta")
@@ -0,0 +1,10 @@
1
+ """ta.correl: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.correl import correl as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, benchmark, length, talib):
9
+ result = _calculate(close=close, benchmark=benchmark, length=length, talib=talib)
10
+ return named_series(result, "ta.correl", "correl")
@@ -0,0 +1,10 @@
1
+ """ta.entropy: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.entropy import entropy as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, base, talib):
9
+ result = _calculate(close=close, length=length, base=base, talib=talib)
10
+ return named_series(result, "ta.entropy", "entropy")
@@ -0,0 +1,10 @@
1
+ """ta.kurtosis: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.kurtosis import kurtosis as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, talib):
9
+ result = _calculate(close=close, length=length, talib=talib)
10
+ return named_series(result, "ta.kurtosis", "kurtosis")
@@ -0,0 +1,10 @@
1
+ """ta.mad: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.mad import mad as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, talib):
9
+ result = _calculate(close=close, length=length, talib=talib)
10
+ return named_series(result, "ta.mad", "mad")
@@ -0,0 +1,10 @@
1
+ """ta.md: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.md import md as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, talib):
9
+ result = _calculate(close=close, length=length, talib=talib)
10
+ return named_series(result, "ta.md", "md")
@@ -0,0 +1,10 @@
1
+ """ta.median: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.median import median as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, talib):
9
+ result = _calculate(close=close, length=length, talib=talib)
10
+ return named_series(result, "ta.median", "median")
@@ -0,0 +1,48 @@
1
+ """Mode of the latest length finite observations; ties choose the smallest value, with state bounded by the window."""
2
+
3
+ from collections import Counter, deque
4
+ from heapq import heapify, heappop, heappush
5
+ from math import isfinite
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+
10
+
11
+ class _ModeWindow:
12
+ def __init__(self, length):
13
+ self.length = length
14
+ self.queue = deque()
15
+ self.counts = Counter()
16
+ self.heap = []
17
+
18
+ def _change(self, value, delta):
19
+ count = self.counts[value] + delta
20
+ if count:
21
+ self.counts[value] = count
22
+ heappush(self.heap, (-count, value))
23
+ else:
24
+ del self.counts[value]
25
+
26
+ def add(self, value):
27
+ self.queue.append(value)
28
+ self._change(value, 1)
29
+ if len(self.queue) > self.length:
30
+ self._change(self.queue.popleft(), -1)
31
+ if len(self.heap) > 4 * self.length:
32
+ self.heap = [(-count, value) for value, count in self.counts.items()]
33
+ heapify(self.heap)
34
+ while -self.heap[0][0] != self.counts.get(self.heap[0][1], 0):
35
+ heappop(self.heap)
36
+
37
+ def value(self):
38
+ return self.heap[0][1] if len(self.queue) == self.length else np.nan
39
+
40
+
41
+ def compute(*, close, length):
42
+ window = _ModeWindow(length)
43
+ result = np.full(len(close), np.nan)
44
+ for i, value in enumerate(close):
45
+ if isfinite(value):
46
+ window.add(value)
47
+ result[i] = window.value()
48
+ return pd.Series(result, index=close.index, name="mode")
@@ -0,0 +1,10 @@
1
+ """ta.quantile: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.quantile import quantile as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, q, talib):
9
+ result = _calculate(close=close, length=length, q=q, talib=talib)
10
+ return named_series(result, "ta.quantile", "quantile")
@@ -0,0 +1,10 @@
1
+ """ta.skew: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.skew import skew as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, talib):
9
+ result = _calculate(close=close, length=length, talib=talib)
10
+ return named_series(result, "ta.skew", "skew")
@@ -0,0 +1,10 @@
1
+ """ta.stderr: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.stderr import stderr as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, ddof, talib):
9
+ result = _calculate(close=close, length=length, ddof=ddof, talib=talib)
10
+ return named_series(result, "ta.stderr", "stderr")
@@ -0,0 +1,10 @@
1
+ """ta.stdev: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.stdev import stdev as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, ddof, talib):
9
+ result = _calculate(close=close, length=length, ddof=ddof, talib=talib)
10
+ return named_series(result, "ta.stdev", "stdev")
@@ -0,0 +1,10 @@
1
+ """ta.variance: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.variance import variance as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, ddof, talib):
9
+ result = _calculate(close=close, length=length, ddof=ddof, talib=talib)
10
+ return named_series(result, "ta.variance", "variance")
@@ -0,0 +1,10 @@
1
+ """ta.zscore: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.statistics.zscore import zscore as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, close, length, std, talib):
9
+ result = _calculate(close=close, length=length, std=std, talib=talib)
10
+ return named_series(result, "ta.zscore", "zscore")
File without changes
@@ -0,0 +1,28 @@
1
+ """ta.adx: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.trend.adx import adx as _calculate
4
+
5
+ from ..common import named_frame
6
+
7
+
8
+ def compute(*, high, low, close, length, lensig, scalar, mamode, drift, talib):
9
+ result = _calculate(
10
+ high=high,
11
+ low=low,
12
+ close=close,
13
+ length=length,
14
+ lensig=lensig,
15
+ scalar=scalar,
16
+ mamode=mamode,
17
+ drift=drift,
18
+ talib=talib,
19
+ )
20
+ return named_frame(
21
+ result,
22
+ "ta.adx",
23
+ {
24
+ f"ADX_{lensig}": "adx",
25
+ f"DMP_{length}": "positive",
26
+ f"DMN_{length}": "negative",
27
+ },
28
+ )
@@ -0,0 +1,28 @@
1
+ """ta.adxr: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.trend.adxr import adxr as _calculate
4
+
5
+ from ..common import named_frame
6
+
7
+
8
+ def compute(*, high, low, close, length, lensig, scalar, mamode, drift, talib):
9
+ result = _calculate(
10
+ high=high,
11
+ low=low,
12
+ close=close,
13
+ length=length,
14
+ lensig=lensig,
15
+ scalar=scalar,
16
+ mamode=mamode,
17
+ drift=drift,
18
+ talib=talib,
19
+ )
20
+ return named_frame(
21
+ result,
22
+ "ta.adxr",
23
+ {
24
+ f"ADXR_{lensig}": "adxr",
25
+ f"DMP_{length}": "positive",
26
+ f"DMN_{length}": "negative",
27
+ },
28
+ )
@@ -0,0 +1,19 @@
1
+ """ta.amat: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.trend.amat import amat as _calculate
4
+
5
+ from ..common import named_frame
6
+
7
+
8
+ def compute(*, close, fast, slow, lookback, mamode, talib):
9
+ result = _calculate(
10
+ close=close, fast=fast, slow=slow, lookback=lookback, mamode=mamode, talib=talib
11
+ )
12
+ return named_frame(
13
+ result,
14
+ "ta.amat",
15
+ {
16
+ f"AMAT{mamode[0]}_LR_{fast}_{slow}_{lookback}": "long",
17
+ f"AMAT{mamode[0]}_SR_{fast}_{slow}_{lookback}": "short",
18
+ },
19
+ )
@@ -0,0 +1,18 @@
1
+ """ta.aroon: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.trend.aroon import aroon as _calculate
4
+
5
+ from ..common import named_frame
6
+
7
+
8
+ def compute(*, high, low, length, scalar, talib):
9
+ result = _calculate(high=high, low=low, length=length, scalar=scalar, talib=talib)
10
+ return named_frame(
11
+ result,
12
+ "ta.aroon",
13
+ {
14
+ f"AROOND_{length}": "down",
15
+ f"AROONU_{length}": "up",
16
+ f"AROONOSC_{length}": "oscillator",
17
+ },
18
+ )
@@ -0,0 +1,33 @@
1
+ """ta.atr_stop: Independent formula with a fixed initialization contract."""
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+ from ..calculus import atr, clean
7
+
8
+
9
+ def compute(*, high, low, close, length, multiplier, end_type):
10
+ high, low, close = clean(high), clean(low), clean(close)
11
+ width = atr(high, low, close, length).to_numpy() * multiplier
12
+ prices, highs, lows = close.to_numpy(), high.to_numpy(), low.to_numpy()
13
+ stop = np.full(len(close), np.nan)
14
+ direction = stop.copy()
15
+ upper = lower = np.nan
16
+ bullish = True
17
+ for i in range(1, len(close)):
18
+ if not np.isfinite(width[i]) or not np.isfinite(prices[i]):
19
+ upper = lower = np.nan
20
+ continue
21
+ hi = (prices[i] if end_type == "close" else highs[i]) + width[i]
22
+ lo = (prices[i] if end_type == "close" else lows[i]) - width[i]
23
+ if not np.isfinite(upper):
24
+ bullish = prices[i] >= prices[i - 1]
25
+ upper, lower = hi, lo
26
+ if hi < upper or prices[i - 1] > upper:
27
+ upper = hi
28
+ if lo > lower or prices[i - 1] < lower:
29
+ lower = lo
30
+ bullish = prices[i] > (lower if bullish else upper)
31
+ stop[i] = lower if bullish else upper
32
+ direction[i] = 1 if bullish else -1
33
+ return pd.DataFrame({"stop": stop, "direction": direction}, index=close.index)
@@ -0,0 +1,20 @@
1
+ """ta.chop: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.trend.chop import chop as _calculate
4
+
5
+ from ..common import named_series
6
+
7
+
8
+ def compute(*, high, low, close, length, atr_length, ln, scalar, drift, talib):
9
+ result = _calculate(
10
+ high=high,
11
+ low=low,
12
+ close=close,
13
+ length=length,
14
+ atr_length=atr_length,
15
+ ln=ln,
16
+ scalar=scalar,
17
+ drift=drift,
18
+ talib=talib,
19
+ )
20
+ return named_series(result, "ta.chop", "chop")
@@ -0,0 +1,17 @@
1
+ """ta.cksp: Computation wrapper for the pinned dependency with canonical outputs."""
2
+
3
+ from pandas_ta_classic.trend.cksp import cksp as _calculate
4
+
5
+ from ..common import named_frame
6
+
7
+
8
+ def compute(*, high, low, close, p, x, q, tvmode, talib):
9
+ result = _calculate(high=high, low=low, close=close, p=p, x=x, q=q, tvmode=tvmode, talib=talib)
10
+ return named_frame(
11
+ result,
12
+ "ta.cksp",
13
+ {
14
+ f"CKSPl_{p}_{float(x)}_{q}": "long",
15
+ f"CKSPs_{p}_{float(x)}_{q}": "short",
16
+ },
17
+ )