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,120 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Analyzer tab.
|
|
4
|
+
|
|
5
|
+
Displays results of all analyzers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ...utils.log_message import get_logger
|
|
9
|
+
from ..tab import BokehTab
|
|
10
|
+
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from bokeh.layouts import column
|
|
15
|
+
from bokeh.models import ColumnDataSource, DataTable, TableColumn
|
|
16
|
+
from bokeh.models.widgets import Div
|
|
17
|
+
|
|
18
|
+
BOKEH_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
BOKEH_AVAILABLE = False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AnalyzerTab(BokehTab):
|
|
24
|
+
"""Analyzer tab.
|
|
25
|
+
|
|
26
|
+
Displays analysis results of all analyzers in the strategy.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def _is_useable(self):
|
|
30
|
+
"""Check if useable.
|
|
31
|
+
|
|
32
|
+
Useable when strategy has analyzers.
|
|
33
|
+
"""
|
|
34
|
+
if not BOKEH_AVAILABLE:
|
|
35
|
+
return False
|
|
36
|
+
strategy = self.strategy
|
|
37
|
+
if strategy is None:
|
|
38
|
+
return False
|
|
39
|
+
return len(getattr(strategy, "analyzers", [])) > 0
|
|
40
|
+
|
|
41
|
+
def _get_panel(self):
|
|
42
|
+
"""Get panel content.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
tuple: (widget, title)
|
|
46
|
+
"""
|
|
47
|
+
strategy = self.strategy
|
|
48
|
+
scheme = self.scheme
|
|
49
|
+
|
|
50
|
+
# Create analyzer results display
|
|
51
|
+
widgets = []
|
|
52
|
+
|
|
53
|
+
for analyzer in strategy.analyzers:
|
|
54
|
+
analyzer_name = analyzer.__class__.__name__
|
|
55
|
+
|
|
56
|
+
# Get analysis results
|
|
57
|
+
try:
|
|
58
|
+
analysis = analyzer.get_analysis()
|
|
59
|
+
except Exception:
|
|
60
|
+
logger.warning("analyzer:56 fallback on Exception")
|
|
61
|
+
analysis = {}
|
|
62
|
+
|
|
63
|
+
# Create title
|
|
64
|
+
title_div = Div(
|
|
65
|
+
text=f'<h3 style="color: {scheme.text_color if scheme else "#333"};">{analyzer_name}</h3>',
|
|
66
|
+
sizing_mode="stretch_width",
|
|
67
|
+
)
|
|
68
|
+
widgets.append(title_div)
|
|
69
|
+
|
|
70
|
+
# Convert analysis results to table data
|
|
71
|
+
data = self._flatten_analysis(analysis)
|
|
72
|
+
|
|
73
|
+
if data:
|
|
74
|
+
source = ColumnDataSource(
|
|
75
|
+
data={"key": list(data.keys()), "value": [str(v) for v in data.values()]}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
columns = [
|
|
79
|
+
TableColumn(field="key", title="Metric"),
|
|
80
|
+
TableColumn(field="value", title="Value"),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
table = DataTable(
|
|
84
|
+
source=source,
|
|
85
|
+
columns=columns,
|
|
86
|
+
width=400,
|
|
87
|
+
height=min(len(data) * 25 + 30, 300),
|
|
88
|
+
index_position=None,
|
|
89
|
+
)
|
|
90
|
+
widgets.append(table)
|
|
91
|
+
else:
|
|
92
|
+
empty_div = Div(text="<p>No data available</p>")
|
|
93
|
+
widgets.append(empty_div)
|
|
94
|
+
|
|
95
|
+
content = column(*widgets, sizing_mode="stretch_width")
|
|
96
|
+
return content, "Analyzers"
|
|
97
|
+
|
|
98
|
+
def _flatten_analysis(self, analysis, prefix=""):
|
|
99
|
+
"""Flatten nested analysis results.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
analysis: Analysis result dictionary
|
|
103
|
+
prefix: Key prefix
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
dict: Flattened dictionary
|
|
107
|
+
"""
|
|
108
|
+
result = {}
|
|
109
|
+
|
|
110
|
+
if isinstance(analysis, dict):
|
|
111
|
+
for key, value in analysis.items():
|
|
112
|
+
new_key = f"{prefix}.{key}" if prefix else str(key)
|
|
113
|
+
if isinstance(value, dict):
|
|
114
|
+
result.update(self._flatten_analysis(value, new_key))
|
|
115
|
+
else:
|
|
116
|
+
result[new_key] = value
|
|
117
|
+
else:
|
|
118
|
+
result[prefix or "value"] = analysis
|
|
119
|
+
|
|
120
|
+
return result
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Configuration tab.
|
|
4
|
+
|
|
5
|
+
Displays configuration info for strategy and data.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from backtrader.utils.log_message import get_logger
|
|
9
|
+
|
|
10
|
+
from ..tab import BokehTab
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from bokeh.layouts import column
|
|
16
|
+
from bokeh.models import ColumnDataSource, DataTable, TableColumn
|
|
17
|
+
from bokeh.models.widgets import Div
|
|
18
|
+
|
|
19
|
+
BOKEH_AVAILABLE = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
BOKEH_AVAILABLE = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ConfigTab(BokehTab):
|
|
25
|
+
"""Configuration tab.
|
|
26
|
+
|
|
27
|
+
Displays strategy parameters, data configuration and other info.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def _is_useable(self):
|
|
31
|
+
"""Config tab is always useable."""
|
|
32
|
+
return BOKEH_AVAILABLE
|
|
33
|
+
|
|
34
|
+
def _get_panel(self):
|
|
35
|
+
"""Get panel content.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
tuple: (widget, title)
|
|
39
|
+
"""
|
|
40
|
+
strategy = self.strategy
|
|
41
|
+
scheme = self.scheme
|
|
42
|
+
|
|
43
|
+
widgets = []
|
|
44
|
+
text_color = scheme.text_color if scheme else "#333"
|
|
45
|
+
|
|
46
|
+
# Strategy parameters + data feeds
|
|
47
|
+
if strategy is not None:
|
|
48
|
+
widgets.extend(self._build_params_widgets(strategy, text_color))
|
|
49
|
+
widgets.extend(self._build_data_widgets(strategy, text_color))
|
|
50
|
+
|
|
51
|
+
if not widgets:
|
|
52
|
+
widgets.append(Div(text="<p>No configuration available</p>"))
|
|
53
|
+
|
|
54
|
+
content = column(*widgets, sizing_mode="stretch_width")
|
|
55
|
+
return content, "Config"
|
|
56
|
+
|
|
57
|
+
def _build_params_widgets(self, strategy, text_color):
|
|
58
|
+
"""Build the strategy-name heading + parameters table widgets."""
|
|
59
|
+
widgets = []
|
|
60
|
+
# Strategy name
|
|
61
|
+
strategy_name = strategy.__class__.__name__
|
|
62
|
+
widgets.append(
|
|
63
|
+
Div(
|
|
64
|
+
text=f'<h3 style="color: {text_color};">Strategy: {strategy_name}</h3>',
|
|
65
|
+
sizing_mode="stretch_width",
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Strategy parameters
|
|
70
|
+
params = {}
|
|
71
|
+
if hasattr(strategy, "params"):
|
|
72
|
+
for name in dir(strategy.params):
|
|
73
|
+
if not name.startswith("_"):
|
|
74
|
+
try:
|
|
75
|
+
value = getattr(strategy.params, name)
|
|
76
|
+
if not callable(value):
|
|
77
|
+
params[name] = value
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.warning("Failed to get param '%s': %s", name, e)
|
|
80
|
+
|
|
81
|
+
if params:
|
|
82
|
+
source = ColumnDataSource(
|
|
83
|
+
data={
|
|
84
|
+
"parameter": list(params.keys()),
|
|
85
|
+
"value": [str(v) for v in params.values()],
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
columns = [
|
|
90
|
+
TableColumn(field="parameter", title="Parameter"),
|
|
91
|
+
TableColumn(field="value", title="Value"),
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
table = DataTable(
|
|
95
|
+
source=source,
|
|
96
|
+
columns=columns,
|
|
97
|
+
width=400,
|
|
98
|
+
height=min(len(params) * 25 + 30, 300),
|
|
99
|
+
index_position=None,
|
|
100
|
+
)
|
|
101
|
+
widgets.append(table)
|
|
102
|
+
return widgets
|
|
103
|
+
|
|
104
|
+
def _build_data_widgets(self, strategy, text_color):
|
|
105
|
+
"""Build the data-feeds heading + data-info table widgets."""
|
|
106
|
+
widgets = []
|
|
107
|
+
if hasattr(strategy, "datas") and strategy.datas:
|
|
108
|
+
widgets.append(
|
|
109
|
+
Div(
|
|
110
|
+
text=f'<h3 style="color: {text_color};">Data Feeds</h3>',
|
|
111
|
+
sizing_mode="stretch_width",
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
data_info = []
|
|
116
|
+
for i, data in enumerate(strategy.datas):
|
|
117
|
+
name = data._name if hasattr(data, "_name") else f"Data{i}"
|
|
118
|
+
length = len(data) if hasattr(data, "__len__") else "N/A"
|
|
119
|
+
data_info.append(
|
|
120
|
+
{
|
|
121
|
+
"name": name,
|
|
122
|
+
"length": str(length),
|
|
123
|
+
"timeframe": str(
|
|
124
|
+
getattr(data._timeframe, "name", "N/A")
|
|
125
|
+
if hasattr(data, "_timeframe")
|
|
126
|
+
else "N/A"
|
|
127
|
+
),
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if data_info:
|
|
132
|
+
source = ColumnDataSource(
|
|
133
|
+
data={
|
|
134
|
+
"name": [d["name"] for d in data_info],
|
|
135
|
+
"length": [d["length"] for d in data_info],
|
|
136
|
+
"timeframe": [d["timeframe"] for d in data_info],
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
columns = [
|
|
141
|
+
TableColumn(field="name", title="Name"),
|
|
142
|
+
TableColumn(field="length", title="Length"),
|
|
143
|
+
TableColumn(field="timeframe", title="Timeframe"),
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
table = DataTable(
|
|
147
|
+
source=source,
|
|
148
|
+
columns=columns,
|
|
149
|
+
width=400,
|
|
150
|
+
height=min(len(data_info) * 25 + 30, 200),
|
|
151
|
+
index_position=None,
|
|
152
|
+
)
|
|
153
|
+
widgets.append(table)
|
|
154
|
+
return widgets
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Live configuration tab.
|
|
4
|
+
For configuration adjustments in live plotting mode.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from ..tab import BokehTab
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from bokeh.layouts import column
|
|
11
|
+
from bokeh.models.widgets import Button, Div, Select, Slider
|
|
12
|
+
|
|
13
|
+
BOKEH_AVAILABLE = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
BOKEH_AVAILABLE = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LiveTab(BokehTab):
|
|
19
|
+
"""Live configuration tab.
|
|
20
|
+
|
|
21
|
+
Provides configuration options for live plotting mode:
|
|
22
|
+
- Lookback adjustment
|
|
23
|
+
- Auto-refresh interval
|
|
24
|
+
- Filter options
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def _is_useable(self):
|
|
28
|
+
"""Useable in live mode."""
|
|
29
|
+
if not BOKEH_AVAILABLE:
|
|
30
|
+
return False
|
|
31
|
+
return self._client is not None
|
|
32
|
+
|
|
33
|
+
def _get_panel(self):
|
|
34
|
+
"""Get panel content.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
tuple: (widget, title)
|
|
38
|
+
"""
|
|
39
|
+
scheme = self.scheme
|
|
40
|
+
text_color = scheme.text_color if scheme else "#333"
|
|
41
|
+
|
|
42
|
+
widgets = []
|
|
43
|
+
|
|
44
|
+
# Title
|
|
45
|
+
widgets.append(
|
|
46
|
+
Div(
|
|
47
|
+
text=f'<h3 style="color: {text_color};">Live Plot Settings</h3>',
|
|
48
|
+
sizing_mode="stretch_width",
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Lookback settings
|
|
53
|
+
lookback_value = getattr(self._client, "lookback", 100) if self._client else 100
|
|
54
|
+
lookback_slider = Slider(
|
|
55
|
+
start=10, end=500, value=lookback_value, step=10, title="Lookback (candles)", width=300
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def on_lookback_change(attr, old, new):
|
|
59
|
+
if self._client is not None:
|
|
60
|
+
self._client.lookback = new
|
|
61
|
+
|
|
62
|
+
lookback_slider.on_change("value", on_lookback_change)
|
|
63
|
+
widgets.append(lookback_slider)
|
|
64
|
+
|
|
65
|
+
# Fill Gaps option
|
|
66
|
+
fill_gaps = getattr(self._client, "fill_gaps", False) if self._client else False
|
|
67
|
+
fill_gaps_select = Select(
|
|
68
|
+
title="Fill Gaps", value="Yes" if fill_gaps else "No", options=["Yes", "No"], width=150
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def on_fill_gaps_change(attr, old, new):
|
|
72
|
+
if self._client is not None:
|
|
73
|
+
self._client.fill_gaps = new == "Yes"
|
|
74
|
+
|
|
75
|
+
fill_gaps_select.on_change("value", on_fill_gaps_change)
|
|
76
|
+
widgets.append(fill_gaps_select)
|
|
77
|
+
|
|
78
|
+
# Plot Group settings
|
|
79
|
+
plotgroup = getattr(self._client, "plotgroup", "") if self._client else ""
|
|
80
|
+
plotgroup_select = Select(
|
|
81
|
+
title="Plot Group",
|
|
82
|
+
value=plotgroup or "All",
|
|
83
|
+
options=["All"], # Can dynamically add more options
|
|
84
|
+
width=200,
|
|
85
|
+
)
|
|
86
|
+
widgets.append(plotgroup_select)
|
|
87
|
+
|
|
88
|
+
# Refresh button
|
|
89
|
+
refresh_btn = Button(label="Refresh", button_type="primary", width=100)
|
|
90
|
+
|
|
91
|
+
def on_refresh_click():
|
|
92
|
+
if self._client is not None:
|
|
93
|
+
self._client.updatemodel()
|
|
94
|
+
|
|
95
|
+
refresh_btn.on_click(on_refresh_click)
|
|
96
|
+
widgets.append(refresh_btn)
|
|
97
|
+
|
|
98
|
+
# Status info
|
|
99
|
+
status_text = (
|
|
100
|
+
"Running" if self._client and not getattr(self._client, "_paused", True) else "Paused"
|
|
101
|
+
)
|
|
102
|
+
status_div = Div(
|
|
103
|
+
text=f'<p style="color: {text_color};">Status: <strong>{status_text}</strong></p>',
|
|
104
|
+
sizing_mode="stretch_width",
|
|
105
|
+
)
|
|
106
|
+
widgets.append(status_div)
|
|
107
|
+
|
|
108
|
+
content = column(*widgets, sizing_mode="stretch_width")
|
|
109
|
+
return content, "Live"
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Log tab.
|
|
4
|
+
|
|
5
|
+
Displays strategy execution logs.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from collections import deque
|
|
10
|
+
|
|
11
|
+
from ..tab import BokehTab
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from bokeh.layouts import column
|
|
15
|
+
from bokeh.models import ColumnDataSource, DataTable, TableColumn
|
|
16
|
+
from bokeh.models.widgets import Div
|
|
17
|
+
|
|
18
|
+
BOKEH_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
BOKEH_AVAILABLE = False
|
|
21
|
+
|
|
22
|
+
# Global log storage
|
|
23
|
+
_log_storage: dict = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LogHandler(logging.Handler):
|
|
27
|
+
"""Log handler.
|
|
28
|
+
|
|
29
|
+
Captures log messages and stores them in specified storage.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, storage_key, max_records=1000):
|
|
33
|
+
"""Initialize log handler.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
storage_key: Key to identify log storage in global dictionary.
|
|
37
|
+
max_records: Maximum number of log records to keep (default: 1000).
|
|
38
|
+
"""
|
|
39
|
+
super().__init__()
|
|
40
|
+
self.storage_key = storage_key
|
|
41
|
+
self.max_records = max_records
|
|
42
|
+
if storage_key not in _log_storage:
|
|
43
|
+
_log_storage[storage_key] = deque(maxlen=max_records)
|
|
44
|
+
|
|
45
|
+
def emit(self, record):
|
|
46
|
+
"""Emit a log record.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
record: Log record to process.
|
|
50
|
+
"""
|
|
51
|
+
log_entry = {
|
|
52
|
+
"time": self.format(record).split(" - ")[0] if " - " in self.format(record) else "",
|
|
53
|
+
"level": record.levelname,
|
|
54
|
+
"message": record.getMessage(),
|
|
55
|
+
}
|
|
56
|
+
_log_storage[self.storage_key].append(log_entry)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def getlogger(name="backtrader", col=None):
|
|
60
|
+
"""Get logger with log handler.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
name: Logger name
|
|
64
|
+
col: Custom columns (optional)
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
logging.Logger instance
|
|
68
|
+
"""
|
|
69
|
+
logger = logging.getLogger(name)
|
|
70
|
+
|
|
71
|
+
# Check if LogHandler already added
|
|
72
|
+
has_handler = any(isinstance(h, LogHandler) for h in logger.handlers)
|
|
73
|
+
if not has_handler:
|
|
74
|
+
handler = LogHandler(name)
|
|
75
|
+
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
76
|
+
logger.addHandler(handler)
|
|
77
|
+
|
|
78
|
+
return logger
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class LogTab(BokehTab):
|
|
82
|
+
"""Log tab.
|
|
83
|
+
|
|
84
|
+
Displays log information during strategy execution.
|
|
85
|
+
|
|
86
|
+
Attributes:
|
|
87
|
+
cols: Column configuration for display
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
cols = ["Time", "Level", "Message"] # Default columns
|
|
91
|
+
|
|
92
|
+
def __init__(self, app, figurepage, client=None, cols=None):
|
|
93
|
+
"""Initialize log tab.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
app: Bokeh application instance.
|
|
97
|
+
figurepage: Figure page instance.
|
|
98
|
+
client: Optional client instance.
|
|
99
|
+
cols: Custom column configuration for log display.
|
|
100
|
+
"""
|
|
101
|
+
super().__init__(app, figurepage, client)
|
|
102
|
+
if cols is not None:
|
|
103
|
+
self.cols = cols
|
|
104
|
+
|
|
105
|
+
def _is_useable(self):
|
|
106
|
+
"""Log tab is always useable."""
|
|
107
|
+
return BOKEH_AVAILABLE
|
|
108
|
+
|
|
109
|
+
def _get_panel(self):
|
|
110
|
+
"""Get panel content.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
tuple: (widget, title)
|
|
114
|
+
"""
|
|
115
|
+
scheme = self.scheme
|
|
116
|
+
text_color = scheme.text_color if scheme else "#333"
|
|
117
|
+
|
|
118
|
+
widgets = []
|
|
119
|
+
|
|
120
|
+
# Title
|
|
121
|
+
widgets.append(
|
|
122
|
+
Div(
|
|
123
|
+
text=f'<h3 style="color: {text_color};">Log Messages</h3>',
|
|
124
|
+
sizing_mode="stretch_width",
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Get log data
|
|
129
|
+
log_data = self._get_log_data()
|
|
130
|
+
|
|
131
|
+
if log_data:
|
|
132
|
+
# Create data source
|
|
133
|
+
source = ColumnDataSource(data=log_data)
|
|
134
|
+
|
|
135
|
+
# Create columns
|
|
136
|
+
columns = []
|
|
137
|
+
for col_name in log_data.keys():
|
|
138
|
+
columns.append(TableColumn(field=col_name, title=col_name.capitalize()))
|
|
139
|
+
|
|
140
|
+
# Create table
|
|
141
|
+
table = DataTable(
|
|
142
|
+
source=source, columns=columns, width=800, height=400, index_position=None
|
|
143
|
+
)
|
|
144
|
+
widgets.append(table)
|
|
145
|
+
else:
|
|
146
|
+
widgets.append(Div(text="<p>No log messages available</p>"))
|
|
147
|
+
|
|
148
|
+
content = column(*widgets, sizing_mode="stretch_width")
|
|
149
|
+
return content, "Log"
|
|
150
|
+
|
|
151
|
+
def _get_log_data(self):
|
|
152
|
+
"""Get log data.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
dict: Log data dictionary
|
|
156
|
+
"""
|
|
157
|
+
# Get data from global log storage
|
|
158
|
+
all_logs = []
|
|
159
|
+
for key, logs in _log_storage.items():
|
|
160
|
+
all_logs.extend(list(logs))
|
|
161
|
+
|
|
162
|
+
if not all_logs:
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
# Sort by time (newest first)
|
|
166
|
+
all_logs = list(reversed(all_logs))
|
|
167
|
+
|
|
168
|
+
# Convert to column data format
|
|
169
|
+
return {
|
|
170
|
+
"time": [log.get("time", "") for log in all_logs],
|
|
171
|
+
"level": [log.get("level", "") for log in all_logs],
|
|
172
|
+
"message": [log.get("message", "") for log in all_logs],
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def LogTabs(cols):
|
|
177
|
+
"""Create log tab class with custom columns.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
cols: Column configuration
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Custom LogTab class
|
|
184
|
+
"""
|
|
185
|
+
return type("LogTab", (LogTab,), {"cols": cols})
|