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,90 @@
1
+ """Author backtest declaration preserving class identity, without global registration or execution side effects."""
2
+
3
+ from collections.abc import Callable
4
+ from inspect import isabstract
5
+ from typing import TypeVar
6
+
7
+ from openbacktesting.simulation.config import (
8
+ BacktestSettings,
9
+ CommissionType,
10
+ EndPolicy,
11
+ ExecutionTiming,
12
+ QuantityType,
13
+ SlippageType,
14
+ )
15
+ from openbacktesting.strategy.base import Strategy
16
+
17
+ _S = TypeVar("_S", bound=Strategy)
18
+ _ATTRIBUTE = "_openbacktesting_backtest_settings"
19
+
20
+
21
+ def backtest(
22
+ *,
23
+ cash: float = 10_000,
24
+ spread: float = 0.0,
25
+ commission_type: CommissionType = CommissionType.PERCENT,
26
+ commission_value: float = 0.0,
27
+ risk_free_rate: float = 0.0,
28
+ default_qty_type: QuantityType = QuantityType.FIXED,
29
+ default_qty_value: float | None = None,
30
+ margin: float = 1.0,
31
+ execution_timing: ExecutionTiming = ExecutionTiming.NEXT_OPEN,
32
+ slippage_type: SlippageType = SlippageType.BPS,
33
+ slippage_value: float = 0.0,
34
+ allow_short: bool = True,
35
+ max_gross_exposure: float | None = None,
36
+ hedging: bool = False,
37
+ exclusive_orders: bool = False,
38
+ end_policy: EndPolicy = EndPolicy.MARK_TO_MARKET,
39
+ ) -> Callable[[type[_S]], type[_S]]:
40
+ """Attach default backtest settings to a concrete strategy class; the host supplies actual market data and execution.
41
+
42
+ Accept only primitive values and corresponding enum members; fixed options reject bare strings and other enums.
43
+ Only the decorator form with parentheses is supported. Algorithm parameters still use inputs; instrument,
44
+ timeframe, and resource options are not accepted. The framework constructs the configuration model and returns the original strategy class.
45
+
46
+ Args:
47
+ cash: Positive finite initial account balance.
48
+ spread: Fractional spread for every buy/sell execution, in [0,1).
49
+ commission_type: One commission type: PERCENT, CASH_PER_ORDER, or CASH_PER_CONTRACT.
50
+ commission_value: PERCENT uses percentage points in [-10,10); 0.2 means 0.2%, and negative values are rebates;
51
+ other types use nonnegative amounts, charged per actual Fill or actual asset unit respectively.
52
+ risk_free_rate: Annualized fraction in (-1,1), used only for statistics without accruing account interest.
53
+ default_qty_type: FIXED means fixed quantity; PERCENT_OF_EQUITY means equity percentage.
54
+ default_qty_value: None disables default sizing; FIXED is positive, and equity percentage is in (0,100].
55
+ Only ordinary buy/sell orders omitting qty use Close and equity at request time to budget, round toward zero, and freeze quantity.
56
+ margin: Margin fraction in (0,1], without amplifying the default quantity budget.
57
+ execution_timing: NEXT_OPEN or SAME_CLOSE; the latter handles only new unconditional market orders from next.
58
+ slippage_type: FIXED means a fixed price distance; BPS means basis points.
59
+ slippage_value: Nonnegative slippage value; zero means no slippage.
60
+ allow_short: Whether new short positions are allowed; strictly bool.
61
+ max_gross_exposure: None or a positive cap on gross notional/equity.
62
+ hedging: Whether to retain two-sided lots; strictly bool.
63
+ exclusive_orders: Whether a new ordinary entry cancels prior entries and submits exits for existing positions; strictly bool.
64
+ end_policy: MARK_TO_MARKET retains marked positions; CLOSE_AT_LAST_CLOSE liquidates actual positions.
65
+
66
+ Raises:
67
+ TypeError: Invalid types, enums, fields, or decorator targets.
68
+ ValueError: Out-of-range numbers or duplicate declarations on the same strategy.
69
+ """
70
+ settings = BacktestSettings(**locals())
71
+
72
+ def decorate(cls: type[_S]) -> type[_S]:
73
+ if not isinstance(cls, type) or not issubclass(cls, Strategy) or isabstract(cls):
74
+ raise TypeError("backtest can decorate only concrete Strategy subclasses")
75
+ if _ATTRIBUTE in vars(cls):
76
+ raise ValueError("backtest cannot be applied repeatedly to the same strategy class")
77
+ setattr(cls, _ATTRIBUTE, settings)
78
+ return cls
79
+
80
+ return decorate
81
+
82
+
83
+ def declaration(cls: type[Strategy]) -> BacktestSettings | None:
84
+ """Read only this class's direct declaration; undecorated subclasses do not inherit entry-point identity."""
85
+ value = vars(cls).get(_ATTRIBUTE)
86
+ if value is None:
87
+ return None
88
+ if not isinstance(value, BacktestSettings):
89
+ raise TypeError("Invalid strategy backtest declaration")
90
+ return value.resolve(None)
@@ -0,0 +1,198 @@
1
+ """Strategy helpers for event predicates, statistics, and resampling."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable, Sequence
6
+ from inspect import currentframe
7
+ from itertools import compress
8
+
9
+ import numpy as np
10
+ import pandas as pd
11
+
12
+ from openbacktesting.data.aggregation import OHLCV_AGG
13
+ from openbacktesting.data.core import _Array
14
+ from openbacktesting.helpers import _as_str, _set_legacy_module
15
+ from openbacktesting.indicators.types import IndicatorHandle
16
+ from openbacktesting.strategy.base import Strategy
17
+
18
+
19
+ def barssince(condition: Sequence[bool], default=np.inf) -> int:
20
+ """Query the distance from the current position to the latest true condition; this is a scalar helper.
21
+
22
+
23
+ Return the number of bars since `condition` sequence was last `True`,
24
+ or if never, return `default`.
25
+
26
+ >>> barssince(self.data.Close > self.data.Open)
27
+ 3
28
+ """
29
+ return next(compress(range(len(condition)), reversed(condition)), default)
30
+
31
+
32
+ def quantile(series: Sequence, quantile: None | float = None):
33
+ """Query the relative rank of the last value or a specified quantile value, depending on quantile.
34
+
35
+
36
+ If `quantile` is `None`, return the quantile _rank_ of the last
37
+ value of `series` wrt former series values.
38
+
39
+ If `quantile` is a value between 0 and 1, return the _value_ of
40
+ `series` at this quantile. If used to working with percentiles, just
41
+ divide your percentile amount with 100 to obtain quantiles.
42
+
43
+ >>> quantile(self.data.Close[-20:], .1)
44
+ 162.130
45
+ >>> quantile(self.data.Close)
46
+ 0.13
47
+ """
48
+ if quantile is None:
49
+ try:
50
+ last, series = series[-1], series[:-1]
51
+ return np.mean(series < last)
52
+ except IndexError:
53
+ return np.nan
54
+ assert 0 <= quantile <= 1, "quantile must be within [0, 1]"
55
+ return np.nanpercentile(series, quantile * 100)
56
+
57
+
58
+ def resample_apply(
59
+ rule: str,
60
+ func: Callable[..., Sequence] | None,
61
+ series: pd.Series | pd.DataFrame | _Array,
62
+ *args,
63
+ agg: str | dict | None = None,
64
+ **kwargs,
65
+ ):
66
+ """Resample and apply the function, then align to the original calendar; automatically register the result handle within strategy initialization.
67
+
68
+
69
+ Apply `func` (such as an indicator) to `series`, resampled to
70
+ a time frame specified by `rule`. When called from inside
71
+ `openbacktesting.engine.Strategy.init`,
72
+ the result (returned) series will be automatically wrapped in
73
+ `openbacktesting.engine.Strategy.ind`
74
+ wrapper method.
75
+
76
+ `rule` is a valid [Pandas offset string] indicating
77
+ a time frame to resample `series` to.
78
+
79
+ [Pandas offset string]: \
80
+ http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases
81
+
82
+ `func` is the indicator function to apply on the resampled series.
83
+
84
+ `series` is a data series (or array), such as any of the
85
+ `openbacktesting.engine.Strategy.data` series. Due to pandas
86
+ resampling limitations, this only works when input series
87
+ has a datetime index.
88
+
89
+ `agg` is the aggregation function to use on resampled groups of data.
90
+ Valid values are anything accepted by `pandas/resample/.agg()`.
91
+ Default value for dataframe input is `OHLCV_AGG` dictionary.
92
+ Default value for series input is the appropriate entry from `OHLCV_AGG`
93
+ if series has a matching name, or otherwise the value `"last"`,
94
+ which is suitable for closing prices,
95
+ but you might prefer another (e.g. `"max"` for peaks, or similar).
96
+
97
+ Finally, any `*args` and `**kwargs` that are not already eaten by
98
+ implicit `openbacktesting.engine.Strategy.ind` call
99
+ are passed to `func`.
100
+
101
+ For example, if we have a typical moving average function
102
+ `SMA(values, lookback_period)`, _hourly_ data source, and need to
103
+ apply the moving average MA(10) on a _daily_ time frame, we can do::
104
+
105
+ class System(Strategy):
106
+ def init(self):
107
+ self.sma = resample_apply(
108
+ 'D', SMA, self.data.Close, 10)
109
+
110
+ The above short snippet is roughly equivalent to::
111
+
112
+ class System(Strategy):
113
+ def init(self):
114
+ # Strategy exposes `self.data` as raw NumPy arrays.
115
+ # Let's convert closing prices back to pandas Series.
116
+ close = self.data.Close.s
117
+
118
+ # Resample to daily resolution. Aggregate groups
119
+ # using their last value (i.e. closing price at the end
120
+ # of the day). Notice `label='right'`. If it were set to
121
+ # 'left' (default), the strategy would exhibit
122
+ # look-ahead bias.
123
+ daily = close.resample('D', label='right').agg('last')
124
+
125
+ # We apply SMA(10) to daily close prices,
126
+ # then reindex it back to original hourly index,
127
+ # forward-filling the missing values in each day.
128
+ # We make a separate function that returns the final
129
+ # indicator array.
130
+ def SMA(series, n):
131
+ from openbacktesting.test import SMA
132
+ return SMA(series, n).reindex(close.index).ffill()
133
+
134
+ # The result equivalent to the short example above:
135
+ self.sma = self.ind(SMA, daily, 10)
136
+
137
+ """
138
+ if func is None:
139
+
140
+ def func(x, *_, **__):
141
+ return x
142
+
143
+ assert callable(func), "resample_apply(func=) must be callable"
144
+
145
+ if not isinstance(series, (pd.Series, pd.DataFrame)):
146
+ assert isinstance(series, (_Array, IndicatorHandle)), (
147
+ "resample_apply(series=) must be `pd.Series`, `pd.DataFrame`, "
148
+ "or a `Strategy.data.*` array"
149
+ )
150
+ series = series.s
151
+
152
+ if agg is None:
153
+ agg = OHLCV_AGG.get(getattr(series, "name", ""), "last")
154
+ if isinstance(series, pd.DataFrame):
155
+ agg = {column: OHLCV_AGG.get(column, "last") for column in series.columns}
156
+
157
+ resampled = series.resample(rule, label="right").agg(agg).dropna()
158
+ resampled.name = _as_str(series) + "[" + rule + "]"
159
+
160
+ # Check first few stack frames if we are being called from
161
+ # inside Strategy.init, and if so, extract Strategy.ind wrapper.
162
+ frame, level = currentframe(), 0
163
+ while frame and level <= 3:
164
+ frame = frame.f_back
165
+ level += 1
166
+ if isinstance(frame.f_locals.get("self"), Strategy): # type: ignore
167
+ strategy_ind = frame.f_locals["self"].ind # type: ignore
168
+ break
169
+ else:
170
+
171
+ def strategy_ind(func, *args, **kwargs):
172
+ return func(*args, **kwargs)
173
+
174
+ def wrap_func(resampled, *args, **kwargs):
175
+ result = func(resampled, *args, **kwargs)
176
+ if not isinstance(result, pd.DataFrame) and not isinstance(result, pd.Series):
177
+ result = np.asarray(result)
178
+ if result.ndim == 1:
179
+ result = pd.Series(result, name=resampled.name)
180
+ elif result.ndim == 2:
181
+ result = pd.DataFrame(result.T)
182
+ # Resample back to data index
183
+ if not isinstance(result.index, pd.DatetimeIndex):
184
+ result.index = resampled.index
185
+ result = result.reindex(index=series.index.union(resampled.index), method="ffill").reindex(
186
+ series.index
187
+ )
188
+ return result
189
+
190
+ wrap_func.__name__ = func.__name__
191
+
192
+ array = strategy_ind(wrap_func, resampled, *args, **kwargs)
193
+ return array
194
+
195
+
196
+ _set_legacy_module(barssince, "openbacktesting.lib")
197
+ _set_legacy_module(quantile, "openbacktesting.lib")
198
+ _set_legacy_module(resample_apply, "openbacktesting.lib")