back-trader-python 1.4.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.
- back_trader_python-1.4.0.dist-info/METADATA +1491 -0
- back_trader_python-1.4.0.dist-info/RECORD +465 -0
- back_trader_python-1.4.0.dist-info/WHEEL +5 -0
- back_trader_python-1.4.0.dist-info/licenses/LICENSE +674 -0
- back_trader_python-1.4.0.dist-info/top_level.txt +1 -0
- backtrader/__init__.py +148 -0
- backtrader/_cerebro/__init__.py +5 -0
- backtrader/_cerebro/channel.py +382 -0
- backtrader/_cerebro/execution.py +377 -0
- backtrader/_cerebro/lifecycle.py +143 -0
- backtrader/_cerebro/notifications.py +150 -0
- backtrader/_cerebro/presentation.py +230 -0
- backtrader/_cerebro/registry.py +593 -0
- backtrader/_cerebro/runnext.py +551 -0
- backtrader/_cerebro/runonce.py +142 -0
- backtrader/analyzer.py +594 -0
- backtrader/analyzers/__init__.py +50 -0
- backtrader/analyzers/annualreturn.py +226 -0
- backtrader/analyzers/calmar.py +165 -0
- backtrader/analyzers/drawdown.py +287 -0
- backtrader/analyzers/leverage.py +112 -0
- backtrader/analyzers/logreturnsrolling.py +190 -0
- backtrader/analyzers/periodstats.py +153 -0
- backtrader/analyzers/positions.py +119 -0
- backtrader/analyzers/pyfolio.py +470 -0
- backtrader/analyzers/returns.py +192 -0
- backtrader/analyzers/sharpe.py +307 -0
- backtrader/analyzers/sharpe_ratio_stats.py +534 -0
- backtrader/analyzers/sqn.py +112 -0
- backtrader/analyzers/timereturn.py +192 -0
- backtrader/analyzers/total_value.py +75 -0
- backtrader/analyzers/tradeanalyzer.py +278 -0
- backtrader/analyzers/transactions.py +141 -0
- backtrader/analyzers/vwr.py +245 -0
- backtrader/bokeh/__init__.py +155 -0
- backtrader/bokeh/analyzers/__init__.py +13 -0
- backtrader/bokeh/analyzers/plot.py +192 -0
- backtrader/bokeh/analyzers/recorder.py +181 -0
- backtrader/bokeh/app.py +1094 -0
- backtrader/bokeh/live/__init__.py +11 -0
- backtrader/bokeh/live/client.py +352 -0
- backtrader/bokeh/live/datahandler.py +346 -0
- backtrader/bokeh/plot_adapter.py +200 -0
- backtrader/bokeh/schemes/__init__.py +14 -0
- backtrader/bokeh/schemes/blackly.py +76 -0
- backtrader/bokeh/schemes/scheme.py +150 -0
- backtrader/bokeh/schemes/tradimo.py +82 -0
- backtrader/bokeh/tab.py +125 -0
- backtrader/bokeh/tabs/__init__.py +30 -0
- backtrader/bokeh/tabs/analyzer.py +120 -0
- backtrader/bokeh/tabs/config.py +154 -0
- backtrader/bokeh/tabs/live.py +109 -0
- backtrader/bokeh/tabs/log.py +185 -0
- backtrader/bokeh/tabs/metadata.py +182 -0
- backtrader/bokeh/tabs/performance.py +359 -0
- backtrader/bokeh/tabs/source.py +70 -0
- backtrader/bokeh/utils/__init__.py +8 -0
- backtrader/bokeh/utils/helpers.py +167 -0
- backtrader/bokeh/webapp.py +164 -0
- backtrader/broker.py +478 -0
- backtrader/brokers/__init__.py +36 -0
- backtrader/brokers/bbroker.py +2576 -0
- backtrader/brokers/btapibroker.py +8227 -0
- backtrader/brokers/hft/__init__.py +89 -0
- backtrader/brokers/hft/binance_bbo.py +625 -0
- backtrader/brokers/hft/binance_bbo_compare.py +1398 -0
- backtrader/brokers/hft/examples.py +1228 -0
- backtrader/brokers/hft/exchange.py +380 -0
- backtrader/brokers/hft/latency.py +309 -0
- backtrader/brokers/hft/matching_core.py +572 -0
- backtrader/brokers/hft/queue.py +238 -0
- backtrader/brokers/hft/recorder.py +88 -0
- backtrader/brokers/hft/state.py +138 -0
- backtrader/brokers/impact_models.py +118 -0
- backtrader/brokers/mixbroker.py +895 -0
- backtrader/brokers/tickbroker.py +1991 -0
- backtrader/btrun/__init__.py +12 -0
- backtrader/btrun/btrun.py +1218 -0
- backtrader/cerebro.py +828 -0
- backtrader/channel.py +682 -0
- backtrader/channels/__init__.py +23 -0
- backtrader/channels/bridge.py +186 -0
- backtrader/channels/funding.py +248 -0
- backtrader/channels/live_queue.py +216 -0
- backtrader/channels/live_validator.py +294 -0
- backtrader/channels/orderbook.py +257 -0
- backtrader/channels/tick.py +202 -0
- backtrader/comminfo.py +665 -0
- backtrader/commissions/__init__.py +106 -0
- backtrader/commissions/ctpoption.py +993 -0
- backtrader/configs/account_config_example.yaml +8 -0
- backtrader/dataseries.py +379 -0
- backtrader/errors.py +106 -0
- backtrader/events.py +980 -0
- backtrader/feed.py +1523 -0
- backtrader/feeds/__init__.py +75 -0
- backtrader/feeds/barrier.py +2006 -0
- backtrader/feeds/blaze.py +118 -0
- backtrader/feeds/btapifeed.py +1538 -0
- backtrader/feeds/btcsv.py +203 -0
- backtrader/feeds/chainer.py +114 -0
- backtrader/feeds/cryptohftdata.py +164 -0
- backtrader/feeds/csvgeneric.py +1205 -0
- backtrader/feeds/ctpcohort.py +1051 -0
- backtrader/feeds/influxfeed.py +158 -0
- backtrader/feeds/livefeed.py +71 -0
- backtrader/feeds/mixed_channel.py +108 -0
- backtrader/feeds/mt4csv.py +42 -0
- backtrader/feeds/pandafeed.py +381 -0
- backtrader/feeds/quandl.py +256 -0
- backtrader/feeds/rollover.py +229 -0
- backtrader/feeds/sierrachart.py +30 -0
- backtrader/feeds/vchart.py +162 -0
- backtrader/feeds/vchartcsv.py +84 -0
- backtrader/feeds/vchartfile.py +153 -0
- backtrader/feeds/yahoo.py +399 -0
- backtrader/fillers.py +148 -0
- backtrader/filters/__init__.py +34 -0
- backtrader/filters/bsplitter.py +127 -0
- backtrader/filters/calendardays.py +121 -0
- backtrader/filters/datafiller.py +192 -0
- backtrader/filters/datafilter.py +74 -0
- backtrader/filters/daysteps.py +96 -0
- backtrader/filters/heikinashi.py +63 -0
- backtrader/filters/renko.py +164 -0
- backtrader/filters/session.py +289 -0
- backtrader/flt.py +80 -0
- backtrader/functions.py +960 -0
- backtrader/indicator.py +449 -0
- backtrader/indicators/__init__.py +148 -0
- backtrader/indicators/accdecoscillator.py +110 -0
- backtrader/indicators/aroon.py +300 -0
- backtrader/indicators/atr.py +315 -0
- backtrader/indicators/awesomeoscillator.py +122 -0
- backtrader/indicators/basicops.py +834 -0
- backtrader/indicators/bollinger.py +223 -0
- backtrader/indicators/cci.py +89 -0
- backtrader/indicators/channels_ext.py +83 -0
- backtrader/indicators/contrib/__init__.py +228 -0
- backtrader/indicators/contrib/absolutely_no_lag_lwma.py +28 -0
- backtrader/indicators/contrib/absolutely_no_lag_lwma_color.py +44 -0
- backtrader/indicators/contrib/accumulation_distribution_line.py +92 -0
- backtrader/indicators/contrib/adx_cross_hull_style_indicator.py +249 -0
- backtrader/indicators/contrib/adxdmi.py +34 -0
- backtrader/indicators/contrib/ai_acceleration_deceleration_oscillator.py +34 -0
- backtrader/indicators/contrib/altr_trend_signal_v22.py +85 -0
- backtrader/indicators/contrib/anchored_momentum_line.py +115 -0
- backtrader/indicators/contrib/any_range_cld_tail_indicator.py +82 -0
- backtrader/indicators/contrib/aroon_horn_sign_indicator.py +96 -0
- backtrader/indicators/contrib/aroon_oscillator_sign_alert.py +50 -0
- backtrader/indicators/contrib/arrows_curves_indicator.py +112 -0
- backtrader/indicators/contrib/as_ctrend_indicator.py +143 -0
- backtrader/indicators/contrib/asimmetric_stoch_nr_indicator.py +187 -0
- backtrader/indicators/contrib/atr_normalize_histogram.py +118 -0
- backtrader/indicators/contrib/average_change_candle.py +165 -0
- backtrader/indicators/contrib/bb_squeeze_indicator.py +60 -0
- backtrader/indicators/contrib/bezier_st_dev_indicator.py +135 -0
- backtrader/indicators/contrib/binary_wave_indicator.py +233 -0
- backtrader/indicators/contrib/blau_c_momentum_indicator.py +123 -0
- backtrader/indicators/contrib/blau_cmi_indicator.py +141 -0
- backtrader/indicators/contrib/blau_csi.py +76 -0
- backtrader/indicators/contrib/blau_ergodic.py +53 -0
- backtrader/indicators/contrib/blau_t_stoch_i.py +72 -0
- backtrader/indicators/contrib/blau_ts_stochastic.py +85 -0
- backtrader/indicators/contrib/blau_tvi.py +55 -0
- backtrader/indicators/contrib/brain_trend2_indicator.py +128 -0
- backtrader/indicators/contrib/brain_trend_signal_proxy.py +47 -0
- backtrader/indicators/contrib/brake_parb_indicator.py +85 -0
- backtrader/indicators/contrib/breakout_bars_trend_v2.py +121 -0
- backtrader/indicators/contrib/bsi_indicator.py +87 -0
- backtrader/indicators/contrib/bulls_bears_eyes.py +67 -0
- backtrader/indicators/contrib/bulls_power.py +56 -0
- backtrader/indicators/contrib/bw_wise_man1_signal.py +102 -0
- backtrader/indicators/contrib/bykov_trend_indicator.py +85 -0
- backtrader/indicators/contrib/candle_stop_color.py +46 -0
- backtrader/indicators/contrib/candles_x_smoothed_indicator.py +69 -0
- backtrader/indicators/contrib/candlesticks_bw.py +45 -0
- backtrader/indicators/contrib/caudate_x_period_candle_color.py +56 -0
- backtrader/indicators/contrib/cci_histogram_indicator.py +53 -0
- backtrader/indicators/contrib/cci_woodies_indicator.py +80 -0
- backtrader/indicators/contrib/center_of_gravity_candle_indicator.py +83 -0
- backtrader/indicators/contrib/center_of_gravity_indicator.py +70 -0
- backtrader/indicators/contrib/cg_oscillator.py +40 -0
- backtrader/indicators/contrib/close_line_cci.py +38 -0
- backtrader/indicators/contrib/close_price_fractals.py +47 -0
- backtrader/indicators/contrib/color3rd_gen_xma_indicator.py +122 -0
- backtrader/indicators/contrib/color_bb_candles_indicator.py +108 -0
- backtrader/indicators/contrib/color_coppock_indicator.py +157 -0
- backtrader/indicators/contrib/color_hma.py +71 -0
- backtrader/indicators/contrib/color_j_variation_indicator.py +53 -0
- backtrader/indicators/contrib/color_metro_de_marker_indicator.py +78 -0
- backtrader/indicators/contrib/color_metro_stochastic_indicator.py +93 -0
- backtrader/indicators/contrib/color_metro_wpr_indicator.py +85 -0
- backtrader/indicators/contrib/color_schaff_de_marker_trend_cycle.py +92 -0
- backtrader/indicators/contrib/color_schaff_trend_cycle_indicator.py +203 -0
- backtrader/indicators/contrib/color_step_xccx_indicator.py +193 -0
- backtrader/indicators/contrib/color_x2_ma.py +49 -0
- backtrader/indicators/contrib/color_x_derivative.py +63 -0
- backtrader/indicators/contrib/color_zerolag_de_marker.py +84 -0
- backtrader/indicators/contrib/corrected_average_indicator.py +127 -0
- backtrader/indicators/contrib/darvas_boxes_system.py +73 -0
- backtrader/indicators/contrib/dema_range_channel_color.py +42 -0
- backtrader/indicators/contrib/derivative_indicator.py +95 -0
- backtrader/indicators/contrib/digital_ft01_indicator.py +112 -0
- backtrader/indicators/contrib/digital_macd.py +200 -0
- backtrader/indicators/contrib/donchian_channels_system.py +45 -0
- backtrader/indicators/contrib/dots_indicator.py +93 -0
- backtrader/indicators/contrib/ef_distance_indicator.py +82 -0
- backtrader/indicators/contrib/ema_rsi_va.py +80 -0
- backtrader/indicators/contrib/envelopes_jp_alonso.py +32 -0
- backtrader/indicators/contrib/f2a_ao_indicator.py +120 -0
- backtrader/indicators/contrib/fatl_filter.py +179 -0
- backtrader/indicators/contrib/fibo_candles_indicator.py +78 -0
- backtrader/indicators/contrib/fine_tuning_ma.py +100 -0
- backtrader/indicators/contrib/fisher_org_v1.py +102 -0
- backtrader/indicators/contrib/fisher_org_v1_sign.py +118 -0
- backtrader/indicators/contrib/force_index_ema.py +96 -0
- backtrader/indicators/contrib/force_index_ema_2.py +27 -0
- backtrader/indicators/contrib/forecast_oscilator.py +145 -0
- backtrader/indicators/contrib/fractal_amambk.py +81 -0
- backtrader/indicators/contrib/frama_series.py +84 -0
- backtrader/indicators/contrib/frasm_av2_indicator.py +104 -0
- backtrader/indicators/contrib/go_indicator.py +93 -0
- backtrader/indicators/contrib/hlr_indicator.py +95 -0
- backtrader/indicators/contrib/hma.py +50 -0
- backtrader/indicators/contrib/i4_drfv2.py +34 -0
- backtrader/indicators/contrib/i4_drfv3.py +38 -0
- backtrader/indicators/contrib/i_anch_mom_indicator.py +72 -0
- backtrader/indicators/contrib/i_de_marker_sign_indicator.py +64 -0
- backtrader/indicators/contrib/i_gap_indicator.py +45 -0
- backtrader/indicators/contrib/i_stoch_komposter_indicator.py +77 -0
- backtrader/indicators/contrib/i_trend_indicator.py +125 -0
- backtrader/indicators/contrib/iamma_indicator.py +39 -0
- backtrader/indicators/contrib/indexed_moving_average.py +33 -0
- backtrader/indicators/contrib/instantaneous_trend_filter_indicator.py +51 -0
- backtrader/indicators/contrib/inverse_reaction_indicator.py +41 -0
- backtrader/indicators/contrib/irsi_sign_indicator.py +95 -0
- backtrader/indicators/contrib/iwpr_sign_indicator.py +59 -0
- backtrader/indicators/contrib/j_brain_trend1_sig_indicator.py +233 -0
- backtrader/indicators/contrib/j_tpo_proxy.py +32 -0
- backtrader/indicators/contrib/jma_slope_indicator.py +73 -0
- backtrader/indicators/contrib/kalman_filter_indicator.py +119 -0
- backtrader/indicators/contrib/kalman_filter_line.py +127 -0
- backtrader/indicators/contrib/kama_indicator.py +150 -0
- backtrader/indicators/contrib/karacatica_indicator.py +99 -0
- backtrader/indicators/contrib/kdj_indicator.py +59 -0
- backtrader/indicators/contrib/kwan_ccc_indicator.py +195 -0
- backtrader/indicators/contrib/kwan_nrp_indicator.py +113 -0
- backtrader/indicators/contrib/kwan_rdp_indicator.py +192 -0
- backtrader/indicators/contrib/laguerre_adx_indicator.py +85 -0
- backtrader/indicators/contrib/laguerre_filter_indicator.py +66 -0
- backtrader/indicators/contrib/laguerre_plus_di_proxy.py +57 -0
- backtrader/indicators/contrib/laguerre_roc_indicator.py +81 -0
- backtrader/indicators/contrib/le_man_signal_indicator.py +63 -0
- backtrader/indicators/contrib/linear_reg_slope_v2_indicator.py +136 -0
- backtrader/indicators/contrib/loco_indicator.py +88 -0
- backtrader/indicators/contrib/lrma_indicator.py +185 -0
- backtrader/indicators/contrib/lsma_angle_indicator.py +106 -0
- backtrader/indicators/contrib/ma_rounding_channel_indicator.py +149 -0
- backtrader/indicators/contrib/macd2_indicator.py +61 -0
- backtrader/indicators/contrib/macd_candle_indicator.py +80 -0
- backtrader/indicators/contrib/malr_indicator.py +77 -0
- backtrader/indicators/contrib/momentum_candle_sign_indicator.py +51 -0
- backtrader/indicators/contrib/moving_average_fn_indicator.py +139 -0
- backtrader/indicators/contrib/mt5_stochastic_close_close.py +57 -0
- backtrader/indicators/contrib/muv_nor_diff_cloud_indicator.py +107 -0
- backtrader/indicators/contrib/non_lag_dot_indicator.py +124 -0
- backtrader/indicators/contrib/nrtr_extr_indicator.py +95 -0
- backtrader/indicators/contrib/nrtr_indicator.py +95 -0
- backtrader/indicators/contrib/p_channel_system.py +40 -0
- backtrader/indicators/contrib/percent_envelope.py +37 -0
- backtrader/indicators/contrib/percentage_crossover_channel.py +47 -0
- backtrader/indicators/contrib/pivot_zig_zag_proxy.py +47 -0
- backtrader/indicators/contrib/price_channel_stop_indicator.py +104 -0
- backtrader/indicators/contrib/price_extreme_channel.py +35 -0
- backtrader/indicators/contrib/qqe_cloud_indicator.py +129 -0
- backtrader/indicators/contrib/ravi_indicator.py +40 -0
- backtrader/indicators/contrib/raw_close_close_stochastic.py +74 -0
- backtrader/indicators/contrib/rd_trend_trigger_indicator.py +51 -0
- backtrader/indicators/contrib/renko_level.py +85 -0
- backtrader/indicators/contrib/renko_line_break.py +91 -0
- backtrader/indicators/contrib/rftl_indicator.py +41 -0
- backtrader/indicators/contrib/rkd_indicator.py +53 -0
- backtrader/indicators/contrib/roc2_vg_indicator.py +68 -0
- backtrader/indicators/contrib/rsi_histogram_indicator.py +43 -0
- backtrader/indicators/contrib/rsi_slowdown.py +57 -0
- backtrader/indicators/contrib/rsioma_v2.py +41 -0
- backtrader/indicators/contrib/rvi_histogram_indicator.py +107 -0
- backtrader/indicators/contrib/safe_adx.py +89 -0
- backtrader/indicators/contrib/shared_strategy_indicators.py +1651 -0
- backtrader/indicators/contrib/sidus_indicator.py +105 -0
- backtrader/indicators/contrib/silver_trend_indicator.py +79 -0
- backtrader/indicators/contrib/sliding_range_color.py +56 -0
- backtrader/indicators/contrib/slow_stoch.py +42 -0
- backtrader/indicators/contrib/smoothed_adx_indicator.py +86 -0
- backtrader/indicators/contrib/smoothed_rsi.py +31 -0
- backtrader/indicators/contrib/spearman_rank_correlation_histogram.py +60 -0
- backtrader/indicators/contrib/stalin_indicator.py +152 -0
- backtrader/indicators/contrib/starter_laguerre_filter.py +62 -0
- backtrader/indicators/contrib/step_manrtr_indicator.py +137 -0
- backtrader/indicators/contrib/stochastic_histogram_indicator.py +143 -0
- backtrader/indicators/contrib/t3_alarm_indicator.py +125 -0
- backtrader/indicators/contrib/t3_average.py +76 -0
- backtrader/indicators/contrib/t3_indicator.py +40 -0
- backtrader/indicators/contrib/the20s_v020_signal.py +93 -0
- backtrader/indicators/contrib/three_candles_indicator.py +70 -0
- backtrader/indicators/contrib/three_line_break_indicator.py +64 -0
- backtrader/indicators/contrib/time_line.py +57 -0
- backtrader/indicators/contrib/trading_channel_index_proxy.py +48 -0
- backtrader/indicators/contrib/trend_arrows_indicator.py +109 -0
- backtrader/indicators/contrib/trend_continuation_indicator.py +127 -0
- backtrader/indicators/contrib/trend_intensity_index_proxy.py +51 -0
- backtrader/indicators/contrib/trend_manager_indicator.py +39 -0
- backtrader/indicators/contrib/tri_x_candle_indicator.py +51 -0
- backtrader/indicators/contrib/trigger_line.py +66 -0
- backtrader/indicators/contrib/triple_ema_rate.py +34 -0
- backtrader/indicators/contrib/trvi_indicator.py +194 -0
- backtrader/indicators/contrib/two_pb_ideal_xosma_indicator.py +127 -0
- backtrader/indicators/contrib/ultra_absolutely_no_lag_lwma_color.py +92 -0
- backtrader/indicators/contrib/ultra_wpr_indicator.py +173 -0
- backtrader/indicators/contrib/up_down_candle_strength.py +68 -0
- backtrader/indicators/contrib/vinin_i_trend_indicator.py +139 -0
- backtrader/indicators/contrib/volume_weighted_ma_indicator.py +78 -0
- backtrader/indicators/contrib/volume_weighted_ma_st_dev_indicator.py +111 -0
- backtrader/indicators/contrib/vwap_close_indicator.py +65 -0
- backtrader/indicators/contrib/vwma_candle.py +57 -0
- backtrader/indicators/contrib/vwma_digit_system.py +70 -0
- backtrader/indicators/contrib/wami.py +43 -0
- backtrader/indicators/contrib/wprsi_signal_indicator.py +105 -0
- backtrader/indicators/contrib/x_de_marker_histogram_vol_direct_indicator.py +145 -0
- backtrader/indicators/contrib/x_fisher_indicator.py +64 -0
- backtrader/indicators/contrib/xcci_histogram_vol_direct_indicator.py +56 -0
- backtrader/indicators/contrib/xcci_histogram_vol_indicator.py +85 -0
- backtrader/indicators/contrib/xma_ichimoku.py +163 -0
- backtrader/indicators/contrib/xma_ishimoku_channel_indicator.py +65 -0
- backtrader/indicators/contrib/xma_ishimoku_line.py +68 -0
- backtrader/indicators/contrib/xma_range_bands_indicator.py +107 -0
- backtrader/indicators/contrib/xmacd_indicator.py +70 -0
- backtrader/indicators/contrib/xrsi_de_marker_histogram.py +67 -0
- backtrader/indicators/contrib/xrsi_histogram_vol_direct_indicator.py +52 -0
- backtrader/indicators/contrib/xrsi_histogram_vol_indicator.py +81 -0
- backtrader/indicators/contrib/xrvi_indicator.py +130 -0
- backtrader/indicators/contrib/zero_lag_macd.py +36 -0
- backtrader/indicators/contrib/zig_zag_recent_pivot_signal.py +90 -0
- backtrader/indicators/contrib/zpf_indicator.py +115 -0
- backtrader/indicators/crossover.py +337 -0
- backtrader/indicators/dema.py +175 -0
- backtrader/indicators/demarker.py +270 -0
- backtrader/indicators/deviation.py +284 -0
- backtrader/indicators/directionalmove.py +1071 -0
- backtrader/indicators/dma.py +112 -0
- backtrader/indicators/dpo.py +96 -0
- backtrader/indicators/dv2.py +56 -0
- backtrader/indicators/ema.py +145 -0
- backtrader/indicators/envelope.py +475 -0
- backtrader/indicators/hadelta.py +198 -0
- backtrader/indicators/heikinashi.py +153 -0
- backtrader/indicators/hma.py +153 -0
- backtrader/indicators/hurst.py +151 -0
- backtrader/indicators/ichimoku.py +267 -0
- backtrader/indicators/kama.py +181 -0
- backtrader/indicators/kst.py +159 -0
- backtrader/indicators/lrsi.py +125 -0
- backtrader/indicators/mabase.py +147 -0
- backtrader/indicators/macd.py +322 -0
- backtrader/indicators/momentum.py +267 -0
- backtrader/indicators/moneyflow.py +237 -0
- backtrader/indicators/mt5atr.py +124 -0
- backtrader/indicators/myind.py +179 -0
- backtrader/indicators/obv.py +94 -0
- backtrader/indicators/ols.py +265 -0
- backtrader/indicators/oscillator.py +161 -0
- backtrader/indicators/percentchange.py +83 -0
- backtrader/indicators/percentrank.py +46 -0
- backtrader/indicators/pivotpoint.py +469 -0
- backtrader/indicators/prettygoodoscillator.py +113 -0
- backtrader/indicators/priceops_ext.py +123 -0
- backtrader/indicators/priceoscillator.py +262 -0
- backtrader/indicators/psar.py +212 -0
- backtrader/indicators/rmi.py +69 -0
- backtrader/indicators/rsi.py +440 -0
- backtrader/indicators/sma.py +141 -0
- backtrader/indicators/smma.py +116 -0
- backtrader/indicators/spread.py +54 -0
- backtrader/indicators/stochastic.py +263 -0
- backtrader/indicators/supertrend.py +436 -0
- backtrader/indicators/trend_ext.py +105 -0
- backtrader/indicators/trix.py +202 -0
- backtrader/indicators/tsi.py +155 -0
- backtrader/indicators/ultimateoscillator.py +158 -0
- backtrader/indicators/vortex.py +62 -0
- backtrader/indicators/williams.py +194 -0
- backtrader/indicators/wma.py +103 -0
- backtrader/indicators/zlema.py +135 -0
- backtrader/indicators/zlind.py +104 -0
- backtrader/linebuffer.py +3155 -0
- backtrader/lineiterator.py +2911 -0
- backtrader/lineroot.py +1106 -0
- backtrader/lineseries.py +2559 -0
- backtrader/live_trading/__init__.py +31 -0
- backtrader/live_trading/interface.py +404 -0
- backtrader/mathsupport.py +94 -0
- backtrader/metabase.py +1804 -0
- backtrader/mixins/__init__.py +21 -0
- backtrader/mixins/singleton.py +118 -0
- backtrader/observer.py +106 -0
- backtrader/observers/__init__.py +45 -0
- backtrader/observers/benchmark.py +126 -0
- backtrader/observers/broker.py +184 -0
- backtrader/observers/buysell.py +144 -0
- backtrader/observers/drawdown.py +161 -0
- backtrader/observers/logreturns.py +113 -0
- backtrader/observers/timereturn.py +86 -0
- backtrader/observers/trade_logger.py +2972 -0
- backtrader/observers/tradelogger.py +6 -0
- backtrader/observers/trades.py +258 -0
- backtrader/order.py +1114 -0
- backtrader/parameters.py +2345 -0
- backtrader/plot/__init__.py +54 -0
- backtrader/plot/finance.py +1022 -0
- backtrader/plot/formatters.py +200 -0
- backtrader/plot/locator.py +353 -0
- backtrader/plot/multicursor.py +495 -0
- backtrader/plot/plot.py +2500 -0
- backtrader/plot/plot_plotly.py +1351 -0
- backtrader/plot/scheme.py +253 -0
- backtrader/plot/utils.py +104 -0
- backtrader/position.py +290 -0
- backtrader/position_modes.py +132 -0
- backtrader/profiles.py +254 -0
- backtrader/reports/__init__.py +39 -0
- backtrader/reports/charts.py +371 -0
- backtrader/reports/performance.py +620 -0
- backtrader/reports/reporter.py +660 -0
- backtrader/resamplerfilter.py +1001 -0
- backtrader/signal.py +118 -0
- backtrader/signals/__init__.py +17 -0
- backtrader/sizer.py +114 -0
- backtrader/sizers/__init__.py +26 -0
- backtrader/sizers/fixedsize.py +161 -0
- backtrader/sizers/percents_sizer.py +119 -0
- backtrader/store.py +221 -0
- backtrader/stores/__init__.py +33 -0
- backtrader/stores/btapistore.py +15506 -0
- backtrader/stores/livestore.py +137 -0
- backtrader/stores/vchartfile.py +96 -0
- backtrader/strategy.py +3655 -0
- backtrader/talib.py +280 -0
- backtrader/test_helpers.py +96 -0
- backtrader/timer.py +358 -0
- backtrader/trade.py +442 -0
- backtrader/tradingcal.py +361 -0
- backtrader/utils/__init__.py +68 -0
- backtrader/utils/autodict.py +251 -0
- backtrader/utils/date.py +71 -0
- backtrader/utils/dateintern.py +509 -0
- backtrader/utils/flushfile.py +94 -0
- backtrader/utils/fractal.py +101 -0
- backtrader/utils/get_metrics.py +101 -0
- backtrader/utils/load_data.py +209 -0
- backtrader/utils/log_message.py +998 -0
- backtrader/utils/ordereddefaultdict.py +75 -0
- backtrader/utils/py3.py +296 -0
- backtrader/version.py +21 -0
- backtrader/writer.py +372 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Leverage Analyzer Module - Gross leverage calculation.
|
|
3
|
+
|
|
4
|
+
This module provides the GrossLeverage analyzer for calculating the
|
|
5
|
+
gross leverage (ratio of used capital to portfolio value).
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
GrossLeverage: Analyzer that calculates gross leverage.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> cerebro = bt.Cerebro()
|
|
12
|
+
>>> cerebro.addanalyzer(bt.analyzers.GrossLeverage, _name='lev')
|
|
13
|
+
>>> results = cerebro.run()
|
|
14
|
+
>>> print(results[0].analyzers.lev.get_analysis())
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import math
|
|
18
|
+
|
|
19
|
+
from ..analyzer import Analyzer
|
|
20
|
+
from ..mathsupport import is_finite_real
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Ratio of used capital
|
|
24
|
+
class GrossLeverage(Analyzer):
|
|
25
|
+
"""This analyzer calculates the Gross Leverage of the current strategy
|
|
26
|
+
on a timeframe basis
|
|
27
|
+
|
|
28
|
+
Params:
|
|
29
|
+
|
|
30
|
+
- ``fund`` (default: ``None``)
|
|
31
|
+
|
|
32
|
+
If None, the actual mode of the broker (fundmode - True/False) will
|
|
33
|
+
be autodetected to decide if the returns are based on the total net
|
|
34
|
+
asset value or on the fund value. See ``set_fundmode`` in the broker
|
|
35
|
+
documentation
|
|
36
|
+
|
|
37
|
+
Set it to ``True`` or ``False`` for a specific behavior
|
|
38
|
+
|
|
39
|
+
Methods:
|
|
40
|
+
|
|
41
|
+
- Get_analysis
|
|
42
|
+
|
|
43
|
+
Returns a dictionary with returns as values and the datetime points for
|
|
44
|
+
each return as keys
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Parameters
|
|
48
|
+
params = (("fund", None),)
|
|
49
|
+
|
|
50
|
+
# Start
|
|
51
|
+
def __init__(self, *args, **kwargs):
|
|
52
|
+
"""Initialize the GrossLeverage analyzer.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
*args: Positional arguments.
|
|
56
|
+
**kwargs: Keyword arguments for analyzer parameters.
|
|
57
|
+
"""
|
|
58
|
+
# CRITICAL FIX: Call super().__init__() first to initialize self.p
|
|
59
|
+
super().__init__(*args, **kwargs)
|
|
60
|
+
self._value = None
|
|
61
|
+
self._cash = None
|
|
62
|
+
self._fundmode = None
|
|
63
|
+
|
|
64
|
+
def start(self):
|
|
65
|
+
"""Initialize the analyzer at the start of the backtest.
|
|
66
|
+
|
|
67
|
+
Sets the fund mode based on parameters or broker settings.
|
|
68
|
+
"""
|
|
69
|
+
if self.p.fund is None:
|
|
70
|
+
self._fundmode = self.strategy.broker.fundmode
|
|
71
|
+
else:
|
|
72
|
+
self._fundmode = self.p.fund
|
|
73
|
+
|
|
74
|
+
# Fund notification
|
|
75
|
+
def notify_fund(self, cash, value, fundvalue, shares):
|
|
76
|
+
"""Update cash and value from fund notification.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
cash: Current cash amount.
|
|
80
|
+
value: Current portfolio value.
|
|
81
|
+
fundvalue: Current fund value.
|
|
82
|
+
shares: Number of fund shares.
|
|
83
|
+
"""
|
|
84
|
+
self._cash = cash
|
|
85
|
+
if not self._fundmode:
|
|
86
|
+
self._value = value
|
|
87
|
+
else:
|
|
88
|
+
self._value = fundvalue
|
|
89
|
+
|
|
90
|
+
# Run once per bar, get ratio of used capital
|
|
91
|
+
def next(self):
|
|
92
|
+
"""Calculate and record the gross leverage for the current bar.
|
|
93
|
+
|
|
94
|
+
Gross leverage is calculated as (value - cash) / value.
|
|
95
|
+
Returns 0.0 if 100% in cash, 1.0 if fully invested without shorting.
|
|
96
|
+
"""
|
|
97
|
+
# Updates the leverage for "dtkey" (see base class) for each cycle
|
|
98
|
+
# 0.0 if 100% in cash, 1.0 if no short selling and fully invested
|
|
99
|
+
try:
|
|
100
|
+
if is_finite_real(self._value) and self._value != 0.0 and is_finite_real(self._cash):
|
|
101
|
+
lev = (self._value - self._cash) / self._value
|
|
102
|
+
if isinstance(lev, complex) or not math.isfinite(lev):
|
|
103
|
+
lev = 0.0
|
|
104
|
+
else:
|
|
105
|
+
lev = 0.0
|
|
106
|
+
except (TypeError, ValueError, ZeroDivisionError):
|
|
107
|
+
lev = 0.0
|
|
108
|
+
|
|
109
|
+
if isinstance(lev, complex) or not math.isfinite(lev):
|
|
110
|
+
lev = 0.0
|
|
111
|
+
|
|
112
|
+
self.rets[self.data0.datetime.datetime()] = lev
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""LogReturnsRolling Analyzer Module - Rolling log returns calculation.
|
|
3
|
+
|
|
4
|
+
This module provides the LogReturnsRolling analyzer for calculating
|
|
5
|
+
rolling log returns over a specified timeframe.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
LogReturnsRolling: Analyzer that calculates rolling log returns.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> cerebro = bt.Cerebro()
|
|
12
|
+
>>> cerebro.addanalyzer(bt.analyzers.LogReturnsRolling)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import collections
|
|
16
|
+
import math
|
|
17
|
+
|
|
18
|
+
from ..analyzer import TimeFrameAnalyzerBase
|
|
19
|
+
from ..utils.log_message import get_logger, throttled_warning
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
__all__ = ["LogReturnsRolling"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LogReturnsRolling(TimeFrameAnalyzerBase):
|
|
27
|
+
"""This analyzer calculates rolling returns for a given timeframe and
|
|
28
|
+
compression
|
|
29
|
+
|
|
30
|
+
Params:
|
|
31
|
+
|
|
32
|
+
- ``timeframe`` (default: ``None``)
|
|
33
|
+
If ``None`` the ``timeframe`` of the first data in the system will be
|
|
34
|
+
used
|
|
35
|
+
|
|
36
|
+
Pass ``TimeFrame.NoTimeFrame`` to consider the entire dataset with no
|
|
37
|
+
time constraints
|
|
38
|
+
|
|
39
|
+
- ``compression`` (default: ``None``)
|
|
40
|
+
|
|
41
|
+
Only used for sub-day timeframes to, for example, work on an hourly
|
|
42
|
+
timeframe by specifying "TimeFrame.Minutes" and 60 as compression
|
|
43
|
+
|
|
44
|
+
If `None`, then the compression of the first data in the system will be
|
|
45
|
+
used
|
|
46
|
+
|
|
47
|
+
- ``data`` (default: ``None``)
|
|
48
|
+
|
|
49
|
+
Reference asset to track instead of the portfolio value.
|
|
50
|
+
|
|
51
|
+
.note: this data must have been added to a ``cerebro`` instance with
|
|
52
|
+
``addata``, ``resampledata`` or ``replaydata``
|
|
53
|
+
|
|
54
|
+
- ``firstopen`` (default: ``True``)
|
|
55
|
+
|
|
56
|
+
When tracking the returns of `data` the following is done when
|
|
57
|
+
crossing a timeframe boundary, for example, ``Years``:
|
|
58
|
+
|
|
59
|
+
- Last ``close`` the previous year is used as the reference price to
|
|
60
|
+
see the return in the current year
|
|
61
|
+
|
|
62
|
+
The problem is the first calculation, because the data has** no
|
|
63
|
+
previous** closing price.As such, and when this parameter is `True`,
|
|
64
|
+
the *opening* price will be used for the first calculation.
|
|
65
|
+
|
|
66
|
+
This requires the data feed to have an ``open`` price (for ``close``
|
|
67
|
+
the standard [0] notations will be used without a reference to a field
|
|
68
|
+
price)
|
|
69
|
+
|
|
70
|
+
Else the initial close will be used.
|
|
71
|
+
|
|
72
|
+
- ``fund`` (default: ``None``)
|
|
73
|
+
|
|
74
|
+
If `None`, the actual mode of the broker (fundmode - True/False) will
|
|
75
|
+
be autodetected to decide if the returns are based on the total net
|
|
76
|
+
asset value or on the fund value. See ``set_fundmode`` in the broker
|
|
77
|
+
documentation
|
|
78
|
+
|
|
79
|
+
Set it to ``True`` or ``False`` for a specific behavior
|
|
80
|
+
|
|
81
|
+
Methods:
|
|
82
|
+
|
|
83
|
+
- Get_analysis
|
|
84
|
+
|
|
85
|
+
Returns a dictionary with returns as values and the datetime points for
|
|
86
|
+
each return as keys
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# Parameters
|
|
90
|
+
params = (
|
|
91
|
+
("data", None),
|
|
92
|
+
("firstopen", True),
|
|
93
|
+
("fund", None),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Start
|
|
97
|
+
def __init__(self, *args, **kwargs):
|
|
98
|
+
"""Initialize the LogReturnsRolling analyzer.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
*args: Positional arguments.
|
|
102
|
+
**kwargs: Keyword arguments for analyzer parameters.
|
|
103
|
+
"""
|
|
104
|
+
# Call parent class __init__ method to support timeframe and compression parameters
|
|
105
|
+
super().__init__(*args, **kwargs)
|
|
106
|
+
|
|
107
|
+
self._value = None
|
|
108
|
+
self._lastvalue = None
|
|
109
|
+
self._values = None
|
|
110
|
+
self._fundmode = None
|
|
111
|
+
|
|
112
|
+
def start(self):
|
|
113
|
+
"""Initialize the analyzer at the start of the backtest.
|
|
114
|
+
|
|
115
|
+
Sets the fund mode and initializes the rolling value queue
|
|
116
|
+
with size controlled by compression parameter.
|
|
117
|
+
"""
|
|
118
|
+
super().start()
|
|
119
|
+
if self.p.fund is None:
|
|
120
|
+
self._fundmode = self.strategy.broker.fundmode
|
|
121
|
+
else:
|
|
122
|
+
self._fundmode = self.p.fund
|
|
123
|
+
# The special part is that self._values is set as a queue, where self.compression parameter controls how many elements the queue saves
|
|
124
|
+
# Note: use self.compression (set in _start from data) not self.p.compression (which may be None)
|
|
125
|
+
self._values = collections.deque([float("Nan")] * self.compression, maxlen=self.compression)
|
|
126
|
+
|
|
127
|
+
if self.p.data is None:
|
|
128
|
+
# keep the initial portfolio value if not tracing data
|
|
129
|
+
if not self._fundmode:
|
|
130
|
+
self._lastvalue = self.strategy.broker.getvalue()
|
|
131
|
+
else:
|
|
132
|
+
self._lastvalue = self.strategy.broker.fundvalue
|
|
133
|
+
|
|
134
|
+
def notify_fund(self, cash, value, fundvalue, shares):
|
|
135
|
+
"""Update current value from fund notification.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
cash: Current cash amount.
|
|
139
|
+
value: Current portfolio value.
|
|
140
|
+
fundvalue: Current fund value.
|
|
141
|
+
shares: Number of fund shares.
|
|
142
|
+
"""
|
|
143
|
+
if not self._fundmode:
|
|
144
|
+
self._value = value if self.p.data is None else self.p.data[0]
|
|
145
|
+
else:
|
|
146
|
+
self._value = fundvalue if self.p.data is None else self.p.data[0]
|
|
147
|
+
|
|
148
|
+
# Called once in a new timeframe
|
|
149
|
+
def on_dt_over(self):
|
|
150
|
+
"""Handle timeframe boundary crossing.
|
|
151
|
+
|
|
152
|
+
Updates the rolling value queue when entering a new period.
|
|
153
|
+
"""
|
|
154
|
+
# next is called in a new timeframe period
|
|
155
|
+
if self.p.data is None or len(self.p.data) > 1:
|
|
156
|
+
# Not tracking a data feed or data feed has data already
|
|
157
|
+
vst = self._lastvalue # update value_start to last
|
|
158
|
+
else:
|
|
159
|
+
# The 1st tick has no previous reference, use the opening price
|
|
160
|
+
vst = self.p.data.open[0] if self.p.firstopen else self.p.data[0]
|
|
161
|
+
|
|
162
|
+
self._values.append(vst) # push values backwards (and out)
|
|
163
|
+
|
|
164
|
+
def next(self):
|
|
165
|
+
"""Calculate and store the rolling log return for the current period.
|
|
166
|
+
|
|
167
|
+
Calculates log(current_value / oldest_value) from the rolling window.
|
|
168
|
+
"""
|
|
169
|
+
# Calculate the return
|
|
170
|
+
super().next()
|
|
171
|
+
# When the strategy is running, if there are too many losses, self._value / self._values[0] might be 0, avoid this situation
|
|
172
|
+
try:
|
|
173
|
+
start_value = self._values[0]
|
|
174
|
+
ratio = self._value / start_value
|
|
175
|
+
if isinstance(ratio, complex) or not math.isfinite(ratio) or ratio <= 0:
|
|
176
|
+
raise ValueError(f"invalid log return ratio: {ratio}")
|
|
177
|
+
log_return = math.log(ratio)
|
|
178
|
+
if not math.isfinite(log_return):
|
|
179
|
+
raise ValueError(f"invalid log return value: {log_return}")
|
|
180
|
+
self.rets[self.dtkey] = log_return
|
|
181
|
+
except (TypeError, ValueError, ZeroDivisionError, OverflowError) as e:
|
|
182
|
+
throttled_warning(
|
|
183
|
+
logger,
|
|
184
|
+
"rolling_log_return",
|
|
185
|
+
"Log return calculation failed: %s",
|
|
186
|
+
e,
|
|
187
|
+
exc_info=False,
|
|
188
|
+
)
|
|
189
|
+
self.rets[self.dtkey] = 0.0
|
|
190
|
+
self._lastvalue = self._value # keep last value
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Period Statistics Analyzer Module - Basic statistics by period.
|
|
3
|
+
|
|
4
|
+
This module provides the PeriodStats analyzer for calculating basic
|
|
5
|
+
statistics (average, standard deviation, etc.) for a given timeframe.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
PeriodStats: Analyzer that calculates period statistics.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> cerebro = bt.Cerebro()
|
|
12
|
+
>>> cerebro.addanalyzer(bt.analyzers.PeriodStats, _name='stats')
|
|
13
|
+
>>> results = cerebro.run()
|
|
14
|
+
>>> print(results[0].analyzers.stats.get_analysis())
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import math
|
|
18
|
+
|
|
19
|
+
from ..analyzer import Analyzer
|
|
20
|
+
from ..dataseries import TimeFrame
|
|
21
|
+
from ..mathsupport import average, standarddev
|
|
22
|
+
from ..metabase import OwnerContext
|
|
23
|
+
from ..utils.py3 import itervalues
|
|
24
|
+
from .timereturn import TimeReturn
|
|
25
|
+
|
|
26
|
+
__all__ = ["PeriodStats"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Period statistics
|
|
30
|
+
class PeriodStats(Analyzer):
|
|
31
|
+
"""Calculates basic statistics for given timeframe
|
|
32
|
+
|
|
33
|
+
Params:
|
|
34
|
+
|
|
35
|
+
- ``timeframe`` (default: ``Years``)
|
|
36
|
+
If ``None`` the ``timeframe`` of the first data in the system will be
|
|
37
|
+
used
|
|
38
|
+
|
|
39
|
+
Pass ``TimeFrame.NoTimeFrame`` to consider the entire dataset with no
|
|
40
|
+
time constraints
|
|
41
|
+
|
|
42
|
+
- ``compression`` (default: ``1``)
|
|
43
|
+
|
|
44
|
+
Only used for sub-day timeframes to, for example, work on an hourly
|
|
45
|
+
timeframe by specifying "TimeFrame.Minutes" and 60 as compression
|
|
46
|
+
|
|
47
|
+
If `None`, then the compression of the first data in the system will be
|
|
48
|
+
used
|
|
49
|
+
|
|
50
|
+
- ``fund`` (default: ``None``)
|
|
51
|
+
|
|
52
|
+
If `None`, the actual mode of the broker (fundmode - True/False) will
|
|
53
|
+
be autodetected to decide if the returns are based on the total net
|
|
54
|
+
asset value or on the fund value. See ``set_fundmode`` in the broker
|
|
55
|
+
documentation
|
|
56
|
+
|
|
57
|
+
Set it to ``True`` or ``False`` for a specific behavior
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
``get_analysis`` returns a dictionary containing the keys:
|
|
61
|
+
|
|
62
|
+
- ``average``
|
|
63
|
+
- ``stddev``
|
|
64
|
+
- ``positive``
|
|
65
|
+
- ``negative``
|
|
66
|
+
- ``nochange``
|
|
67
|
+
- ``best``
|
|
68
|
+
- ``worst``
|
|
69
|
+
|
|
70
|
+
If the parameter ``zeroispos`` is set to ``True``, periods with no change
|
|
71
|
+
will be counted as positive
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
# Parameters
|
|
75
|
+
params = (
|
|
76
|
+
("timeframe", TimeFrame.Years),
|
|
77
|
+
("compression", 1),
|
|
78
|
+
("zeroispos", False),
|
|
79
|
+
("fund", None),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Initialize, call TimeReturn
|
|
83
|
+
def __init__(self, *args, **kwargs):
|
|
84
|
+
"""Initialize the PeriodStats analyzer.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
*args: Positional arguments.
|
|
88
|
+
**kwargs: Keyword arguments for analyzer parameters.
|
|
89
|
+
"""
|
|
90
|
+
# CRITICAL FIX: Call super().__init__() first to initialize self.p
|
|
91
|
+
super().__init__(*args, **kwargs)
|
|
92
|
+
# Use OwnerContext so child analyzer can find this as its parent
|
|
93
|
+
with OwnerContext.set_owner(self):
|
|
94
|
+
self._tr = TimeReturn(
|
|
95
|
+
timeframe=self.p.timeframe, compression=self.p.compression, fund=self.p.fund
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Stop
|
|
99
|
+
def stop(self):
|
|
100
|
+
"""Calculate period statistics when backtest ends.
|
|
101
|
+
|
|
102
|
+
Computes average, standard deviation, and count of positive/negative/
|
|
103
|
+
zero returns for the specified timeframe period.
|
|
104
|
+
"""
|
|
105
|
+
# Get returns, default is annual
|
|
106
|
+
trets = self._tr.get_analysis() # dict key = date, value = ret
|
|
107
|
+
# Count years with positive, negative, and zero returns
|
|
108
|
+
pos = nul = neg = 0
|
|
109
|
+
sanitized_trets = []
|
|
110
|
+
for tret in itervalues(trets):
|
|
111
|
+
try:
|
|
112
|
+
tret = float(tret)
|
|
113
|
+
except (TypeError, ValueError):
|
|
114
|
+
tret = 0.0
|
|
115
|
+
if not math.isfinite(tret):
|
|
116
|
+
tret = 0.0
|
|
117
|
+
sanitized_trets.append(tret)
|
|
118
|
+
trets = sanitized_trets
|
|
119
|
+
if not trets:
|
|
120
|
+
self.rets["average"] = 0.0
|
|
121
|
+
self.rets["stddev"] = 0.0
|
|
122
|
+
self.rets["positive"] = 0
|
|
123
|
+
self.rets["negative"] = 0
|
|
124
|
+
self.rets["nochange"] = 0
|
|
125
|
+
self.rets["best"] = 0.0
|
|
126
|
+
self.rets["worst"] = 0.0
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
for tret in trets:
|
|
130
|
+
if tret > 0.0:
|
|
131
|
+
pos += 1
|
|
132
|
+
elif tret < 0.0:
|
|
133
|
+
neg += 1
|
|
134
|
+
else:
|
|
135
|
+
# Whether 0 is considered positive return
|
|
136
|
+
if self.p.zeroispos:
|
|
137
|
+
pos += tret == 0.0
|
|
138
|
+
else:
|
|
139
|
+
nul += tret == 0.0
|
|
140
|
+
# Average return
|
|
141
|
+
self.rets["average"] = avg = average(trets)
|
|
142
|
+
# Return standard deviation
|
|
143
|
+
self.rets["stddev"] = standarddev(trets, avg)
|
|
144
|
+
# Number of positive years
|
|
145
|
+
self.rets["positive"] = pos
|
|
146
|
+
# Number of negative years
|
|
147
|
+
self.rets["negative"] = neg
|
|
148
|
+
# Number of unchanged years
|
|
149
|
+
self.rets["nochange"] = nul
|
|
150
|
+
# Best year return
|
|
151
|
+
self.rets["best"] = max(trets)
|
|
152
|
+
# Worst year return
|
|
153
|
+
self.rets["worst"] = min(trets)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Positions Analyzer Module - Position value tracking.
|
|
3
|
+
|
|
4
|
+
This module provides the PositionsValue analyzer for tracking the
|
|
5
|
+
value of positions across all data feeds.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
PositionsValue: Analyzer that reports position values over time.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> cerebro = bt.Cerebro()
|
|
12
|
+
>>> cerebro.addanalyzer(bt.analyzers.PositionsValue, _name='posval')
|
|
13
|
+
>>> results = cerebro.run()
|
|
14
|
+
>>> print(results[0].analyzers.posval.get_analysis())
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import math
|
|
18
|
+
|
|
19
|
+
from ..analyzer import Analyzer
|
|
20
|
+
from ..dataseries import TimeFrame
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _finite_real_or_zero(value):
|
|
24
|
+
try:
|
|
25
|
+
if isinstance(value, complex) or not math.isfinite(value):
|
|
26
|
+
return 0.0
|
|
27
|
+
except TypeError:
|
|
28
|
+
return 0.0
|
|
29
|
+
return value
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Position value
|
|
33
|
+
class PositionsValue(Analyzer):
|
|
34
|
+
"""This analyzer reports the value of the positions of the current set of
|
|
35
|
+
datas
|
|
36
|
+
|
|
37
|
+
Params:
|
|
38
|
+
|
|
39
|
+
- timeframe (default: ``None``)
|
|
40
|
+
If ``None`` then the timeframe of the first data of the system will be
|
|
41
|
+
used
|
|
42
|
+
|
|
43
|
+
- compression (default: ``None``)
|
|
44
|
+
|
|
45
|
+
Only used for sub-day timeframes to, for example, work on an hourly
|
|
46
|
+
timeframe by specifying "TimeFrame.Minutes" and 60 as compression
|
|
47
|
+
|
|
48
|
+
If `None`, then the compression of the first data in the system will be
|
|
49
|
+
used
|
|
50
|
+
|
|
51
|
+
- headers (default: ``False``)
|
|
52
|
+
|
|
53
|
+
Add an initial key to the dictionary holding the results with the names
|
|
54
|
+
of the data 'Datetime' as a key
|
|
55
|
+
|
|
56
|
+
- cash (default: ``False``)
|
|
57
|
+
|
|
58
|
+
Include the actual cash as an extra position (for the header 'cash'
|
|
59
|
+
will be used as name)
|
|
60
|
+
|
|
61
|
+
Methods:
|
|
62
|
+
|
|
63
|
+
- get_analysis
|
|
64
|
+
|
|
65
|
+
Returns a dictionary with returns as values and the datetime points for
|
|
66
|
+
each return as keys
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
# Parameters
|
|
70
|
+
params = (
|
|
71
|
+
("headers", False),
|
|
72
|
+
("cash", False),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Start
|
|
76
|
+
def __init__(self, *args, **kwargs):
|
|
77
|
+
"""Initialize the PositionsValue analyzer.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
*args: Positional arguments.
|
|
81
|
+
**kwargs: Keyword arguments for analyzer parameters.
|
|
82
|
+
"""
|
|
83
|
+
# CRITICAL FIX: Call super().__init__() first to initialize self.p
|
|
84
|
+
super().__init__(*args, **kwargs)
|
|
85
|
+
self._usedate = None
|
|
86
|
+
|
|
87
|
+
def start(self):
|
|
88
|
+
"""Initialize the analyzer at the start of the backtest.
|
|
89
|
+
|
|
90
|
+
Sets up headers for the results dictionary and determines whether
|
|
91
|
+
to use date or datetime as the key based on the timeframe.
|
|
92
|
+
"""
|
|
93
|
+
# If headers parameter is True, use each data's name as header
|
|
94
|
+
if self.p.headers:
|
|
95
|
+
headers = [d._name or "Data%d" % i for i, d in enumerate(self.datas)]
|
|
96
|
+
# If cash is True, also save cash
|
|
97
|
+
self.rets["Datetime"] = headers + ["cash"] * self.p.cash
|
|
98
|
+
# Time period
|
|
99
|
+
tf = min(d._timeframe for d in self.datas)
|
|
100
|
+
# If time period >= Days, set usedate parameter to True
|
|
101
|
+
self._usedate = tf >= TimeFrame.Days
|
|
102
|
+
|
|
103
|
+
# Called once per bar
|
|
104
|
+
def next(self):
|
|
105
|
+
"""Record position values for the current bar.
|
|
106
|
+
|
|
107
|
+
Gets the value of positions for each data feed and optionally
|
|
108
|
+
includes cash. Stores results keyed by date or datetime.
|
|
109
|
+
"""
|
|
110
|
+
# Get value for each data
|
|
111
|
+
pvals = [_finite_real_or_zero(self.strategy.broker.get_value([d])) for d in self.datas]
|
|
112
|
+
# If cash is True, save cash
|
|
113
|
+
if self.p.cash:
|
|
114
|
+
pvals.append(_finite_real_or_zero(self.strategy.broker.get_cash()))
|
|
115
|
+
# If usedate is True, use date as key, otherwise use datetime as key
|
|
116
|
+
if self._usedate:
|
|
117
|
+
self.rets[self.strategy.datetime.date()] = pvals
|
|
118
|
+
else:
|
|
119
|
+
self.rets[self.strategy.datetime.datetime()] = pvals
|