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,192 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""TimeReturn Analyzer Module - Time-based returns calculation.
|
|
3
|
+
|
|
4
|
+
This module provides the TimeReturn analyzer for calculating returns
|
|
5
|
+
over specified time periods.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
TimeReturn: Analyzer that calculates returns by timeframe.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> cerebro = bt.Cerebro()
|
|
12
|
+
>>> cerebro.addanalyzer(bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.Years)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import math
|
|
16
|
+
|
|
17
|
+
from ..analyzer import TimeFrameAnalyzerBase
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TimeReturn(TimeFrameAnalyzerBase):
|
|
21
|
+
"""This analyzer calculates the Returns by looking at the beginning
|
|
22
|
+
and end of the timeframe
|
|
23
|
+
|
|
24
|
+
Params:
|
|
25
|
+
|
|
26
|
+
- ``timeframe`` (default: ``None``)
|
|
27
|
+
If ``None`` the ``timeframe`` of the first data in the system will be
|
|
28
|
+
used
|
|
29
|
+
|
|
30
|
+
Pass ``TimeFrame.NoTimeFrame`` to consider the entire dataset with no
|
|
31
|
+
time constraints
|
|
32
|
+
|
|
33
|
+
- ``compression`` (default: ``None``)
|
|
34
|
+
|
|
35
|
+
Only used for sub-day timeframes to, for example, work on an hourly
|
|
36
|
+
timeframe by specifying "TimeFrame.Minutes" and 60 as compression
|
|
37
|
+
|
|
38
|
+
If `None`, then the compression of the first data in the system will be
|
|
39
|
+
used
|
|
40
|
+
|
|
41
|
+
- ``data`` (default: ``None``)
|
|
42
|
+
|
|
43
|
+
Reference asset to track instead of the portfolio value.
|
|
44
|
+
|
|
45
|
+
- Note:: this data must have been added to a ``cerebro`` instance with
|
|
46
|
+
``addata``, ``resampledata`` or ``replaydata``
|
|
47
|
+
|
|
48
|
+
- ``firstopen`` (default: ``True``)
|
|
49
|
+
|
|
50
|
+
When tracking the returns of `data` the following is done when
|
|
51
|
+
crossing a timeframe boundary, for example, ``Years``:
|
|
52
|
+
|
|
53
|
+
- Last ``close`` the previous year is used as the reference price to
|
|
54
|
+
see the return in the current year
|
|
55
|
+
|
|
56
|
+
The problem is the first calculation, because the data has** no
|
|
57
|
+
previous** closing price.As such, and when this parameter is `True`,
|
|
58
|
+
the *opening* price will be used for the first calculation.
|
|
59
|
+
|
|
60
|
+
This requires the data feed to have an ``open`` price (for ``close``
|
|
61
|
+
the standard [0] notations will be used without a reference to a field
|
|
62
|
+
price)
|
|
63
|
+
|
|
64
|
+
Else the initial close will be used.
|
|
65
|
+
# When calculating returns for the first period, whether to use the first opening price. If parameter is False, the first closing price will be used
|
|
66
|
+
|
|
67
|
+
- ``fund`` (default: ``None``)
|
|
68
|
+
|
|
69
|
+
If `None`, the actual mode of the broker (fundmode - True/False) will
|
|
70
|
+
be autodetected to decide if the returns are based on the total net
|
|
71
|
+
asset value or on the fund value. See ``set_fundmode`` in the broker
|
|
72
|
+
documentation
|
|
73
|
+
|
|
74
|
+
Set it to ``True`` or ``False`` for a specific behavior
|
|
75
|
+
|
|
76
|
+
Methods:
|
|
77
|
+
|
|
78
|
+
- Get_analysis
|
|
79
|
+
|
|
80
|
+
Returns a dictionary with returns as values and the datetime points for
|
|
81
|
+
each return as keys
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
# Parameters
|
|
85
|
+
params = (
|
|
86
|
+
("data", None),
|
|
87
|
+
("firstopen", True),
|
|
88
|
+
("fund", None),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# __init__ method to support parameter initialization after metaclass removal
|
|
92
|
+
def __init__(self, *args, **kwargs):
|
|
93
|
+
"""Initialize the TimeReturn analyzer.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
*args: Positional arguments.
|
|
97
|
+
**kwargs: Keyword arguments for analyzer parameters.
|
|
98
|
+
"""
|
|
99
|
+
super().__init__(*args, **kwargs)
|
|
100
|
+
|
|
101
|
+
# Start
|
|
102
|
+
def start(self):
|
|
103
|
+
"""Initialize the analyzer at the start of the backtest.
|
|
104
|
+
|
|
105
|
+
Sets the fund mode and initializes the starting value for return
|
|
106
|
+
calculations.
|
|
107
|
+
"""
|
|
108
|
+
super().start()
|
|
109
|
+
if self.p.fund is None:
|
|
110
|
+
self._fundmode = self.strategy.broker.fundmode
|
|
111
|
+
else:
|
|
112
|
+
self._fundmode = self.p.fund
|
|
113
|
+
# PERFORMANCE OPTIMIZATION: Cache p.data reference (called 691K+ times)
|
|
114
|
+
self._p_data = self.p.data
|
|
115
|
+
# Start value
|
|
116
|
+
self._value_start = 0.0
|
|
117
|
+
# End value
|
|
118
|
+
self._lastvalue = None
|
|
119
|
+
# If parameter data is None
|
|
120
|
+
if self._p_data is None:
|
|
121
|
+
# keep the initial portfolio value if not tracing a data
|
|
122
|
+
if not self._fundmode:
|
|
123
|
+
self._lastvalue = self.strategy.broker.getvalue()
|
|
124
|
+
else:
|
|
125
|
+
self._lastvalue = self.strategy.broker.fundvalue
|
|
126
|
+
|
|
127
|
+
# Notify fund information
|
|
128
|
+
def notify_fund(self, cash, value, fundvalue, shares):
|
|
129
|
+
"""Update current value based on fund notification.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
cash: Current cash amount.
|
|
133
|
+
value: Current portfolio value.
|
|
134
|
+
fundvalue: Current fund value.
|
|
135
|
+
shares: Number of fund shares.
|
|
136
|
+
"""
|
|
137
|
+
# PERFORMANCE OPTIMIZATION: Use cached _p_data reference
|
|
138
|
+
p_data = self._p_data
|
|
139
|
+
if not self._fundmode:
|
|
140
|
+
# Record current value
|
|
141
|
+
if p_data is None:
|
|
142
|
+
self._value = value # the portofolio value if tracking no data
|
|
143
|
+
else:
|
|
144
|
+
self._value = p_data[0] # the data value if tracking data
|
|
145
|
+
else:
|
|
146
|
+
if p_data is None:
|
|
147
|
+
self._value = fundvalue # the fund value if tracking no data
|
|
148
|
+
else:
|
|
149
|
+
self._value = p_data[0] # the data value if tracking data
|
|
150
|
+
|
|
151
|
+
# On datetime over
|
|
152
|
+
def on_dt_over(self):
|
|
153
|
+
"""Handle timeframe boundary crossing.
|
|
154
|
+
|
|
155
|
+
Updates the starting value for return calculation when crossing
|
|
156
|
+
into a new timeframe period.
|
|
157
|
+
"""
|
|
158
|
+
# next is called in a new timeframe period
|
|
159
|
+
# PERFORMANCE OPTIMIZATION: Use cached _p_data reference
|
|
160
|
+
p_data = self._p_data
|
|
161
|
+
if p_data is None or self._lastvalue is not None:
|
|
162
|
+
self._value_start = self._lastvalue # update value_start to last
|
|
163
|
+
|
|
164
|
+
else:
|
|
165
|
+
# The 1st tick has no previous reference, use the opening price
|
|
166
|
+
if self.p.firstopen:
|
|
167
|
+
self._value_start = p_data.open[0]
|
|
168
|
+
else:
|
|
169
|
+
self._value_start = p_data[0]
|
|
170
|
+
|
|
171
|
+
# Call next
|
|
172
|
+
def next(self):
|
|
173
|
+
"""Calculate and store the return for the current period.
|
|
174
|
+
|
|
175
|
+
Calculates the return as (current_value / start_value) - 1 and
|
|
176
|
+
stores it in the results dictionary keyed by datetime.
|
|
177
|
+
"""
|
|
178
|
+
# Calculate the return
|
|
179
|
+
super().next()
|
|
180
|
+
# self.dtkey is an attribute set in analyzer, usually the end date of a period
|
|
181
|
+
if self._value_start:
|
|
182
|
+
try:
|
|
183
|
+
ret = (self._value / self._value_start) - 1.0
|
|
184
|
+
if isinstance(ret, complex) or not math.isfinite(ret):
|
|
185
|
+
ret = 0.0
|
|
186
|
+
except (ZeroDivisionError, TypeError, ValueError):
|
|
187
|
+
ret = 0.0
|
|
188
|
+
self.rets[self.dtkey] = ret
|
|
189
|
+
else:
|
|
190
|
+
self.rets[self.dtkey] = 0.0
|
|
191
|
+
# self.rets[self.dtkey] = (float(self._value) / float(self._value_start)) - 1.0
|
|
192
|
+
self._lastvalue = self._value # keep last value
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""TotalValue Analyzer Module - Portfolio value tracking.
|
|
2
|
+
|
|
3
|
+
This module provides the TotalValue analyzer for tracking the total
|
|
4
|
+
portfolio value over time.
|
|
5
|
+
|
|
6
|
+
Classes:
|
|
7
|
+
TotalValue: Analyzer that records portfolio value at each step.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> cerebro = bt.Cerebro()
|
|
11
|
+
>>> cerebro.addanalyzer(bt.analyzers.TotalValue, _name='val')
|
|
12
|
+
>>> results = cerebro.run()
|
|
13
|
+
>>> print(results[0].analyzers.val.get_analysis())
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import math
|
|
17
|
+
from collections import OrderedDict
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from ..analyzer import Analyzer
|
|
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
|
+
class TotalValue(Analyzer):
|
|
33
|
+
"""This analyzer will get total value from every next.
|
|
34
|
+
|
|
35
|
+
Params:
|
|
36
|
+
Methods:
|
|
37
|
+
|
|
38
|
+
- Get_analysis
|
|
39
|
+
|
|
40
|
+
Returns a dictionary with returns as values and the datetime points for
|
|
41
|
+
each return as keys
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
params = ()
|
|
45
|
+
rets: Optional[dict] = None
|
|
46
|
+
|
|
47
|
+
def start(self):
|
|
48
|
+
"""Initialize the analyzer at the start of the backtest.
|
|
49
|
+
|
|
50
|
+
Creates the ordered dictionary to store value history.
|
|
51
|
+
"""
|
|
52
|
+
super().start()
|
|
53
|
+
self.rets = OrderedDict()
|
|
54
|
+
# PERFORMANCE OPTIMIZATION: Cache broker reference
|
|
55
|
+
self._broker = self.strategy.broker
|
|
56
|
+
|
|
57
|
+
# PERFORMANCE OPTIMIZATION: Cache attribute access, called 522K+ times
|
|
58
|
+
def next(self):
|
|
59
|
+
"""Record the total portfolio value for the current bar.
|
|
60
|
+
|
|
61
|
+
Gets the current portfolio value from the broker and stores it
|
|
62
|
+
keyed by datetime.
|
|
63
|
+
"""
|
|
64
|
+
# Calculate the return
|
|
65
|
+
super().next()
|
|
66
|
+
# Cache attribute access for performance
|
|
67
|
+
self.rets[self.datas[0].datetime.datetime()] = _finite_real_or_zero(self._broker.getvalue())
|
|
68
|
+
|
|
69
|
+
def get_analysis(self):
|
|
70
|
+
"""Return the total value analysis results.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
OrderedDict: Dictionary mapping datetimes to portfolio values.
|
|
74
|
+
"""
|
|
75
|
+
return self.rets
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Trade Analyzer Module - Detailed trade statistics.
|
|
3
|
+
|
|
4
|
+
This module provides the TradeAnalyzer for calculating comprehensive
|
|
5
|
+
trade statistics including win/loss ratios, streaks, and PnL metrics.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
TradeAnalyzer: Analyzer that calculates detailed trade statistics.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> cerebro = bt.Cerebro()
|
|
12
|
+
>>> cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='ta')
|
|
13
|
+
>>> results = cerebro.run()
|
|
14
|
+
>>> print(results[0].analyzers.ta.get_analysis())
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from ..analyzer import Analyzer
|
|
18
|
+
from ..utils import AutoDict, AutoOrderedDict
|
|
19
|
+
from ..utils.py3 import MAXINT
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Analyze trades
|
|
23
|
+
class TradeAnalyzer(Analyzer):
|
|
24
|
+
"""
|
|
25
|
+
Provides statistics on closed trades (keeps also the count of open ones)
|
|
26
|
+
|
|
27
|
+
- Total Open/Closed Trades
|
|
28
|
+
|
|
29
|
+
- Streak Won/Lost Current/Longest
|
|
30
|
+
|
|
31
|
+
- ProfitAndLoss Total/Average
|
|
32
|
+
|
|
33
|
+
- Won/Lost Count/ Total PNL/ Average PNL / Max PNL
|
|
34
|
+
|
|
35
|
+
- Long/Short Count/ Total PNL / Average PNL / Max PNL
|
|
36
|
+
|
|
37
|
+
- Won/Lost Count/ Total PNL/ Average PNL / Max PNL
|
|
38
|
+
|
|
39
|
+
- Length (bars in the market)
|
|
40
|
+
|
|
41
|
+
- Total/Average/Max/Min
|
|
42
|
+
|
|
43
|
+
- Won/Lost Total/Average/Max/Min
|
|
44
|
+
|
|
45
|
+
- Long/Short Total/Average/Max/Min
|
|
46
|
+
|
|
47
|
+
- Won/Lost Total/Average/Max/Min
|
|
48
|
+
|
|
49
|
+
Note:
|
|
50
|
+
|
|
51
|
+
The analyzer uses an autodict for the fields, which means that if no
|
|
52
|
+
trades are executed, no statistics will be generated.
|
|
53
|
+
|
|
54
|
+
In that case, there will be a single field/subfield in the dictionary
|
|
55
|
+
returned by ``get_analysis``, namely:
|
|
56
|
+
|
|
57
|
+
- Dictname['total']['total'] which will have a value of 0 (the field is
|
|
58
|
+
also reachable with dot notation dictname.total.total
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
# rets is created in create_analysis(); declared here for typing only.
|
|
62
|
+
rets: AutoOrderedDict
|
|
63
|
+
|
|
64
|
+
# Create analysis
|
|
65
|
+
def create_analysis(self):
|
|
66
|
+
"""Create the analysis result data structure.
|
|
67
|
+
|
|
68
|
+
Initializes the AutoOrderedDict with a total counter set to zero.
|
|
69
|
+
"""
|
|
70
|
+
self.rets = AutoOrderedDict()
|
|
71
|
+
self.rets.total.total = 0
|
|
72
|
+
|
|
73
|
+
# Stop
|
|
74
|
+
def stop(self):
|
|
75
|
+
"""Finalize the analysis when backtest ends.
|
|
76
|
+
|
|
77
|
+
Closes the results dictionary to prevent further modifications.
|
|
78
|
+
"""
|
|
79
|
+
super().stop()
|
|
80
|
+
self.rets._close()
|
|
81
|
+
|
|
82
|
+
# Trade notification
|
|
83
|
+
def notify_trade(self, trade):
|
|
84
|
+
"""Process trade notifications to build detailed statistics.
|
|
85
|
+
|
|
86
|
+
Updates all trade statistics including win/loss counts, streaks,
|
|
87
|
+
PnL metrics, and trade duration for both long and short positions.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
trade: The trade object with status and PnL information.
|
|
91
|
+
"""
|
|
92
|
+
# If trade just opened
|
|
93
|
+
if trade.justopened:
|
|
94
|
+
# Trade just opened
|
|
95
|
+
self.rets.total.total += 1
|
|
96
|
+
self.rets.total.open += 1
|
|
97
|
+
# If trade is closed
|
|
98
|
+
elif trade.status == trade.Closed:
|
|
99
|
+
self._on_trade_closed(trade)
|
|
100
|
+
|
|
101
|
+
def _on_trade_closed(self, trade):
|
|
102
|
+
"""Update all closed-trade statistics (streak / pnl / won-lost /
|
|
103
|
+
long-short / length) for a single closed trade.
|
|
104
|
+
|
|
105
|
+
Extracted verbatim from notify_trade's Closed branch to keep that
|
|
106
|
+
dispatcher trivial; no behavior change. Split into per-category
|
|
107
|
+
helpers (streak / pnl / won-lost / long-short / length) to reduce
|
|
108
|
+
complexity; the computation order and shared ``res``/``trades``
|
|
109
|
+
state are preserved exactly.
|
|
110
|
+
"""
|
|
111
|
+
trades = self.rets
|
|
112
|
+
|
|
113
|
+
res = AutoDict()
|
|
114
|
+
# Trade just closed
|
|
115
|
+
# Profit
|
|
116
|
+
res.won = int(trade.pnlcomm >= 0.0)
|
|
117
|
+
# Loss
|
|
118
|
+
res.lost = int(not res.won)
|
|
119
|
+
# Long position
|
|
120
|
+
res.tlong = trade.long
|
|
121
|
+
# Short position
|
|
122
|
+
res.tshort = not trade.long
|
|
123
|
+
# Opened trade
|
|
124
|
+
trades.total.open -= 1
|
|
125
|
+
# Closed trade
|
|
126
|
+
trades.total.closed += 1
|
|
127
|
+
|
|
128
|
+
self._update_streak(trades, res)
|
|
129
|
+
self._update_gross_net_pnl(trades, trade)
|
|
130
|
+
self._update_won_lost(trades, trade, res)
|
|
131
|
+
self._update_long_short(trades, trade, res)
|
|
132
|
+
self._update_length(trades, trade)
|
|
133
|
+
self._update_length_won_lost(trades, trade, res)
|
|
134
|
+
self._update_length_long_short(trades, trade, res)
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def _update_streak(trades, res):
|
|
138
|
+
"""Streak: consecutive win and loss counts."""
|
|
139
|
+
for wlname in ["won", "lost"]:
|
|
140
|
+
# Current win/loss status
|
|
141
|
+
wl = res[wlname]
|
|
142
|
+
# Current consecutive win or loss count
|
|
143
|
+
trades.streak[wlname].current *= wl
|
|
144
|
+
trades.streak[wlname].current += wl
|
|
145
|
+
# Get maximum consecutive win or loss count
|
|
146
|
+
ls = trades.streak[wlname].longest or 0
|
|
147
|
+
# Recalculate
|
|
148
|
+
trades.streak[wlname].longest = max(ls, trades.streak[wlname].current)
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def _update_gross_net_pnl(trades, trade):
|
|
152
|
+
"""Aggregate gross/net total and average trade pnl."""
|
|
153
|
+
# Trade profit/loss
|
|
154
|
+
trpnl = trades.pnl
|
|
155
|
+
# Total trade profit/loss
|
|
156
|
+
trpnl.gross.total += trade.pnl
|
|
157
|
+
# Average profit/loss
|
|
158
|
+
trpnl.gross.average = trades.pnl.gross.total / trades.total.closed
|
|
159
|
+
# Net trade profit/loss
|
|
160
|
+
trpnl.net.total += trade.pnlcomm
|
|
161
|
+
# Average net profit/loss
|
|
162
|
+
trpnl.net.average = trades.pnl.net.total / trades.total.closed
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def _update_won_lost(trades, trade, res):
|
|
166
|
+
"""Won/Lost statistics: counts and pnl per win/loss bucket."""
|
|
167
|
+
for wlname in ["won", "lost"]:
|
|
168
|
+
# Current win/loss
|
|
169
|
+
wl = res[wlname]
|
|
170
|
+
# Historical win/loss
|
|
171
|
+
trwl = trades[wlname]
|
|
172
|
+
# Win/loss count
|
|
173
|
+
trwl.total += wl # won.total / lost.total
|
|
174
|
+
# Total and average profit/loss
|
|
175
|
+
trwlpnl = trwl.pnl
|
|
176
|
+
pnlcomm = trade.pnlcomm * wl
|
|
177
|
+
|
|
178
|
+
trwlpnl.total += pnlcomm
|
|
179
|
+
trwlpnl.average = trwlpnl.total / (trwl.total or 1.0)
|
|
180
|
+
# Maximum profit or minimum loss (largest losing trade)
|
|
181
|
+
wm = trwlpnl.max or 0.0
|
|
182
|
+
func = max if wlname == "won" else min
|
|
183
|
+
trwlpnl.max = func(wm, pnlcomm)
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def _update_long_short(trades, trade, res):
|
|
187
|
+
"""Long/Short statistics: counts and pnl per direction and win/loss."""
|
|
188
|
+
for tname in ["long", "short"]:
|
|
189
|
+
# Long and short
|
|
190
|
+
trls = trades[tname]
|
|
191
|
+
# Current trade's long and short
|
|
192
|
+
ls = res["t" + tname]
|
|
193
|
+
# Calculate long and short counts
|
|
194
|
+
trls.total += ls # long.total / short.total
|
|
195
|
+
# Calculate total pnl for long and short
|
|
196
|
+
trls.pnl.total += trade.pnlcomm * ls
|
|
197
|
+
# Calculate average profit for long and short
|
|
198
|
+
trls.pnl.average = trls.pnl.total / (trls.total or 1.0)
|
|
199
|
+
# Analyze win/loss status for long and short
|
|
200
|
+
for wlname in ["won", "lost"]:
|
|
201
|
+
wl = res[wlname]
|
|
202
|
+
pnlcomm = trade.pnlcomm * wl * ls
|
|
203
|
+
|
|
204
|
+
trls[wlname] += wl * ls # long.won / short.won
|
|
205
|
+
|
|
206
|
+
trls.pnl[wlname].total += pnlcomm
|
|
207
|
+
trls.pnl[wlname].average = trls.pnl[wlname].total / (trls[wlname] or 1.0)
|
|
208
|
+
|
|
209
|
+
wm = trls.pnl[wlname].max or 0.0
|
|
210
|
+
func = max if wlname == "won" else min
|
|
211
|
+
trls.pnl[wlname].max = func(wm, pnlcomm)
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def _update_length(trades, trade):
|
|
215
|
+
"""Length: total/average/max/min bars across all closed trades."""
|
|
216
|
+
# Number of bars occupied by trade
|
|
217
|
+
trades.len.total += trade.barlen
|
|
218
|
+
# Average number of bars per trade
|
|
219
|
+
trades.len.average = trades.len.total / trades.total.closed
|
|
220
|
+
# Maximum number of bars occupied by trade
|
|
221
|
+
ml = trades.len.max or 0
|
|
222
|
+
trades.len.max = max(ml, trade.barlen)
|
|
223
|
+
# Minimum number of bars occupied by trade
|
|
224
|
+
ml = trades.len.min or MAXINT
|
|
225
|
+
trades.len.min = min(ml, trade.barlen)
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def _update_length_won_lost(trades, trade, res):
|
|
229
|
+
"""Length split by win/loss bucket."""
|
|
230
|
+
# Number of bars for winning/losing trades, similar to above but separated by profit and loss
|
|
231
|
+
for wlname in ["won", "lost"]:
|
|
232
|
+
trwl = trades.len[wlname]
|
|
233
|
+
wl = res[wlname]
|
|
234
|
+
|
|
235
|
+
trwl.total += trade.barlen * wl
|
|
236
|
+
trwl.average = trwl.total / (trades[wlname].total or 1.0)
|
|
237
|
+
|
|
238
|
+
m = trwl.max or 0
|
|
239
|
+
trwl.max = max(m, trade.barlen * wl)
|
|
240
|
+
if trade.barlen * wl:
|
|
241
|
+
m = trwl.min or MAXINT
|
|
242
|
+
trwl.min = min(m, trade.barlen * wl)
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def _update_length_long_short(trades, trade, res):
|
|
246
|
+
"""Length split by direction and win/loss bucket."""
|
|
247
|
+
# Distinguish long and short lengths
|
|
248
|
+
for lsname in ["long", "short"]:
|
|
249
|
+
trls = trades.len[lsname] # trades.len.long
|
|
250
|
+
ls = res["t" + lsname] # tlong/tshort
|
|
251
|
+
|
|
252
|
+
barlen = trade.barlen * ls
|
|
253
|
+
|
|
254
|
+
trls.total += barlen # trades.len.long.total
|
|
255
|
+
total_ls = trades[lsname].total # trades.long.total
|
|
256
|
+
trls.average = trls.total / (total_ls or 1.0)
|
|
257
|
+
|
|
258
|
+
# max/min
|
|
259
|
+
m = trls.max or 0
|
|
260
|
+
trls.max = max(m, barlen)
|
|
261
|
+
m = trls.min or MAXINT
|
|
262
|
+
trls.min = min(m, barlen or m)
|
|
263
|
+
# Distinguish winning and losing lengths for long and short
|
|
264
|
+
for wlname in ["won", "lost"]:
|
|
265
|
+
wl = res[wlname] # won/lost
|
|
266
|
+
|
|
267
|
+
barlen2 = trade.barlen * ls * wl
|
|
268
|
+
|
|
269
|
+
trls_wl = trls[wlname] # trades.len.long.won
|
|
270
|
+
trls_wl.total += barlen2 # trades.len.long.won.total
|
|
271
|
+
|
|
272
|
+
trls_wl.average = trls_wl.total / (trades[lsname][wlname] or 1.0)
|
|
273
|
+
|
|
274
|
+
# max/min
|
|
275
|
+
m = trls_wl.max or 0
|
|
276
|
+
trls_wl.max = max(m, barlen2)
|
|
277
|
+
m = trls_wl.min or MAXINT
|
|
278
|
+
trls_wl.min = min(m, barlen2 or m)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Transactions Analyzer Module - Transaction logging.
|
|
3
|
+
|
|
4
|
+
This module provides the Transactions analyzer for recording all
|
|
5
|
+
transactions (order executions) during backtesting.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
Transactions: Analyzer that records transaction history.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> cerebro = bt.Cerebro()
|
|
12
|
+
>>> cerebro.addanalyzer(bt.analyzers.Transactions, _name='txn')
|
|
13
|
+
>>> results = cerebro.run()
|
|
14
|
+
>>> print(results[0].analyzers.txn.get_analysis())
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import collections
|
|
18
|
+
|
|
19
|
+
from ..analyzer import Analyzer
|
|
20
|
+
from ..order import Order
|
|
21
|
+
from ..position import Position
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Transactions
|
|
25
|
+
class Transactions(Analyzer):
|
|
26
|
+
"""This analyzer reports the transactions occurred with each every data in
|
|
27
|
+
the system
|
|
28
|
+
|
|
29
|
+
It looks at the order execution bits to create a `Position` starting from
|
|
30
|
+
0 during each `next` cycle.
|
|
31
|
+
|
|
32
|
+
The result is used during next to record the transactions
|
|
33
|
+
|
|
34
|
+
Params:
|
|
35
|
+
|
|
36
|
+
- Headers (default: ``True``)
|
|
37
|
+
|
|
38
|
+
Add an initial key to the dictionary holding the results with the names
|
|
39
|
+
of the datas
|
|
40
|
+
|
|
41
|
+
This analyzer was modeled to facilitate the integration with
|
|
42
|
+
``pyfolio``, and the header names are taken from the samples used for
|
|
43
|
+
it::
|
|
44
|
+
|
|
45
|
+
'Date', 'amount', 'price', 'sid', 'symbol', 'value'
|
|
46
|
+
|
|
47
|
+
Methods:
|
|
48
|
+
|
|
49
|
+
- Get_analysis
|
|
50
|
+
|
|
51
|
+
Returns a dictionary with returns as values and the datetime points for
|
|
52
|
+
each return as keys
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
# Parameters
|
|
56
|
+
params = (
|
|
57
|
+
("headers", False),
|
|
58
|
+
("_pfheaders", ("date", "amount", "price", "sid", "symbol", "value")),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Initialize
|
|
62
|
+
def __init__(self, *args, **kwargs):
|
|
63
|
+
"""Initialize the Transactions analyzer.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
*args: Positional arguments.
|
|
67
|
+
**kwargs: Keyword arguments for analyzer parameters.
|
|
68
|
+
"""
|
|
69
|
+
# CRITICAL FIX: Call super().__init__() first to initialize self.p
|
|
70
|
+
super().__init__(*args, **kwargs)
|
|
71
|
+
self._idnames = None
|
|
72
|
+
self._positions = None
|
|
73
|
+
|
|
74
|
+
def start(self):
|
|
75
|
+
"""Initialize the analyzer at the start of the backtest.
|
|
76
|
+
|
|
77
|
+
Sets up the results structure and initializes position tracking
|
|
78
|
+
for each data feed.
|
|
79
|
+
"""
|
|
80
|
+
super().start()
|
|
81
|
+
# If headers is True, initialize rets
|
|
82
|
+
if self.p.headers:
|
|
83
|
+
self.rets[self.p._pfheaders[0]] = [list(self.p._pfheaders[1:])]
|
|
84
|
+
# Positions
|
|
85
|
+
self._positions = collections.defaultdict(Position)
|
|
86
|
+
# Index and data names
|
|
87
|
+
self._idnames = list(enumerate(self.strategy.getdatanames()))
|
|
88
|
+
|
|
89
|
+
# Order information processing
|
|
90
|
+
def notify_order(self, order):
|
|
91
|
+
"""Process order execution notifications.
|
|
92
|
+
|
|
93
|
+
Updates the position tracking when orders are executed or partially
|
|
94
|
+
executed. Collected positions are recorded in the next() method.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
order: The order object with execution information.
|
|
98
|
+
"""
|
|
99
|
+
# An order could have several partial executions per cycle (unlikely
|
|
100
|
+
# but possible) and therefore: collect each new execution notification
|
|
101
|
+
# and let the work for the next
|
|
102
|
+
|
|
103
|
+
# We use a fresh Position object for each round to get a summary of what
|
|
104
|
+
# the execution bits have done in that round
|
|
105
|
+
# If order is not executed, ignore
|
|
106
|
+
if order.status not in [Order.Partial, Order.Completed]:
|
|
107
|
+
return # It's not an execution
|
|
108
|
+
# Get position of the data that generated the order
|
|
109
|
+
pos = self._positions[order.data._name]
|
|
110
|
+
# Loop
|
|
111
|
+
for exbit in order.executed.iterpending():
|
|
112
|
+
# If execution info is None, break
|
|
113
|
+
if exbit is None:
|
|
114
|
+
break # end of pending reached
|
|
115
|
+
# Update position information
|
|
116
|
+
pos.update(exbit.size, exbit.price)
|
|
117
|
+
|
|
118
|
+
# Called once per bar
|
|
119
|
+
def next(self):
|
|
120
|
+
"""Record transactions for the current bar.
|
|
121
|
+
|
|
122
|
+
Collects position changes from all data feeds and records them
|
|
123
|
+
in the results dictionary keyed by datetime.
|
|
124
|
+
"""
|
|
125
|
+
# super(Transactions, self).next() # let dtkey update
|
|
126
|
+
# Entries
|
|
127
|
+
entries = []
|
|
128
|
+
# For index and data names
|
|
129
|
+
for i, dname in self._idnames:
|
|
130
|
+
# Get position of the data
|
|
131
|
+
pos = self._positions.get(dname, None)
|
|
132
|
+
# If position is not None, if position is not 0, save position related data
|
|
133
|
+
if pos is not None:
|
|
134
|
+
size, price = pos.size, pos.price
|
|
135
|
+
if size:
|
|
136
|
+
entries.append([size, price, i, dname, -size * price])
|
|
137
|
+
# If position is not 0, update current bar's position data
|
|
138
|
+
if entries:
|
|
139
|
+
self.rets[self.strategy.datetime.datetime()] = entries
|
|
140
|
+
# Clear self._positions
|
|
141
|
+
self._positions.clear()
|