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,377 @@
|
|
|
1
|
+
"""Cerebro run orchestration mixin (iteration 28 split).
|
|
2
|
+
|
|
3
|
+
Moved verbatim from ``backtrader/cerebro.py``: strategy instantiation
|
|
4
|
+
preparation, runstrategies orchestration, writers and shared helpers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import itertools
|
|
8
|
+
import logging
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
from .. import errors, observers
|
|
12
|
+
from ..metabase import OwnerContext
|
|
13
|
+
from ..utils import OrderedDict, tzparse
|
|
14
|
+
from ..utils.log_message import _is_output_enabled_for, get_logger
|
|
15
|
+
from ..utils.py3 import integer_types
|
|
16
|
+
|
|
17
|
+
# Keep the historical logger name (D28-04.6): routing/filters must not change.
|
|
18
|
+
logger = get_logger("backtrader.cerebro")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ExecutionMixin:
|
|
22
|
+
"""Run orchestration half of Cerebro (see module docstring)."""
|
|
23
|
+
|
|
24
|
+
def _read_broker_lifecycle_value(self, accessor):
|
|
25
|
+
"""Read an optional broker summary without changing the run outcome.
|
|
26
|
+
|
|
27
|
+
Lifecycle INFO logging is opt-in. A custom broker can legitimately
|
|
28
|
+
expose cash/value only after ``start()`` or before ``stop()``. The
|
|
29
|
+
summary is diagnostic data, so an unavailable accessor must not make
|
|
30
|
+
a backtest fail.
|
|
31
|
+
"""
|
|
32
|
+
try:
|
|
33
|
+
return getattr(self._broker, accessor)()
|
|
34
|
+
except Exception:
|
|
35
|
+
# Broker implementations can raise third-party exceptions carrying
|
|
36
|
+
# account or transport details. This optional summary must never
|
|
37
|
+
# copy that unknown payload into a framework log.
|
|
38
|
+
logger.warning("broker %s unavailable for lifecycle logging", accessor)
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
def _read_data_lifecycle_length(self, data):
|
|
42
|
+
"""Read a data length only while its lifecycle is still active."""
|
|
43
|
+
try:
|
|
44
|
+
return len(data)
|
|
45
|
+
except Exception:
|
|
46
|
+
# ``data`` can be a live feed whose ``stop()`` releases buffers.
|
|
47
|
+
# Keep the event useful without writing a third-party traceback.
|
|
48
|
+
logger.warning("data length unavailable for lifecycle logging")
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def _read_lifecycle_count(values):
|
|
53
|
+
"""Read an optional count without consuming iterable strategy input."""
|
|
54
|
+
try:
|
|
55
|
+
return len(values)
|
|
56
|
+
except Exception:
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
# Initialize count
|
|
60
|
+
def _init_stcount(self):
|
|
61
|
+
self.stcount = itertools.count(0)
|
|
62
|
+
|
|
63
|
+
# Call next count
|
|
64
|
+
def _next_stid(self):
|
|
65
|
+
return next(self.stcount)
|
|
66
|
+
|
|
67
|
+
def _prepare_run(self, predata=False):
|
|
68
|
+
"""Start components and (optionally) preload data before strategies run.
|
|
69
|
+
|
|
70
|
+
Extracted from runstrategies() to keep that method readable. Starts
|
|
71
|
+
stores, applies cheat-on-open/fund/order-history settings, starts the
|
|
72
|
+
broker and feeds, writes CSV writer headers, and resets/preloads each
|
|
73
|
+
data feed unless ``predata`` is True.
|
|
74
|
+
"""
|
|
75
|
+
# Iterate stores and start
|
|
76
|
+
for store in self.stores:
|
|
77
|
+
store.start()
|
|
78
|
+
# If cheat_on_open and broker_coo, set broker accordingly
|
|
79
|
+
if self.p.cheat_on_open and self.p.broker_coo:
|
|
80
|
+
# try to activate in broker
|
|
81
|
+
if hasattr(self._broker, "set_coo"):
|
|
82
|
+
self._broker.set_coo(True)
|
|
83
|
+
# If fund history is not None, need to set fund history
|
|
84
|
+
if self._fhistory is not None:
|
|
85
|
+
self._broker.set_fund_history(self._fhistory)
|
|
86
|
+
# Iterate order history
|
|
87
|
+
for orders, onotify in self._ohistory:
|
|
88
|
+
self._broker.add_order_history(orders, onotify)
|
|
89
|
+
# Broker start
|
|
90
|
+
self._broker.start()
|
|
91
|
+
# Feed start
|
|
92
|
+
for feed in self.feeds:
|
|
93
|
+
feed.start()
|
|
94
|
+
# If need to save writer data
|
|
95
|
+
if self.writers_csv:
|
|
96
|
+
# headers
|
|
97
|
+
wheaders = []
|
|
98
|
+
# Iterate data, if data csv attribute is True, get headers that need saving
|
|
99
|
+
for data in self.datas:
|
|
100
|
+
if data.csv:
|
|
101
|
+
wheaders.extend(data.getwriterheaders())
|
|
102
|
+
# Save writer headers
|
|
103
|
+
for writer in self.runwriters:
|
|
104
|
+
if writer.p.csv:
|
|
105
|
+
writer.addheaders(wheaders)
|
|
106
|
+
|
|
107
|
+
# If no predata, need to pre-process data, similar to run method preprocessing
|
|
108
|
+
if not predata:
|
|
109
|
+
for data in self.datas:
|
|
110
|
+
data.reset()
|
|
111
|
+
if self._exactbars < 1: # datas can be a full length
|
|
112
|
+
data.extend(size=self.params.lookahead)
|
|
113
|
+
data._start()
|
|
114
|
+
if self._dopreload:
|
|
115
|
+
data.preload()
|
|
116
|
+
|
|
117
|
+
# Run strategy
|
|
118
|
+
def runstrategies(self, iterstrat, predata=False):
|
|
119
|
+
"""
|
|
120
|
+
Internal method invoked by ``run``` to run a set of strategies
|
|
121
|
+
"""
|
|
122
|
+
# Lifecycle diagnostics are opt-in and must not probe broker state
|
|
123
|
+
# before start()/after stop(). Some live/custom brokers deliberately
|
|
124
|
+
# reject those accessors outside their active lifecycle window.
|
|
125
|
+
log_lifecycle = _is_output_enabled_for(logging.INFO)
|
|
126
|
+
t0 = time.time() if log_lifecycle else None
|
|
127
|
+
initial_cash = None
|
|
128
|
+
self._init_stcount()
|
|
129
|
+
# Initialize running strategy as empty list
|
|
130
|
+
self.runningstrats = runstrats = []
|
|
131
|
+
# Start stores/broker/feeds, apply fund + order history, write headers
|
|
132
|
+
# and (optionally) preload data. Extracted for readability.
|
|
133
|
+
self._prepare_run(predata)
|
|
134
|
+
if log_lifecycle:
|
|
135
|
+
initial_cash = self._read_broker_lifecycle_value("getcash")
|
|
136
|
+
strategy_count = self._read_lifecycle_count(iterstrat)
|
|
137
|
+
logger.info(
|
|
138
|
+
"run starting: runonce=%s oldsync=%s strategies=%s datas=%d initial cash=%s",
|
|
139
|
+
self.p.runonce,
|
|
140
|
+
self.p.oldsync,
|
|
141
|
+
strategy_count if strategy_count is not None else "unavailable",
|
|
142
|
+
len(self.datas),
|
|
143
|
+
initial_cash if initial_cash is not None else "unavailable",
|
|
144
|
+
)
|
|
145
|
+
for data in self.datas:
|
|
146
|
+
name = getattr(data, "_name", "")
|
|
147
|
+
bars = self._read_data_lifecycle_length(data)
|
|
148
|
+
logger.info(
|
|
149
|
+
"data loaded: name=%s bars=%s",
|
|
150
|
+
name,
|
|
151
|
+
bars if bars is not None else "unavailable",
|
|
152
|
+
)
|
|
153
|
+
# Loop through strategies
|
|
154
|
+
for stratcls, sargs, skwargs in iterstrat:
|
|
155
|
+
# Add data to strategy parameters
|
|
156
|
+
sargs = self.datas + list(sargs)
|
|
157
|
+
# Instantiate strategy with OwnerContext so findowner() can find Cerebro
|
|
158
|
+
try:
|
|
159
|
+
# Use OwnerContext so Strategy.__new__ can find Cerebro via findowner()
|
|
160
|
+
with OwnerContext.set_owner(self):
|
|
161
|
+
# Use safe strategy creation to handle parameter filtering
|
|
162
|
+
if hasattr(stratcls, "_create_strategy_safely"):
|
|
163
|
+
strat = stratcls._create_strategy_safely(*sargs, **skwargs)
|
|
164
|
+
else:
|
|
165
|
+
# Fallback to direct instantiation
|
|
166
|
+
strat = stratcls(*sargs, **skwargs)
|
|
167
|
+
except errors.StrategySkipError:
|
|
168
|
+
logger.warning("execution:122 suppressed bare")
|
|
169
|
+
continue # do not add strategy to the mix
|
|
170
|
+
# Old data synchronization method
|
|
171
|
+
if self.p.oldsync:
|
|
172
|
+
strat._oldsync = True # tell strategy to use old clock update
|
|
173
|
+
# Whether to save trade history data
|
|
174
|
+
if self.p.tradehistory:
|
|
175
|
+
strat.set_tradehistory()
|
|
176
|
+
# Add strategy
|
|
177
|
+
runstrats.append(strat)
|
|
178
|
+
# Get timezone info, if tz is integer, get tz at that index; otherwise use tzparse
|
|
179
|
+
tz = self.p.tz
|
|
180
|
+
if isinstance(tz, integer_types):
|
|
181
|
+
tz = self.datas[tz]._tz
|
|
182
|
+
else:
|
|
183
|
+
tz = tzparse(tz)
|
|
184
|
+
# StrategySkipError may exclude every strategy; cleanup still runs.
|
|
185
|
+
run_exception = None
|
|
186
|
+
# If runstrats is not empty list
|
|
187
|
+
if runstrats:
|
|
188
|
+
# loop separated for clarity
|
|
189
|
+
# Get default sizer
|
|
190
|
+
defaultsizer = self.sizers.get(None, (None, None, None))
|
|
191
|
+
# For each strategy
|
|
192
|
+
for idx, strat in enumerate(runstrats):
|
|
193
|
+
# If stdstats is True, add several observers
|
|
194
|
+
if self.p.stdstats:
|
|
195
|
+
# Add observer broker
|
|
196
|
+
strat._addobserver(False, observers.Broker)
|
|
197
|
+
# Add observers.BuySell
|
|
198
|
+
if self.p.oldbuysell:
|
|
199
|
+
strat._addobserver(True, observers.BuySell)
|
|
200
|
+
else:
|
|
201
|
+
strat._addobserver(True, observers.BuySell, barplot=True)
|
|
202
|
+
# Add observer trade
|
|
203
|
+
if self.p.oldtrades or len(self.datas) == 1:
|
|
204
|
+
strat._addobserver(False, observers.Trades)
|
|
205
|
+
else:
|
|
206
|
+
strat._addobserver(False, observers.DataTrades)
|
|
207
|
+
# Add observers and their parameters to strategy
|
|
208
|
+
for multi, obscls, obsargs, obskwargs in self.observers:
|
|
209
|
+
strat._addobserver(multi, obscls, *obsargs, **obskwargs)
|
|
210
|
+
# Add indicators to strategy
|
|
211
|
+
for indcls, indargs, indkwargs in self.indicators:
|
|
212
|
+
strat._addindicator(indcls, *indargs, **indkwargs)
|
|
213
|
+
# Add analyzers to strategy
|
|
214
|
+
for ancls, anargs, ankwargs in self.analyzers:
|
|
215
|
+
strat._addanalyzer(ancls, *anargs, **ankwargs)
|
|
216
|
+
# Get specific sizer, if sizer is not None, add to strategy
|
|
217
|
+
sizer, sargs, skwargs = self.sizers.get(idx, defaultsizer)
|
|
218
|
+
if sizer is not None:
|
|
219
|
+
strat._addsizer(sizer, *sargs, **skwargs)
|
|
220
|
+
# Set timezone
|
|
221
|
+
strat._settz(tz)
|
|
222
|
+
# Strategy start
|
|
223
|
+
strat._start()
|
|
224
|
+
# For running writers, if csv parameter is True, save strategy data to writer
|
|
225
|
+
for writer in self.runwriters:
|
|
226
|
+
if writer.p.csv:
|
|
227
|
+
writer.addheaders(strat.getwriterheaders())
|
|
228
|
+
# If predata is False, data not preloaded
|
|
229
|
+
if not predata:
|
|
230
|
+
# Loop each strategy, call qbuffer to cache data
|
|
231
|
+
for strat in runstrats:
|
|
232
|
+
strat.qbuffer(self._exactbars, replaying=self._doreplay)
|
|
233
|
+
# Loop each writer, start writer
|
|
234
|
+
for writer in self.runwriters:
|
|
235
|
+
writer.start()
|
|
236
|
+
|
|
237
|
+
# Prepare timers
|
|
238
|
+
self._timers = []
|
|
239
|
+
self._timerscheat = []
|
|
240
|
+
# Loop timers
|
|
241
|
+
for timer in self._pretimers:
|
|
242
|
+
# preprocess tzdata if needed
|
|
243
|
+
# Start timer
|
|
244
|
+
timer.start(self.datas[0])
|
|
245
|
+
# If timer parameter cheat is True, add timer to self._timerscheat, otherwise add to self._timers
|
|
246
|
+
if timer.params.cheat:
|
|
247
|
+
self._timerscheat.append(timer)
|
|
248
|
+
else:
|
|
249
|
+
self._timers.append(timer)
|
|
250
|
+
# Run the main loop; keep cleanup deterministic, but never turn a
|
|
251
|
+
# strategy/runtime exception into a successful empty backtest.
|
|
252
|
+
try:
|
|
253
|
+
# If _dopreload and _dorunonce are True
|
|
254
|
+
if self._dopreload and self._dorunonce:
|
|
255
|
+
# If old data alignment and sync method, use _runonce_old, otherwise use _runonce
|
|
256
|
+
if self.p.oldsync:
|
|
257
|
+
self._runonce_old(runstrats)
|
|
258
|
+
else:
|
|
259
|
+
self._runonce(runstrats)
|
|
260
|
+
# If _dopreload and _dorunonce are not both True
|
|
261
|
+
else:
|
|
262
|
+
# If old data alignment and sync method, use _runnext_old, otherwise use _runnext
|
|
263
|
+
if self.p.oldsync:
|
|
264
|
+
self._runnext_old(runstrats)
|
|
265
|
+
else:
|
|
266
|
+
self._runnext(runstrats)
|
|
267
|
+
except Exception as exc:
|
|
268
|
+
run_exception = exc
|
|
269
|
+
logger.exception("Unhandled exception in run loop, cleaning up before re-raising")
|
|
270
|
+
finally:
|
|
271
|
+
# Iterate strategies and stop running (always runs)
|
|
272
|
+
for strat in runstrats:
|
|
273
|
+
strat._stop()
|
|
274
|
+
# Capture the final value while the broker is still active. This is
|
|
275
|
+
# only diagnostic data, so a custom broker may decline the accessor.
|
|
276
|
+
final_value = None
|
|
277
|
+
final_bars = None
|
|
278
|
+
if run_exception is None and log_lifecycle:
|
|
279
|
+
final_value = self._read_broker_lifecycle_value("getvalue")
|
|
280
|
+
final_bars = self._read_data_lifecycle_length(self.datas[0]) if self.datas else 0
|
|
281
|
+
|
|
282
|
+
# Stop broker
|
|
283
|
+
self._broker.stop()
|
|
284
|
+
# If predata is False, iterate data and stop each data
|
|
285
|
+
if not predata:
|
|
286
|
+
for data in self.datas:
|
|
287
|
+
data.stop()
|
|
288
|
+
# Iterate each feed and stop feed
|
|
289
|
+
for feed in self.feeds:
|
|
290
|
+
feed.stop()
|
|
291
|
+
# Iterate each store and stop store
|
|
292
|
+
for store in self.stores:
|
|
293
|
+
if getattr(store, "_cerebro_managed_lifecycle", True) is False:
|
|
294
|
+
continue
|
|
295
|
+
store.stop()
|
|
296
|
+
# Stop writer
|
|
297
|
+
self.stop_writers(runstrats)
|
|
298
|
+
if run_exception is None and log_lifecycle:
|
|
299
|
+
if final_value is None or initial_cash is None:
|
|
300
|
+
pnl = "unavailable"
|
|
301
|
+
else:
|
|
302
|
+
try:
|
|
303
|
+
pnl = final_value - initial_cash
|
|
304
|
+
except Exception:
|
|
305
|
+
logger.warning("broker pnl unavailable for lifecycle logging")
|
|
306
|
+
pnl = "unavailable"
|
|
307
|
+
logger.info(
|
|
308
|
+
"run finished: final value=%s pnl=%s bars=%d elapsed=%.2fs",
|
|
309
|
+
final_value if final_value is not None else "unavailable",
|
|
310
|
+
pnl,
|
|
311
|
+
final_bars if final_bars is not None else 0,
|
|
312
|
+
time.time() - t0,
|
|
313
|
+
)
|
|
314
|
+
if run_exception is not None:
|
|
315
|
+
raise run_exception
|
|
316
|
+
# If doing parameter optimization and optreturn is True, build lightweight
|
|
317
|
+
# OptReturn results (detached from data) instead of full strategy objects.
|
|
318
|
+
if self._dooptimize and self.p.optreturn:
|
|
319
|
+
return self._build_optreturn_results(runstrats)
|
|
320
|
+
|
|
321
|
+
return runstrats
|
|
322
|
+
|
|
323
|
+
# Stop writer
|
|
324
|
+
def stop_writers(self, runstrats):
|
|
325
|
+
"""Stop all writers and write final information.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
runstrats: List of strategy instances that were run.
|
|
329
|
+
|
|
330
|
+
Collects information from data feeds and strategies, writes
|
|
331
|
+
the information to all registered writers, and stops them.
|
|
332
|
+
"""
|
|
333
|
+
# Cerebro info
|
|
334
|
+
cerebroinfo = OrderedDict()
|
|
335
|
+
# Data info
|
|
336
|
+
datainfos = OrderedDict()
|
|
337
|
+
# Get info for each data, save to datainfos, then save to cerebroinfo
|
|
338
|
+
for i, data in enumerate(self.datas):
|
|
339
|
+
datainfos["Data%d" % i] = data.getwriterinfo()
|
|
340
|
+
|
|
341
|
+
cerebroinfo["Datas"] = datainfos
|
|
342
|
+
# Get strategy info and save to stratinfos and cerebroinfo
|
|
343
|
+
stratinfos = {}
|
|
344
|
+
for strat in runstrats:
|
|
345
|
+
stname = strat.__class__.__name__
|
|
346
|
+
stratinfos[stname] = strat.getwriterinfo()
|
|
347
|
+
|
|
348
|
+
cerebroinfo["Strategies"] = stratinfos
|
|
349
|
+
# Write cerebroinfo to file
|
|
350
|
+
for writer in self.runwriters:
|
|
351
|
+
writer.writedict({"Cerebro": cerebroinfo})
|
|
352
|
+
writer.stop()
|
|
353
|
+
|
|
354
|
+
# Run writer's next
|
|
355
|
+
def _next_writers(self, runstrats):
|
|
356
|
+
if not self.runwriters:
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
if self.writers_csv:
|
|
360
|
+
wvalues = []
|
|
361
|
+
for data in self.datas:
|
|
362
|
+
if data.csv:
|
|
363
|
+
wvalues.extend(data.getwritervalues())
|
|
364
|
+
|
|
365
|
+
for strat in runstrats:
|
|
366
|
+
wvalues.extend(strat.getwritervalues())
|
|
367
|
+
|
|
368
|
+
for writer in self.runwriters:
|
|
369
|
+
if writer.p.csv:
|
|
370
|
+
writer.addvalues(wvalues)
|
|
371
|
+
|
|
372
|
+
writer.next()
|
|
373
|
+
|
|
374
|
+
# Disable runonce
|
|
375
|
+
def _disable_runonce(self):
|
|
376
|
+
"""API for lineiterators to disable runonce (see HeikinAshi)"""
|
|
377
|
+
self._dorunonce = False
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Cerebro run-scope lifecycle mixin (iteration 28 split).
|
|
2
|
+
|
|
3
|
+
Moved verbatim from ``backtrader/cerebro.py``: run scope begin/end,
|
|
4
|
+
external channel scope retention and runstop publication.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# pylint: disable=no-member
|
|
8
|
+
# Mixin state (``_run_scope_token`` etc.) is created by ``Cerebro.__init__``
|
|
9
|
+
# on the assembled class; it cannot be seen from this partial class alone.
|
|
10
|
+
import threading
|
|
11
|
+
|
|
12
|
+
from ..utils.log_message import get_logger
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RunLifecycleMixin:
|
|
18
|
+
"""Run-scope lifecycle half of Cerebro (see module docstring)."""
|
|
19
|
+
|
|
20
|
+
def _begin_run(self):
|
|
21
|
+
"""Start one synchronized run-stop scope for this Cerebro instance."""
|
|
22
|
+
with self._runstop_lock:
|
|
23
|
+
if self._run_active:
|
|
24
|
+
raise RuntimeError("Cerebro is already running")
|
|
25
|
+
self._event_stop.clear()
|
|
26
|
+
self._run_scope_token += 1
|
|
27
|
+
self._run_scope_owner = threading.get_ident()
|
|
28
|
+
self._run_active = True
|
|
29
|
+
return self._run_scope_token
|
|
30
|
+
|
|
31
|
+
def _open_run_scope(self):
|
|
32
|
+
"""Open a run scope and roll it back if an overridden start hook fails."""
|
|
33
|
+
with self._runstop_lock:
|
|
34
|
+
previous_token = self._run_scope_token
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
self._begin_run()
|
|
38
|
+
with self._runstop_lock:
|
|
39
|
+
if not self._run_active or self._run_scope_owner != threading.get_ident():
|
|
40
|
+
raise RuntimeError("Cerebro run scope was not published by the calling thread")
|
|
41
|
+
return self._run_scope_token
|
|
42
|
+
except BaseException:
|
|
43
|
+
# A subclass can call ``super()._begin_run()`` and then fail. Only
|
|
44
|
+
# retire a scope created by this thread after the snapshot; never
|
|
45
|
+
# clear another thread's active run after a rejected re-entry.
|
|
46
|
+
logger.error("lifecycle:41 exception before re-raise (BaseException)", exc_info=True)
|
|
47
|
+
self._end_run_if_started_by_current_thread(previous_token)
|
|
48
|
+
raise
|
|
49
|
+
|
|
50
|
+
def _end_run_if_started_by_current_thread(self, previous_token):
|
|
51
|
+
"""Undo a partially opened scope without touching a different active run."""
|
|
52
|
+
with self._runstop_lock:
|
|
53
|
+
if (
|
|
54
|
+
self._run_active
|
|
55
|
+
and self._run_scope_owner == threading.get_ident()
|
|
56
|
+
and self._run_scope_token != previous_token
|
|
57
|
+
):
|
|
58
|
+
self._retire_run_scope_locked()
|
|
59
|
+
|
|
60
|
+
def _retire_run_scope_locked(self):
|
|
61
|
+
"""Clear one active run scope while ``_runstop_lock`` is held."""
|
|
62
|
+
self._run_active = False
|
|
63
|
+
self._run_scope_owner = None
|
|
64
|
+
self._event_stop.clear()
|
|
65
|
+
self._external_channel_token = None
|
|
66
|
+
self._external_channel_runstrats = None
|
|
67
|
+
self._external_channel_closing = False
|
|
68
|
+
|
|
69
|
+
def _end_run(self, token):
|
|
70
|
+
"""Retire only this caller's run-stop scope.
|
|
71
|
+
|
|
72
|
+
A timer that fires after another run has already opened remains an
|
|
73
|
+
ordinary stop request for that later active scope; callers must cancel
|
|
74
|
+
or generation-bind such timers before reusing the instance.
|
|
75
|
+
"""
|
|
76
|
+
with self._runstop_lock:
|
|
77
|
+
if (
|
|
78
|
+
not self._run_active
|
|
79
|
+
or self._run_scope_owner != threading.get_ident()
|
|
80
|
+
or self._run_scope_token != token
|
|
81
|
+
):
|
|
82
|
+
return
|
|
83
|
+
self._retire_run_scope_locked()
|
|
84
|
+
|
|
85
|
+
def _retain_external_channel_scope(self, token, runstrats):
|
|
86
|
+
"""Keep a ``run(channel=True)`` session active until its owner closes it."""
|
|
87
|
+
with self._runstop_lock:
|
|
88
|
+
if (
|
|
89
|
+
not self._run_active
|
|
90
|
+
or self._run_scope_owner != threading.get_ident()
|
|
91
|
+
or self._run_scope_token != token
|
|
92
|
+
):
|
|
93
|
+
raise RuntimeError("Cerebro external channel scope was not published by its owner")
|
|
94
|
+
self._external_channel_token = token
|
|
95
|
+
self._external_channel_runstrats = runstrats
|
|
96
|
+
self._external_channel_closing = False
|
|
97
|
+
|
|
98
|
+
def close_channel(self):
|
|
99
|
+
"""Tear down an external ``run(channel=True)`` session on its owner thread.
|
|
100
|
+
|
|
101
|
+
``runstop()`` only publishes a stop request. The thread which called
|
|
102
|
+
``run(channel=True)`` must call this method after its external driver
|
|
103
|
+
has stopped dispatching callbacks. This keeps broker and strategy
|
|
104
|
+
teardown out of foreign Timer or worker threads.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
``True`` if an external channel session was closed, otherwise
|
|
108
|
+
``False`` when no such session is active.
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
RuntimeError: If a different thread tries to close the active
|
|
112
|
+
external channel session.
|
|
113
|
+
"""
|
|
114
|
+
with self._runstop_lock:
|
|
115
|
+
token = self._external_channel_token
|
|
116
|
+
if token is None or not self._run_active or self._run_scope_token != token:
|
|
117
|
+
return False
|
|
118
|
+
if self._run_scope_owner != threading.get_ident():
|
|
119
|
+
raise RuntimeError("Cerebro external channel must be closed by its owner thread")
|
|
120
|
+
if self._external_channel_closing:
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
self._external_channel_closing = True
|
|
124
|
+
self._event_stop.set()
|
|
125
|
+
runstrats = self._external_channel_runstrats
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
self._teardown_channel(runstrats)
|
|
129
|
+
finally:
|
|
130
|
+
self._end_run(token)
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
# When called from within a strategy or elsewhere, stops execution quickly
|
|
134
|
+
def runstop(self):
|
|
135
|
+
"""Request prompt termination of the currently active run.
|
|
136
|
+
|
|
137
|
+
Calls from a strategy or another thread are safe. Calls made while
|
|
138
|
+
no ``run`` / optimization worker is active are ignored so a delayed
|
|
139
|
+
``threading.Timer`` cannot stop a later, unrelated run.
|
|
140
|
+
"""
|
|
141
|
+
with self._runstop_lock:
|
|
142
|
+
if self._run_active:
|
|
143
|
+
self._event_stop.set()
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Cerebro notification dispatch mixin (iteration 28 split).
|
|
2
|
+
|
|
3
|
+
Moved verbatim from ``backtrader/cerebro.py``: store/data callbacks and
|
|
4
|
+
broker notification delivery.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from ..brokers import BackBroker
|
|
8
|
+
from ..feed import AbstractDataBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NotificationMixin:
|
|
12
|
+
"""Notification dispatch half of Cerebro (see module docstring)."""
|
|
13
|
+
|
|
14
|
+
def addstorecb(self, callback):
|
|
15
|
+
"""Adds a callback to get messages which would be handled by the
|
|
16
|
+
notify_store method
|
|
17
|
+
|
|
18
|
+
The signature of the callback must support the following:
|
|
19
|
+
|
|
20
|
+
- callback(msg, *args, *kwargs)
|
|
21
|
+
|
|
22
|
+
The actual ``msg``, ``*args`` and ``**kwargs`` received are
|
|
23
|
+
implementation defined (depend entirely on the *data/broker/store*) but
|
|
24
|
+
in general one should expect them to be *printable* to allow for
|
|
25
|
+
reception and experimentation.
|
|
26
|
+
"""
|
|
27
|
+
self.storecbs.append(callback)
|
|
28
|
+
|
|
29
|
+
def _notify_store(self, msg, *args, **kwargs):
|
|
30
|
+
"""Internal method to dispatch store notifications."""
|
|
31
|
+
for callback in self.storecbs:
|
|
32
|
+
callback(msg, *args, **kwargs)
|
|
33
|
+
|
|
34
|
+
self.notify_store(msg, *args, **kwargs)
|
|
35
|
+
|
|
36
|
+
def notify_store(self, msg, *args, **kwargs):
|
|
37
|
+
"""Receive store notifications in cerebro
|
|
38
|
+
|
|
39
|
+
This method can be overridden in ``Cerebro`` subclasses
|
|
40
|
+
|
|
41
|
+
The actual ``msg``, ``*args`` and ``**kwargs`` received are
|
|
42
|
+
implementation defined (depend entirely on the *data/broker/store*) but
|
|
43
|
+
in general one should expect them to be *printable* to allow for
|
|
44
|
+
reception and experimentation.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def _storenotify(self):
|
|
48
|
+
"""Process and dispatch store notifications to strategies."""
|
|
49
|
+
for store in self.stores:
|
|
50
|
+
for notif in store.get_notifications():
|
|
51
|
+
msg, args, kwargs = notif
|
|
52
|
+
|
|
53
|
+
self._notify_store(msg, *args, **kwargs)
|
|
54
|
+
for strat in self.runningstrats:
|
|
55
|
+
strat.notify_store(msg, *args, **kwargs)
|
|
56
|
+
if hasattr(strat, "_notify_store_to_observers"):
|
|
57
|
+
strat._notify_store_to_observers(msg, *args, **kwargs)
|
|
58
|
+
|
|
59
|
+
def adddatacb(self, callback):
|
|
60
|
+
"""Adds a callback to get messages which would be handled by the
|
|
61
|
+
notify_data method
|
|
62
|
+
|
|
63
|
+
The signature of the callback must support the following:
|
|
64
|
+
|
|
65
|
+
- callback(data, status, *args, *kwargs)
|
|
66
|
+
|
|
67
|
+
The actual ``*args`` and ``**kwargs`` received are implementation
|
|
68
|
+
defined (depend entirely on the *data/broker/store*), but in general one
|
|
69
|
+
should expect them to be *printable* to allow for reception and
|
|
70
|
+
experimentation.
|
|
71
|
+
"""
|
|
72
|
+
self.datacbs.append(callback)
|
|
73
|
+
|
|
74
|
+
def _datanotify(self):
|
|
75
|
+
"""Process and dispatch data notifications to strategies."""
|
|
76
|
+
for data in self.datas:
|
|
77
|
+
if type(data).get_notifications is AbstractDataBase.get_notifications:
|
|
78
|
+
notifications = data.notifs
|
|
79
|
+
if not notifications:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
notifications.append(None)
|
|
83
|
+
while True:
|
|
84
|
+
notif = notifications.popleft()
|
|
85
|
+
if notif is None:
|
|
86
|
+
break
|
|
87
|
+
status, args, kwargs = notif
|
|
88
|
+
self._notify_data(data, status, *args, **kwargs)
|
|
89
|
+
for strat in self.runningstrats:
|
|
90
|
+
strat.notify_data(data, status, *args, **kwargs)
|
|
91
|
+
if hasattr(strat, "_notify_data_to_observers"):
|
|
92
|
+
strat._notify_data_to_observers(data, status, *args, **kwargs)
|
|
93
|
+
else:
|
|
94
|
+
for notif in data.get_notifications():
|
|
95
|
+
status, args, kwargs = notif
|
|
96
|
+
self._notify_data(data, status, *args, **kwargs)
|
|
97
|
+
for strat in self.runningstrats:
|
|
98
|
+
strat.notify_data(data, status, *args, **kwargs)
|
|
99
|
+
if hasattr(strat, "_notify_data_to_observers"):
|
|
100
|
+
strat._notify_data_to_observers(data, status, *args, **kwargs)
|
|
101
|
+
|
|
102
|
+
def _notify_data(self, data, status, *args, **kwargs):
|
|
103
|
+
"""Internal method to dispatch data notifications."""
|
|
104
|
+
for callback in self.datacbs:
|
|
105
|
+
callback(data, status, *args, **kwargs)
|
|
106
|
+
|
|
107
|
+
self.notify_data(data, status, *args, **kwargs)
|
|
108
|
+
|
|
109
|
+
def notify_data(self, data, status, *args, **kwargs):
|
|
110
|
+
"""Receive data notifications in cerebro
|
|
111
|
+
|
|
112
|
+
This method can be overridden in ``Cerebro`` subclasses
|
|
113
|
+
|
|
114
|
+
The actual ``*args`` and ``**kwargs`` received are
|
|
115
|
+
implementation defined (depend entirely on the *data/broker/store*), but
|
|
116
|
+
in general one should expect them to be *printable* to allow for
|
|
117
|
+
reception and experimentation.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
# Notify broker info
|
|
121
|
+
def _brokernotify(self):
|
|
122
|
+
"""
|
|
123
|
+
Internal method which kicks the broker and delivers any broker
|
|
124
|
+
notification to the strategy
|
|
125
|
+
"""
|
|
126
|
+
# Call broker's next
|
|
127
|
+
broker = self._broker
|
|
128
|
+
broker.next()
|
|
129
|
+
if type(broker).get_notification is BackBroker.get_notification:
|
|
130
|
+
notifications = broker.notifs
|
|
131
|
+
while notifications:
|
|
132
|
+
order = notifications.popleft()
|
|
133
|
+
owner = order.owner
|
|
134
|
+
if owner is None:
|
|
135
|
+
owner = self.runningstrats[0] # default
|
|
136
|
+
# Notify order info through first strategy
|
|
137
|
+
owner._addnotification(order, quicknotify=self.p.quicknotify)
|
|
138
|
+
else:
|
|
139
|
+
while True:
|
|
140
|
+
# Get order info to notify, if order is None break loop, otherwise get order's owner.
|
|
141
|
+
# If owner is None, default to first strategy
|
|
142
|
+
order = broker.get_notification()
|
|
143
|
+
if order is None:
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
owner = order.owner
|
|
147
|
+
if owner is None:
|
|
148
|
+
owner = self.runningstrats[0] # default
|
|
149
|
+
# Notify order info through first strategy
|
|
150
|
+
owner._addnotification(order, quicknotify=self.p.quicknotify)
|