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.
- openbacktesting/__init__.py +85 -0
- openbacktesting/aggregation.py +8 -0
- openbacktesting/broker/__init__.py +54 -0
- openbacktesting/broker/commission.py +57 -0
- openbacktesting/broker/core.py +1122 -0
- openbacktesting/broker/execution.py +62 -0
- openbacktesting/broker/instrument.py +106 -0
- openbacktesting/broker/ledger.py +60 -0
- openbacktesting/broker/models.py +236 -0
- openbacktesting/broker/observations.py +99 -0
- openbacktesting/broker/requests.py +192 -0
- openbacktesting/broker/sizing.py +76 -0
- openbacktesting/colors.py +175 -0
- openbacktesting/data/__init__.py +20 -0
- openbacktesting/data/aggregation.py +39 -0
- openbacktesting/data/context.py +30 -0
- openbacktesting/data/core.py +218 -0
- openbacktesting/data/generators.py +54 -0
- openbacktesting/data/strategy_data.py +103 -0
- openbacktesting/data/timeframe.py +69 -0
- openbacktesting/engine.py +34 -0
- openbacktesting/helpers.py +77 -0
- openbacktesting/indicator_api.py +10 -0
- openbacktesting/indicator_methods.py +123 -0
- openbacktesting/indicator_types.py +19 -0
- openbacktesting/indicators/__init__.py +7 -0
- openbacktesting/indicators/api.py +97 -0
- openbacktesting/indicators/calculus.py +75 -0
- openbacktesting/indicators/candles/__init__.py +0 -0
- openbacktesting/indicators/candles/cdl_doji.py +17 -0
- openbacktesting/indicators/candles/cdl_engulfing.py +14 -0
- openbacktesting/indicators/candles/cdl_eveningstar.py +15 -0
- openbacktesting/indicators/candles/cdl_hammer.py +14 -0
- openbacktesting/indicators/candles/cdl_harami.py +14 -0
- openbacktesting/indicators/candles/cdl_inside.py +14 -0
- openbacktesting/indicators/candles/cdl_marubozu.py +14 -0
- openbacktesting/indicators/candles/cdl_morningstar.py +15 -0
- openbacktesting/indicators/candles/cdl_shootingstar.py +14 -0
- openbacktesting/indicators/candles/cdl_z.py +28 -0
- openbacktesting/indicators/candles/ha.py +19 -0
- openbacktesting/indicators/catalog.json +8348 -0
- openbacktesting/indicators/common.py +94 -0
- openbacktesting/indicators/cycles/__init__.py +0 -0
- openbacktesting/indicators/cycles/dsp.py +10 -0
- openbacktesting/indicators/cycles/ebsw.py +10 -0
- openbacktesting/indicators/cycles/ht_dcperiod.py +10 -0
- openbacktesting/indicators/cycles/ht_dcphase.py +10 -0
- openbacktesting/indicators/cycles/ht_phasor.py +17 -0
- openbacktesting/indicators/cycles/ht_sine.py +17 -0
- openbacktesting/indicators/cycles/ht_trendmode.py +10 -0
- openbacktesting/indicators/cycles/msw.py +19 -0
- openbacktesting/indicators/methods.py +5762 -0
- openbacktesting/indicators/momentum/__init__.py +0 -0
- openbacktesting/indicators/momentum/ac.py +9 -0
- openbacktesting/indicators/momentum/ao.py +10 -0
- openbacktesting/indicators/momentum/apo.py +10 -0
- openbacktesting/indicators/momentum/bias.py +10 -0
- openbacktesting/indicators/momentum/bop.py +10 -0
- openbacktesting/indicators/momentum/brar.py +26 -0
- openbacktesting/indicators/momentum/cci.py +10 -0
- openbacktesting/indicators/momentum/cfo.py +10 -0
- openbacktesting/indicators/momentum/cg.py +10 -0
- openbacktesting/indicators/momentum/cmo.py +10 -0
- openbacktesting/indicators/momentum/coppock.py +10 -0
- openbacktesting/indicators/momentum/crsi.py +28 -0
- openbacktesting/indicators/momentum/cti.py +10 -0
- openbacktesting/indicators/momentum/dm.py +17 -0
- openbacktesting/indicators/momentum/er.py +10 -0
- openbacktesting/indicators/momentum/eri.py +17 -0
- openbacktesting/indicators/momentum/fisher.py +17 -0
- openbacktesting/indicators/momentum/fosc.py +10 -0
- openbacktesting/indicators/momentum/gator.py +13 -0
- openbacktesting/indicators/momentum/ibs.py +7 -0
- openbacktesting/indicators/momentum/inertia.py +22 -0
- openbacktesting/indicators/momentum/kdj.py +18 -0
- openbacktesting/indicators/momentum/kst.py +30 -0
- openbacktesting/indicators/momentum/lrsi.py +10 -0
- openbacktesting/indicators/momentum/macd.py +27 -0
- openbacktesting/indicators/momentum/macdext.py +27 -0
- openbacktesting/indicators/momentum/macdfix.py +18 -0
- openbacktesting/indicators/momentum/mom.py +10 -0
- openbacktesting/indicators/momentum/pgo.py +10 -0
- openbacktesting/indicators/momentum/pmo.py +13 -0
- openbacktesting/indicators/momentum/po.py +10 -0
- openbacktesting/indicators/momentum/ppo.py +20 -0
- openbacktesting/indicators/momentum/psl.py +12 -0
- openbacktesting/indicators/momentum/pvo.py +20 -0
- openbacktesting/indicators/momentum/qqe.py +31 -0
- openbacktesting/indicators/momentum/rci.py +20 -0
- openbacktesting/indicators/momentum/rmi.py +7 -0
- openbacktesting/indicators/momentum/roc.py +10 -0
- openbacktesting/indicators/momentum/rocp.py +10 -0
- openbacktesting/indicators/momentum/rocr.py +10 -0
- openbacktesting/indicators/momentum/rocr100.py +10 -0
- openbacktesting/indicators/momentum/rsi.py +10 -0
- openbacktesting/indicators/momentum/rsx.py +10 -0
- openbacktesting/indicators/momentum/rvgi.py +26 -0
- openbacktesting/indicators/momentum/slope.py +17 -0
- openbacktesting/indicators/momentum/smc_sweep.py +18 -0
- openbacktesting/indicators/momentum/smi.py +21 -0
- openbacktesting/indicators/momentum/smi_stochastic.py +15 -0
- openbacktesting/indicators/momentum/squeeze.py +47 -0
- openbacktesting/indicators/momentum/squeeze_pro.py +53 -0
- openbacktesting/indicators/momentum/stc.py +20 -0
- openbacktesting/indicators/momentum/stoch.py +19 -0
- openbacktesting/indicators/momentum/stochf.py +19 -0
- openbacktesting/indicators/momentum/stochrsi.py +19 -0
- openbacktesting/indicators/momentum/td_seq.py +17 -0
- openbacktesting/indicators/momentum/trix.py +19 -0
- openbacktesting/indicators/momentum/trixh.py +20 -0
- openbacktesting/indicators/momentum/tsi.py +26 -0
- openbacktesting/indicators/momentum/uo.py +22 -0
- openbacktesting/indicators/momentum/vwmacd.py +20 -0
- openbacktesting/indicators/momentum/wavetrend.py +15 -0
- openbacktesting/indicators/momentum/willr.py +10 -0
- openbacktesting/indicators/overlap/__init__.py +0 -0
- openbacktesting/indicators/overlap/alligator.py +13 -0
- openbacktesting/indicators/overlap/alma.py +33 -0
- openbacktesting/indicators/overlap/anchored_vwap.py +50 -0
- openbacktesting/indicators/overlap/anchored_vwap_bands.py +13 -0
- openbacktesting/indicators/overlap/avgprice.py +10 -0
- openbacktesting/indicators/overlap/dema.py +10 -0
- openbacktesting/indicators/overlap/ema.py +12 -0
- openbacktesting/indicators/overlap/frama.py +29 -0
- openbacktesting/indicators/overlap/fwma.py +10 -0
- openbacktesting/indicators/overlap/hilo.py +26 -0
- openbacktesting/indicators/overlap/hl2.py +10 -0
- openbacktesting/indicators/overlap/hlc3.py +10 -0
- openbacktesting/indicators/overlap/hma.py +10 -0
- openbacktesting/indicators/overlap/ht_trendline.py +10 -0
- openbacktesting/indicators/overlap/hwma.py +10 -0
- openbacktesting/indicators/overlap/ichimoku.py +44 -0
- openbacktesting/indicators/overlap/jma.py +10 -0
- openbacktesting/indicators/overlap/kama.py +10 -0
- openbacktesting/indicators/overlap/linreg.py +15 -0
- openbacktesting/indicators/overlap/linregangle.py +10 -0
- openbacktesting/indicators/overlap/linregintercept.py +10 -0
- openbacktesting/indicators/overlap/linregslope.py +10 -0
- openbacktesting/indicators/overlap/ma.py +10 -0
- openbacktesting/indicators/overlap/ma_envelopes.py +16 -0
- openbacktesting/indicators/overlap/mama.py +17 -0
- openbacktesting/indicators/overlap/mavp.py +17 -0
- openbacktesting/indicators/overlap/mcgd.py +10 -0
- openbacktesting/indicators/overlap/medprice.py +10 -0
- openbacktesting/indicators/overlap/midpoint.py +10 -0
- openbacktesting/indicators/overlap/midprice.py +10 -0
- openbacktesting/indicators/overlap/mmar.py +21 -0
- openbacktesting/indicators/overlap/ohlc4.py +10 -0
- openbacktesting/indicators/overlap/pwma.py +10 -0
- openbacktesting/indicators/overlap/rainbow.py +25 -0
- openbacktesting/indicators/overlap/rma.py +10 -0
- openbacktesting/indicators/overlap/sinwma.py +10 -0
- openbacktesting/indicators/overlap/sma.py +14 -0
- openbacktesting/indicators/overlap/ssf.py +10 -0
- openbacktesting/indicators/overlap/supertrend.py +21 -0
- openbacktesting/indicators/overlap/swma.py +10 -0
- openbacktesting/indicators/overlap/t3.py +10 -0
- openbacktesting/indicators/overlap/tema.py +10 -0
- openbacktesting/indicators/overlap/trima.py +10 -0
- openbacktesting/indicators/overlap/tsf.py +10 -0
- openbacktesting/indicators/overlap/typprice.py +10 -0
- openbacktesting/indicators/overlap/vidya.py +10 -0
- openbacktesting/indicators/overlap/vwap.py +10 -0
- openbacktesting/indicators/overlap/vwma.py +10 -0
- openbacktesting/indicators/overlap/wcp.py +10 -0
- openbacktesting/indicators/overlap/wma.py +10 -0
- openbacktesting/indicators/overlap/zlma.py +10 -0
- openbacktesting/indicators/performance/__init__.py +0 -0
- openbacktesting/indicators/performance/drawdown.py +18 -0
- openbacktesting/indicators/performance/log_return.py +10 -0
- openbacktesting/indicators/performance/percent_return.py +10 -0
- openbacktesting/indicators/predicates.py +88 -0
- openbacktesting/indicators/registry.py +571 -0
- openbacktesting/indicators/runtime.py +290 -0
- openbacktesting/indicators/statistics/__init__.py +0 -0
- openbacktesting/indicators/statistics/beta.py +10 -0
- openbacktesting/indicators/statistics/correl.py +10 -0
- openbacktesting/indicators/statistics/entropy.py +10 -0
- openbacktesting/indicators/statistics/kurtosis.py +10 -0
- openbacktesting/indicators/statistics/mad.py +10 -0
- openbacktesting/indicators/statistics/md.py +10 -0
- openbacktesting/indicators/statistics/median.py +10 -0
- openbacktesting/indicators/statistics/mode.py +48 -0
- openbacktesting/indicators/statistics/quantile.py +10 -0
- openbacktesting/indicators/statistics/skew.py +10 -0
- openbacktesting/indicators/statistics/stderr.py +10 -0
- openbacktesting/indicators/statistics/stdev.py +10 -0
- openbacktesting/indicators/statistics/variance.py +10 -0
- openbacktesting/indicators/statistics/zscore.py +10 -0
- openbacktesting/indicators/trend/__init__.py +0 -0
- openbacktesting/indicators/trend/adx.py +28 -0
- openbacktesting/indicators/trend/adxr.py +28 -0
- openbacktesting/indicators/trend/amat.py +19 -0
- openbacktesting/indicators/trend/aroon.py +18 -0
- openbacktesting/indicators/trend/atr_stop.py +33 -0
- openbacktesting/indicators/trend/chop.py +20 -0
- openbacktesting/indicators/trend/cksp.py +17 -0
- openbacktesting/indicators/trend/cpr.py +57 -0
- openbacktesting/indicators/trend/decay.py +10 -0
- openbacktesting/indicators/trend/decreasing.py +18 -0
- openbacktesting/indicators/trend/dpo.py +12 -0
- openbacktesting/indicators/trend/dx.py +19 -0
- openbacktesting/indicators/trend/edecay.py +10 -0
- openbacktesting/indicators/trend/increasing.py +18 -0
- openbacktesting/indicators/trend/long_run.py +10 -0
- openbacktesting/indicators/trend/minus_dm.py +10 -0
- openbacktesting/indicators/trend/pivot_points.py +89 -0
- openbacktesting/indicators/trend/pivothigh.py +23 -0
- openbacktesting/indicators/trend/pivotlow.py +23 -0
- openbacktesting/indicators/trend/plus_dm.py +10 -0
- openbacktesting/indicators/trend/pmax.py +18 -0
- openbacktesting/indicators/trend/psar.py +19 -0
- openbacktesting/indicators/trend/qstick.py +10 -0
- openbacktesting/indicators/trend/sarext.py +35 -0
- openbacktesting/indicators/trend/short_run.py +10 -0
- openbacktesting/indicators/trend/tsignals.py +26 -0
- openbacktesting/indicators/trend/ttm_trend.py +16 -0
- openbacktesting/indicators/trend/vhf.py +10 -0
- openbacktesting/indicators/trend/volatility_stop.py +33 -0
- openbacktesting/indicators/trend/vortex.py +17 -0
- openbacktesting/indicators/trend/xsignals.py +29 -0
- openbacktesting/indicators/types.py +344 -0
- openbacktesting/indicators/utility/__init__.py +0 -0
- openbacktesting/indicators/utility/barssince.py +21 -0
- openbacktesting/indicators/utility/change.py +7 -0
- openbacktesting/indicators/utility/cross.py +12 -0
- openbacktesting/indicators/utility/crossover.py +11 -0
- openbacktesting/indicators/utility/crossunder.py +11 -0
- openbacktesting/indicators/utility/cum.py +7 -0
- openbacktesting/indicators/utility/falling.py +10 -0
- openbacktesting/indicators/utility/highest.py +7 -0
- openbacktesting/indicators/utility/highestbars.py +29 -0
- openbacktesting/indicators/utility/lowest.py +7 -0
- openbacktesting/indicators/utility/lowestbars.py +29 -0
- openbacktesting/indicators/utility/percentile_linear_interpolation.py +12 -0
- openbacktesting/indicators/utility/percentile_nearest_rank.py +16 -0
- openbacktesting/indicators/utility/percentrank.py +7 -0
- openbacktesting/indicators/utility/range.py +8 -0
- openbacktesting/indicators/utility/rising.py +10 -0
- openbacktesting/indicators/utility/rolling_count.py +12 -0
- openbacktesting/indicators/utility/rolling_sum.py +7 -0
- openbacktesting/indicators/utility/running_max.py +7 -0
- openbacktesting/indicators/utility/running_min.py +7 -0
- openbacktesting/indicators/utility/shift.py +7 -0
- openbacktesting/indicators/utility/valuewhen.py +25 -0
- openbacktesting/indicators/volatility/__init__.py +0 -0
- openbacktesting/indicators/volatility/aberration.py +21 -0
- openbacktesting/indicators/volatility/accbands.py +20 -0
- openbacktesting/indicators/volatility/adr.py +7 -0
- openbacktesting/indicators/volatility/atr.py +12 -0
- openbacktesting/indicators/volatility/avolume.py +10 -0
- openbacktesting/indicators/volatility/bbands.py +20 -0
- openbacktesting/indicators/volatility/ce.py +25 -0
- openbacktesting/indicators/volatility/cvi.py +10 -0
- openbacktesting/indicators/volatility/donchian.py +20 -0
- openbacktesting/indicators/volatility/hvol.py +10 -0
- openbacktesting/indicators/volatility/hwc.py +27 -0
- openbacktesting/indicators/volatility/kc.py +27 -0
- openbacktesting/indicators/volatility/massi.py +10 -0
- openbacktesting/indicators/volatility/natr.py +19 -0
- openbacktesting/indicators/volatility/pdist.py +10 -0
- openbacktesting/indicators/volatility/rvi.py +21 -0
- openbacktesting/indicators/volatility/starc.py +11 -0
- openbacktesting/indicators/volatility/thermo.py +28 -0
- openbacktesting/indicators/volatility/true_range.py +31 -0
- openbacktesting/indicators/volatility/ui.py +10 -0
- openbacktesting/indicators/volume/__init__.py +0 -0
- openbacktesting/indicators/volume/ad.py +10 -0
- openbacktesting/indicators/volume/adosc.py +19 -0
- openbacktesting/indicators/volume/aobv.py +31 -0
- openbacktesting/indicators/volume/cmf.py +12 -0
- openbacktesting/indicators/volume/efi.py +12 -0
- openbacktesting/indicators/volume/emv.py +10 -0
- openbacktesting/indicators/volume/eom.py +19 -0
- openbacktesting/indicators/volume/iii.py +9 -0
- openbacktesting/indicators/volume/kvo.py +28 -0
- openbacktesting/indicators/volume/marketfi.py +10 -0
- openbacktesting/indicators/volume/mfi.py +12 -0
- openbacktesting/indicators/volume/nvi.py +26 -0
- openbacktesting/indicators/volume/obv.py +10 -0
- openbacktesting/indicators/volume/pvi.py +26 -0
- openbacktesting/indicators/volume/pvol.py +10 -0
- openbacktesting/indicators/volume/pvr.py +10 -0
- openbacktesting/indicators/volume/pvt.py +10 -0
- openbacktesting/indicators/volume/rvol.py +8 -0
- openbacktesting/indicators/volume/vfi.py +18 -0
- openbacktesting/indicators/volume/vosc.py +10 -0
- openbacktesting/indicators/volume/wad.py +10 -0
- openbacktesting/indicators/volume/wvad.py +9 -0
- openbacktesting/inputs.py +51 -0
- openbacktesting/lib.py +31 -0
- openbacktesting/logs.py +129 -0
- openbacktesting/optimization.py +8 -0
- openbacktesting/optimizer/__init__.py +25 -0
- openbacktesting/optimizer/_semantics.py +155 -0
- openbacktesting/optimizer/identity.py +45 -0
- openbacktesting/optimizer/models.py +92 -0
- openbacktesting/optimizer/plan.py +129 -0
- openbacktesting/optimizer/portfolio-result.schema.json +4984 -0
- openbacktesting/optimizer/query.py +257 -0
- openbacktesting/optimizer/runner.py +98 -0
- openbacktesting/optimizer/storage.py +261 -0
- openbacktesting/optimizer/validation.py +64 -0
- openbacktesting/parallel.py +9 -0
- openbacktesting/plotting/__init__.py +23 -0
- openbacktesting/plotting/collector.py +509 -0
- openbacktesting/plotting/encoding.py +102 -0
- openbacktesting/plotting/types.py +92 -0
- openbacktesting/portfolio/__init__.py +15 -0
- openbacktesting/portfolio/analysis.py +245 -0
- openbacktesting/portfolio/builder.py +381 -0
- openbacktesting/portfolio/catalog.json +1404 -0
- openbacktesting/portfolio/helpers.py +92 -0
- openbacktesting/portfolio/metrics.py +186 -0
- openbacktesting/portfolio/models.py +63 -0
- openbacktesting/portfolio/periods.py +164 -0
- openbacktesting/portfolio/rates.py +14 -0
- openbacktesting/portfolio/snapshot.py +111 -0
- openbacktesting/portfolio/stats.py +272 -0
- openbacktesting/portfolio/trades.py +136 -0
- openbacktesting/portfolio/trading.py +215 -0
- openbacktesting/py.typed +0 -0
- openbacktesting/runtime.py +330 -0
- openbacktesting/simulation/__init__.py +17 -0
- openbacktesting/simulation/collection.py +13 -0
- openbacktesting/simulation/config.py +241 -0
- openbacktesting/simulation/core.py +528 -0
- openbacktesting/simulation/optimization.py +326 -0
- openbacktesting/simulation/parallel.py +145 -0
- openbacktesting/simulation/variants.py +107 -0
- openbacktesting/strategy/__init__.py +19 -0
- openbacktesting/strategy/bar_state.py +14 -0
- openbacktesting/strategy/base.py +810 -0
- openbacktesting/strategy/declaration.py +90 -0
- openbacktesting/strategy/helpers.py +198 -0
- openbacktesting/strategy/inputs.py +460 -0
- openbacktesting/strategy/sizing.py +80 -0
- openbacktesting/strategy/templates.py +160 -0
- openbacktesting/strategy_data.py +10 -0
- openbacktesting/test/BTCUSD.csv +157 -0
- openbacktesting/test/EURUSD.csv +5001 -0
- openbacktesting/test/GOOG.csv +2149 -0
- openbacktesting/test/__init__.py +29 -0
- openbacktesting/test/__main__.py +11 -0
- openbacktesting/test/_test.py +1312 -0
- openbacktesting/util.py +31 -0
- openbacktesting-0.2.0.dist-info/METADATA +151 -0
- openbacktesting-0.2.0.dist-info/RECORD +350 -0
- openbacktesting-0.2.0.dist-info/WHEEL +4 -0
- 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")
|