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
backtrader/channel.py
ADDED
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
"""Data channel infrastructure for tick-level backtesting.
|
|
2
|
+
|
|
3
|
+
This module provides the core channel system:
|
|
4
|
+
- Event / EventPriority: Event wrapper with priority for queue ordering
|
|
5
|
+
- DataChannel: Base class for all data channels (Tick, OrderBook, Funding)
|
|
6
|
+
- DataValidationResult: Result container for data validation
|
|
7
|
+
- StreamingEventQueue: Memory-efficient event queue with adaptive preloading
|
|
8
|
+
- ChannelSharingMode: Enum for channel sharing between strategies
|
|
9
|
+
|
|
10
|
+
The channel system is independent of LineSeries and uses deque for buffering,
|
|
11
|
+
enabling high-frequency event processing without the overhead of the
|
|
12
|
+
traditional bar-based data system.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
Basic event queue usage::
|
|
16
|
+
|
|
17
|
+
queue = StreamingEventQueue(channels=[tick_channel], bars=[])
|
|
18
|
+
while not queue.empty:
|
|
19
|
+
event = queue.pop()
|
|
20
|
+
process(event)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import heapq
|
|
24
|
+
from collections import deque
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from enum import Enum, IntEnum
|
|
27
|
+
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
|
28
|
+
|
|
29
|
+
from .utils.log_message import get_logger
|
|
30
|
+
|
|
31
|
+
logger = get_logger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EventPriority(IntEnum):
|
|
35
|
+
"""Event processing priority (lower value = higher priority).
|
|
36
|
+
|
|
37
|
+
Within the same timestamp, events are processed in priority order.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
SYSTEM = 0
|
|
41
|
+
FUNDING = 10
|
|
42
|
+
TICK = 20
|
|
43
|
+
ORDERBOOK = 30
|
|
44
|
+
BAR = 40
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(order=True)
|
|
48
|
+
class Event:
|
|
49
|
+
"""Event wrapper for queue ordering.
|
|
50
|
+
|
|
51
|
+
Events are ordered by (timestamp, priority, sequence) for deterministic
|
|
52
|
+
processing order. The data field is excluded from comparison.
|
|
53
|
+
|
|
54
|
+
Attributes:
|
|
55
|
+
timestamp: Event timestamp (seconds).
|
|
56
|
+
priority: Processing priority (lower = first).
|
|
57
|
+
sequence: Insertion sequence number for stable sort.
|
|
58
|
+
channel_type: Source channel type identifier.
|
|
59
|
+
channel_name: Source channel/symbol name.
|
|
60
|
+
data: The actual event data (EventData subclass instance).
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
timestamp: float = 0.0
|
|
64
|
+
priority: int = field(default=EventPriority.TICK)
|
|
65
|
+
sequence: int = field(default=0, compare=True)
|
|
66
|
+
channel_type: str = field(default="", compare=False)
|
|
67
|
+
channel_name: str = field(default="", compare=False)
|
|
68
|
+
data: Any = field(default=None, compare=False, repr=False)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ChannelDataRef:
|
|
72
|
+
"""Lightweight data reference for channel-only strategies.
|
|
73
|
+
|
|
74
|
+
Channel-mode HFT strategies submit orders through the standard
|
|
75
|
+
``Strategy.buy`` / ``Strategy.sell`` APIs but may not have LineSeries
|
|
76
|
+
data feeds. This object provides the minimal data identity expected by
|
|
77
|
+
brokers and order objects: ``_name``, ``name`` and ``symbol``.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, symbol, channel_name=None):
|
|
81
|
+
self._name = str(symbol)
|
|
82
|
+
self.name = str(symbol)
|
|
83
|
+
self.symbol = str(symbol)
|
|
84
|
+
self.channel_name = str(channel_name or symbol)
|
|
85
|
+
|
|
86
|
+
def __repr__(self):
|
|
87
|
+
return f"ChannelDataRef(symbol={self.symbol!r})"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ChannelSharingMode(Enum):
|
|
91
|
+
"""Channel sharing mode between strategies.
|
|
92
|
+
|
|
93
|
+
- EXCLUSIVE: One strategy owns the channel exclusively.
|
|
94
|
+
- SHARED_READONLY: Multiple strategies can read, none can modify.
|
|
95
|
+
- SHARED_ISOLATED: Each strategy gets its own cursor/state.
|
|
96
|
+
- SHARED_FULL: Full sharing with write access (use with caution).
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
EXCLUSIVE = "exclusive"
|
|
100
|
+
SHARED_READONLY = "shared_readonly"
|
|
101
|
+
SHARED_ISOLATED = "shared_isolated"
|
|
102
|
+
SHARED_FULL = "shared_full"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class DataValidationResult:
|
|
107
|
+
"""Result of data validation.
|
|
108
|
+
|
|
109
|
+
Attributes:
|
|
110
|
+
valid: Whether the data passed validation.
|
|
111
|
+
errors: List of error descriptions.
|
|
112
|
+
warnings: List of warning descriptions.
|
|
113
|
+
auto_fixed: Whether the data was auto-fixed.
|
|
114
|
+
fixed_fields: Dict of field names to (original, fixed) values.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
valid: bool = True
|
|
118
|
+
errors: List[str] = field(default_factory=list)
|
|
119
|
+
warnings: List[str] = field(default_factory=list)
|
|
120
|
+
auto_fixed: bool = False
|
|
121
|
+
fixed_fields: Dict[str, Tuple[Any, Any]] = field(default_factory=dict)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class DataChannel:
|
|
125
|
+
"""Base class for all data channels.
|
|
126
|
+
|
|
127
|
+
Channels provide buffered, validated data streams independent of
|
|
128
|
+
the LineSeries system. Each channel type (Tick, OrderBook, Funding)
|
|
129
|
+
subclasses DataChannel and implements type-specific validation.
|
|
130
|
+
|
|
131
|
+
Attributes:
|
|
132
|
+
symbol: Trading pair symbol.
|
|
133
|
+
channel_type: Channel type identifier (set by subclasses).
|
|
134
|
+
maxlen: Maximum buffer size.
|
|
135
|
+
sharing_mode: How the channel is shared between strategies.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
symbol: Trading pair symbol (e.g., 'BTC/USDT').
|
|
139
|
+
maxlen: Maximum number of events to buffer.
|
|
140
|
+
validate: Whether to validate incoming events.
|
|
141
|
+
auto_fix: Whether to attempt auto-fixing invalid data.
|
|
142
|
+
sharing_mode: Channel sharing mode.
|
|
143
|
+
**kwargs: Additional channel parameters.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
channel_type = "generic"
|
|
147
|
+
|
|
148
|
+
def __init__(
|
|
149
|
+
self,
|
|
150
|
+
symbol,
|
|
151
|
+
maxlen=10000,
|
|
152
|
+
validate=True,
|
|
153
|
+
auto_fix=True,
|
|
154
|
+
sharing_mode=ChannelSharingMode.SHARED_READONLY,
|
|
155
|
+
**kwargs,
|
|
156
|
+
):
|
|
157
|
+
"""Initialize a DataChannel.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
symbol: Trading pair symbol (e.g., 'BTC/USDT').
|
|
161
|
+
maxlen: Maximum number of events to buffer.
|
|
162
|
+
validate: Whether to validate incoming events.
|
|
163
|
+
auto_fix: Whether to attempt auto-fixing invalid data.
|
|
164
|
+
sharing_mode: Channel sharing mode between strategies.
|
|
165
|
+
**kwargs: Additional channel parameters.
|
|
166
|
+
"""
|
|
167
|
+
self.symbol = symbol
|
|
168
|
+
self.maxlen = maxlen
|
|
169
|
+
self.params = kwargs
|
|
170
|
+
self._buffer: deque = deque(maxlen=maxlen)
|
|
171
|
+
self._event_count = 0
|
|
172
|
+
self._validate = validate
|
|
173
|
+
self._auto_fix = auto_fix
|
|
174
|
+
self._validation_errors = []
|
|
175
|
+
self._last_timestamp = None
|
|
176
|
+
self.sharing_mode = sharing_mode
|
|
177
|
+
self._strategy_states = {}
|
|
178
|
+
self._dataname = kwargs.get("dataname")
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def latest(self):
|
|
182
|
+
"""Get the most recent event in the buffer."""
|
|
183
|
+
return self._buffer[-1] if self._buffer else None
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def event_count(self):
|
|
187
|
+
"""Total number of events pushed to this channel."""
|
|
188
|
+
return self._event_count
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def buffer_size(self):
|
|
192
|
+
"""Current number of events in the buffer."""
|
|
193
|
+
return len(self._buffer)
|
|
194
|
+
|
|
195
|
+
def push(self, event) -> bool:
|
|
196
|
+
"""Push an event into the channel buffer.
|
|
197
|
+
|
|
198
|
+
If validation is enabled, the event is validated (and potentially
|
|
199
|
+
auto-fixed) before being added to the buffer.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
event: An EventData subclass instance.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
True if the event was accepted, False if rejected.
|
|
206
|
+
"""
|
|
207
|
+
if self._validate:
|
|
208
|
+
result = self._validate_event(event)
|
|
209
|
+
if not result.valid:
|
|
210
|
+
self._validation_errors.append(result)
|
|
211
|
+
return False
|
|
212
|
+
if result.auto_fixed:
|
|
213
|
+
logger.debug("Auto-fixed event for %s: %s", self.symbol, result.fixed_fields)
|
|
214
|
+
|
|
215
|
+
self._buffer.append(event)
|
|
216
|
+
self._event_count += 1
|
|
217
|
+
self._last_timestamp = event.timestamp
|
|
218
|
+
return True
|
|
219
|
+
|
|
220
|
+
def _validate_event(self, event) -> DataValidationResult:
|
|
221
|
+
"""Validate an event. Override in subclasses for type-specific checks.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
event: The event to validate.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
DataValidationResult with validation outcome.
|
|
228
|
+
"""
|
|
229
|
+
result = DataValidationResult()
|
|
230
|
+
|
|
231
|
+
# Basic validation
|
|
232
|
+
if not hasattr(event, "timestamp"):
|
|
233
|
+
result.valid = False
|
|
234
|
+
result.errors.append("Event missing timestamp")
|
|
235
|
+
return result
|
|
236
|
+
|
|
237
|
+
if not hasattr(event, "validate") or not event.validate():
|
|
238
|
+
result.valid = False
|
|
239
|
+
result.errors.append("Event failed built-in validation")
|
|
240
|
+
return result
|
|
241
|
+
|
|
242
|
+
# Timestamp ordering check
|
|
243
|
+
if self._last_timestamp is not None:
|
|
244
|
+
if event.timestamp < self._last_timestamp:
|
|
245
|
+
if self._auto_fix:
|
|
246
|
+
result.warnings.append(
|
|
247
|
+
f"Out-of-order timestamp: {event.timestamp} < {self._last_timestamp}"
|
|
248
|
+
)
|
|
249
|
+
result.auto_fixed = True
|
|
250
|
+
result.fixed_fields["timestamp"] = (event.timestamp, self._last_timestamp)
|
|
251
|
+
event.timestamp = self._last_timestamp
|
|
252
|
+
else:
|
|
253
|
+
result.valid = False
|
|
254
|
+
result.errors.append(
|
|
255
|
+
f"Out-of-order timestamp: {event.timestamp} < {self._last_timestamp}"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
return result
|
|
259
|
+
|
|
260
|
+
def get_state(self, strategy_id: str) -> dict:
|
|
261
|
+
"""Get per-strategy state for isolated sharing mode.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
strategy_id: Unique identifier for the strategy.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Dict containing the strategy's channel state.
|
|
268
|
+
"""
|
|
269
|
+
if strategy_id not in self._strategy_states:
|
|
270
|
+
self._strategy_states[strategy_id] = {
|
|
271
|
+
"cursor": 0,
|
|
272
|
+
"last_event": None,
|
|
273
|
+
}
|
|
274
|
+
return self._strategy_states[strategy_id]
|
|
275
|
+
|
|
276
|
+
def load(self) -> Iterator:
|
|
277
|
+
"""Load events from the data source.
|
|
278
|
+
|
|
279
|
+
Subclasses should override this to implement data loading from
|
|
280
|
+
files, databases, or other sources.
|
|
281
|
+
|
|
282
|
+
Yields:
|
|
283
|
+
EventData instances.
|
|
284
|
+
"""
|
|
285
|
+
raise NotImplementedError(f"{self.__class__.__name__} must implement load()")
|
|
286
|
+
|
|
287
|
+
def get_validation_errors(self) -> List[DataValidationResult]:
|
|
288
|
+
"""Get all validation errors encountered."""
|
|
289
|
+
return list(self._validation_errors)
|
|
290
|
+
|
|
291
|
+
def clear_validation_errors(self):
|
|
292
|
+
"""Clear accumulated validation errors."""
|
|
293
|
+
self._validation_errors.clear()
|
|
294
|
+
|
|
295
|
+
def __len__(self):
|
|
296
|
+
"""Return the current number of events in the buffer.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
int: Number of events currently buffered.
|
|
300
|
+
"""
|
|
301
|
+
return len(self._buffer)
|
|
302
|
+
|
|
303
|
+
def __iter__(self):
|
|
304
|
+
"""Iterate over events in the buffer.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
Iterator over events in insertion order.
|
|
308
|
+
"""
|
|
309
|
+
return iter(self._buffer)
|
|
310
|
+
|
|
311
|
+
def __repr__(self):
|
|
312
|
+
"""Return a string representation of the channel.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
str: Representation showing symbol, type, and event counts.
|
|
316
|
+
"""
|
|
317
|
+
return (
|
|
318
|
+
f"{self.__class__.__name__}(symbol={self.symbol!r}, "
|
|
319
|
+
f"type={self.channel_type!r}, "
|
|
320
|
+
f"events={self._event_count}, "
|
|
321
|
+
f"buffered={len(self._buffer)})"
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class StreamingEventQueue:
|
|
326
|
+
"""Memory-efficient event queue with adaptive preloading.
|
|
327
|
+
|
|
328
|
+
Merges events from multiple channels and bar data sources into a
|
|
329
|
+
single ordered stream. Uses a heap (priority queue) for efficient
|
|
330
|
+
merging and supports adaptive preload windows to control memory usage.
|
|
331
|
+
|
|
332
|
+
The queue loads data in chunks rather than all at once, keeping
|
|
333
|
+
memory usage bounded regardless of total data size.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
channels: List of DataChannel instances.
|
|
337
|
+
bars: List of bar data iterators.
|
|
338
|
+
preload_window: Initial preload time window in seconds.
|
|
339
|
+
max_memory_mb: Maximum memory target in MB.
|
|
340
|
+
adaptive: Whether to adapt the preload window based on memory.
|
|
341
|
+
batch_size: Number of events to load per batch from each channel.
|
|
342
|
+
|
|
343
|
+
Example::
|
|
344
|
+
|
|
345
|
+
queue = StreamingEventQueue(
|
|
346
|
+
channels=[tick_channel, ob_channel],
|
|
347
|
+
bars=[bar_data],
|
|
348
|
+
preload_window=300.0,
|
|
349
|
+
max_memory_mb=200,
|
|
350
|
+
adaptive=True
|
|
351
|
+
)
|
|
352
|
+
while not queue.empty:
|
|
353
|
+
event = queue.pop()
|
|
354
|
+
# process event
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
def __init__(
|
|
358
|
+
self,
|
|
359
|
+
channels=None,
|
|
360
|
+
bars=None,
|
|
361
|
+
preload_window=300.0,
|
|
362
|
+
max_memory_mb=200,
|
|
363
|
+
adaptive=True,
|
|
364
|
+
batch_size=10000,
|
|
365
|
+
):
|
|
366
|
+
"""Initialize a StreamingEventQueue.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
channels: List of DataChannel instances.
|
|
370
|
+
bars: List of bar data iterators.
|
|
371
|
+
preload_window: Initial preload time window in seconds.
|
|
372
|
+
max_memory_mb: Maximum memory target in MB.
|
|
373
|
+
adaptive: Whether to adapt the preload window based on memory.
|
|
374
|
+
batch_size: Number of events to load per batch from each channel.
|
|
375
|
+
"""
|
|
376
|
+
self._channels = channels or []
|
|
377
|
+
self._bars = bars or []
|
|
378
|
+
self._window = preload_window
|
|
379
|
+
self._max_memory = max_memory_mb * 1024 * 1024
|
|
380
|
+
self._adaptive = adaptive
|
|
381
|
+
self._batch_size = batch_size
|
|
382
|
+
|
|
383
|
+
# Heap: (timestamp, priority, sequence, Event)
|
|
384
|
+
self._heap = []
|
|
385
|
+
self._sequence = 0
|
|
386
|
+
|
|
387
|
+
# Tracking
|
|
388
|
+
self._current_ts = float("-inf")
|
|
389
|
+
self._preload_up_to = float("-inf")
|
|
390
|
+
|
|
391
|
+
# Channel iterators (lazy initialized)
|
|
392
|
+
self._channel_iters = {}
|
|
393
|
+
self._bar_iters = {}
|
|
394
|
+
self._channel_exhausted = set()
|
|
395
|
+
self._bar_exhausted = set()
|
|
396
|
+
|
|
397
|
+
# Adaptive window parameters
|
|
398
|
+
self._window_min = 60.0
|
|
399
|
+
self._window_max = 600.0
|
|
400
|
+
self._adjustment_count = 0
|
|
401
|
+
|
|
402
|
+
# Statistics
|
|
403
|
+
self._total_events_popped = 0
|
|
404
|
+
|
|
405
|
+
# Initialize iterators
|
|
406
|
+
self._init_iterators()
|
|
407
|
+
# Initial preload
|
|
408
|
+
self._ensure_preload()
|
|
409
|
+
|
|
410
|
+
def _init_iterators(self):
|
|
411
|
+
"""Initialize iterators for all channels and bar data."""
|
|
412
|
+
for i, channel in enumerate(self._channels):
|
|
413
|
+
try:
|
|
414
|
+
self._channel_iters[i] = iter(channel.load())
|
|
415
|
+
except Exception as e:
|
|
416
|
+
logger.warning("Failed to init iterator for channel %s: %s", channel, e)
|
|
417
|
+
self._channel_exhausted.add(i)
|
|
418
|
+
|
|
419
|
+
for i, bar_data in enumerate(self._bars):
|
|
420
|
+
try:
|
|
421
|
+
self._bar_iters[i] = iter(bar_data)
|
|
422
|
+
except Exception as e:
|
|
423
|
+
logger.warning("Failed to init iterator for bar data %d: %s", i, e)
|
|
424
|
+
self._bar_exhausted.add(i)
|
|
425
|
+
|
|
426
|
+
def _ensure_preload(self):
|
|
427
|
+
"""Ensure events are preloaded up to the current window boundary.
|
|
428
|
+
|
|
429
|
+
Loads events from all channels and bar data sources up to the target
|
|
430
|
+
timestamp (current timestamp + preload window). Skips already-loaded
|
|
431
|
+
ranges to avoid redundant work.
|
|
432
|
+
"""
|
|
433
|
+
if self._current_ts == float("-inf"):
|
|
434
|
+
# Initial load: use inf to load the first batch fully
|
|
435
|
+
target_ts = float("inf")
|
|
436
|
+
else:
|
|
437
|
+
target_ts = self._current_ts + self._window
|
|
438
|
+
if target_ts <= self._preload_up_to:
|
|
439
|
+
return
|
|
440
|
+
|
|
441
|
+
# Load from channels
|
|
442
|
+
for idx, ch_iter in list(self._channel_iters.items()):
|
|
443
|
+
if idx in self._channel_exhausted:
|
|
444
|
+
continue
|
|
445
|
+
self._load_from_channel(idx, ch_iter, target_ts)
|
|
446
|
+
|
|
447
|
+
# Load from bar data
|
|
448
|
+
for idx, bar_iter in list(self._bar_iters.items()):
|
|
449
|
+
if idx in self._bar_exhausted:
|
|
450
|
+
continue
|
|
451
|
+
self._load_from_bars(idx, bar_iter, target_ts)
|
|
452
|
+
|
|
453
|
+
self._preload_up_to = target_ts
|
|
454
|
+
|
|
455
|
+
def _load_from_channel(self, idx, ch_iter, target_ts):
|
|
456
|
+
"""Load events from a channel iterator up to target timestamp.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
idx: Index of the channel.
|
|
460
|
+
ch_iter: Iterator for the channel.
|
|
461
|
+
target_ts: Target timestamp to load up to.
|
|
462
|
+
"""
|
|
463
|
+
channel = self._channels[idx]
|
|
464
|
+
loaded = 0
|
|
465
|
+
|
|
466
|
+
try:
|
|
467
|
+
while loaded < self._batch_size:
|
|
468
|
+
event = next(ch_iter)
|
|
469
|
+
if not hasattr(event, "timestamp"):
|
|
470
|
+
continue
|
|
471
|
+
|
|
472
|
+
wrapped = Event(
|
|
473
|
+
timestamp=event.timestamp,
|
|
474
|
+
priority=self._get_channel_priority(channel),
|
|
475
|
+
sequence=self._sequence,
|
|
476
|
+
channel_type=channel.channel_type,
|
|
477
|
+
channel_name=channel.symbol,
|
|
478
|
+
data=event,
|
|
479
|
+
)
|
|
480
|
+
self._sequence += 1
|
|
481
|
+
heapq.heappush(self._heap, wrapped)
|
|
482
|
+
loaded += 1
|
|
483
|
+
|
|
484
|
+
if event.timestamp > target_ts:
|
|
485
|
+
break
|
|
486
|
+
|
|
487
|
+
except StopIteration:
|
|
488
|
+
self._channel_exhausted.add(idx)
|
|
489
|
+
|
|
490
|
+
def _load_from_bars(self, idx, bar_iter, target_ts):
|
|
491
|
+
"""Load events from a bar data iterator up to target timestamp.
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
idx: Index of the bar data source.
|
|
495
|
+
bar_iter: Iterator for the bar data source.
|
|
496
|
+
target_ts: Target timestamp to load up to.
|
|
497
|
+
"""
|
|
498
|
+
loaded = 0
|
|
499
|
+
|
|
500
|
+
try:
|
|
501
|
+
while loaded < self._batch_size:
|
|
502
|
+
bar_event = next(bar_iter)
|
|
503
|
+
if not hasattr(bar_event, "timestamp"):
|
|
504
|
+
continue
|
|
505
|
+
|
|
506
|
+
wrapped = Event(
|
|
507
|
+
timestamp=bar_event.timestamp,
|
|
508
|
+
priority=EventPriority.BAR,
|
|
509
|
+
sequence=self._sequence,
|
|
510
|
+
channel_type="bar",
|
|
511
|
+
channel_name=getattr(bar_event, "symbol", ""),
|
|
512
|
+
data=bar_event,
|
|
513
|
+
)
|
|
514
|
+
self._sequence += 1
|
|
515
|
+
heapq.heappush(self._heap, wrapped)
|
|
516
|
+
loaded += 1
|
|
517
|
+
|
|
518
|
+
if bar_event.timestamp > target_ts:
|
|
519
|
+
break
|
|
520
|
+
|
|
521
|
+
except StopIteration:
|
|
522
|
+
self._bar_exhausted.add(idx)
|
|
523
|
+
|
|
524
|
+
def _get_channel_priority(self, channel) -> int:
|
|
525
|
+
"""Map channel type to event priority."""
|
|
526
|
+
priority_map = {
|
|
527
|
+
"tick": EventPriority.TICK,
|
|
528
|
+
"orderbook": EventPriority.ORDERBOOK,
|
|
529
|
+
"funding_rate": EventPriority.FUNDING,
|
|
530
|
+
"funding": EventPriority.FUNDING,
|
|
531
|
+
"bar": EventPriority.BAR,
|
|
532
|
+
}
|
|
533
|
+
return priority_map.get(channel.channel_type, EventPriority.TICK)
|
|
534
|
+
|
|
535
|
+
@property
|
|
536
|
+
def empty(self) -> bool:
|
|
537
|
+
"""Whether the queue is empty and all sources exhausted."""
|
|
538
|
+
if self._heap:
|
|
539
|
+
return False
|
|
540
|
+
# Try loading more
|
|
541
|
+
all_exhausted = self._channel_exhausted.issuperset(
|
|
542
|
+
self._channel_iters.keys()
|
|
543
|
+
) and self._bar_exhausted.issuperset(self._bar_iters.keys())
|
|
544
|
+
if all_exhausted:
|
|
545
|
+
return True
|
|
546
|
+
# Try preloading ahead
|
|
547
|
+
self._preload_up_to = float("-inf")
|
|
548
|
+
self._ensure_preload()
|
|
549
|
+
return len(self._heap) == 0
|
|
550
|
+
|
|
551
|
+
def pop(self) -> Optional[Event]:
|
|
552
|
+
"""Pop the next event from the queue.
|
|
553
|
+
|
|
554
|
+
Returns the event with the smallest (timestamp, priority, sequence).
|
|
555
|
+
Triggers preloading if needed.
|
|
556
|
+
|
|
557
|
+
Returns:
|
|
558
|
+
The next Event, or None if the queue is empty.
|
|
559
|
+
"""
|
|
560
|
+
if not self._heap:
|
|
561
|
+
if self.empty:
|
|
562
|
+
return None
|
|
563
|
+
|
|
564
|
+
if not self._heap:
|
|
565
|
+
return None
|
|
566
|
+
|
|
567
|
+
event = heapq.heappop(self._heap)
|
|
568
|
+
self._current_ts = event.timestamp
|
|
569
|
+
self._total_events_popped += 1
|
|
570
|
+
|
|
571
|
+
# Trigger preload if we're getting close to the boundary
|
|
572
|
+
if self._current_ts + self._window * 0.5 >= self._preload_up_to:
|
|
573
|
+
self._ensure_preload()
|
|
574
|
+
|
|
575
|
+
# Adaptive window adjustment
|
|
576
|
+
if self._adaptive and self._total_events_popped % 1000 == 0:
|
|
577
|
+
self._adjust_window()
|
|
578
|
+
|
|
579
|
+
return event
|
|
580
|
+
|
|
581
|
+
def peek(self) -> Optional[Event]:
|
|
582
|
+
"""Peek at the next event without removing it.
|
|
583
|
+
|
|
584
|
+
Returns:
|
|
585
|
+
The next Event, or None if the queue is empty.
|
|
586
|
+
"""
|
|
587
|
+
if not self._heap:
|
|
588
|
+
if self.empty:
|
|
589
|
+
return None
|
|
590
|
+
return self._heap[0] if self._heap else None
|
|
591
|
+
|
|
592
|
+
def _adjust_window(self):
|
|
593
|
+
"""Adjust preload window based on current memory usage.
|
|
594
|
+
|
|
595
|
+
Shrinks the window if memory usage exceeds 80% of the maximum,
|
|
596
|
+
grows it if usage is below 30% and the window is below maximum.
|
|
597
|
+
Adjustments are made gradually (multiply by 0.7 or 1.3) to avoid
|
|
598
|
+
oscillation.
|
|
599
|
+
"""
|
|
600
|
+
mem_usage = self._estimate_memory_usage()
|
|
601
|
+
|
|
602
|
+
if mem_usage > self._max_memory * 0.8:
|
|
603
|
+
# Shrink window
|
|
604
|
+
self._window = max(self._window_min, self._window * 0.7)
|
|
605
|
+
self._adjustment_count += 1
|
|
606
|
+
logger.debug(
|
|
607
|
+
"Shrinking preload window to %.0fs (memory: %.1fMB)",
|
|
608
|
+
self._window,
|
|
609
|
+
mem_usage / 1024 / 1024,
|
|
610
|
+
)
|
|
611
|
+
elif mem_usage < self._max_memory * 0.3 and self._window < self._window_max:
|
|
612
|
+
# Grow window
|
|
613
|
+
self._window = min(self._window_max, self._window * 1.3)
|
|
614
|
+
self._adjustment_count += 1
|
|
615
|
+
logger.debug(
|
|
616
|
+
"Growing preload window to %.0fs (memory: %.1fMB)",
|
|
617
|
+
self._window,
|
|
618
|
+
mem_usage / 1024 / 1024,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
def _estimate_memory_usage(self) -> int:
|
|
622
|
+
"""Estimate current memory usage of the heap.
|
|
623
|
+
|
|
624
|
+
Returns:
|
|
625
|
+
Estimated memory usage in bytes.
|
|
626
|
+
"""
|
|
627
|
+
# Rough estimate: each event ~200 bytes (dataclass + heap entry)
|
|
628
|
+
return len(self._heap) * 200
|
|
629
|
+
|
|
630
|
+
@property
|
|
631
|
+
def heap_size(self) -> int:
|
|
632
|
+
"""Current number of events in the heap."""
|
|
633
|
+
return len(self._heap)
|
|
634
|
+
|
|
635
|
+
@property
|
|
636
|
+
def total_events_popped(self) -> int:
|
|
637
|
+
"""Total number of events popped from the queue."""
|
|
638
|
+
return self._total_events_popped
|
|
639
|
+
|
|
640
|
+
@property
|
|
641
|
+
def current_timestamp(self) -> float:
|
|
642
|
+
"""Current processing timestamp."""
|
|
643
|
+
return self._current_ts
|
|
644
|
+
|
|
645
|
+
def __len__(self):
|
|
646
|
+
"""Return the current number of events in the heap.
|
|
647
|
+
|
|
648
|
+
Returns:
|
|
649
|
+
int: Number of events currently in the heap.
|
|
650
|
+
"""
|
|
651
|
+
return len(self._heap)
|
|
652
|
+
|
|
653
|
+
def __iter__(self):
|
|
654
|
+
"""Iterate over all events in timestamp order.
|
|
655
|
+
|
|
656
|
+
Yields:
|
|
657
|
+
Event: The next event in timestamp order.
|
|
658
|
+
"""
|
|
659
|
+
while not self.empty:
|
|
660
|
+
event = self.pop()
|
|
661
|
+
if event is not None:
|
|
662
|
+
yield event
|
|
663
|
+
|
|
664
|
+
def __bool__(self):
|
|
665
|
+
"""Return whether the queue has events available.
|
|
666
|
+
|
|
667
|
+
Returns:
|
|
668
|
+
bool: True if the queue is not empty, False otherwise.
|
|
669
|
+
"""
|
|
670
|
+
return not self.empty
|
|
671
|
+
|
|
672
|
+
def __repr__(self):
|
|
673
|
+
"""Return a string representation of the queue.
|
|
674
|
+
|
|
675
|
+
Returns:
|
|
676
|
+
str: Representation showing heap size, popped count, and window.
|
|
677
|
+
"""
|
|
678
|
+
return (
|
|
679
|
+
f"StreamingEventQueue(heap={len(self._heap)}, "
|
|
680
|
+
f"popped={self._total_events_popped}, "
|
|
681
|
+
f"window={self._window:.0f}s)"
|
|
682
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Data channels package for tick-level backtesting.
|
|
2
|
+
|
|
3
|
+
Provides specialized data channels for different market data types:
|
|
4
|
+
- TickChannel: Trade/tick data
|
|
5
|
+
- OrderBookChannel: Order book depth snapshots
|
|
6
|
+
- FundingRateChannel: Funding rate data for perpetual contracts
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .bridge import ChannelBridge
|
|
10
|
+
from .funding import FundingRateChannel
|
|
11
|
+
from .live_queue import LiveEventQueue
|
|
12
|
+
from .live_validator import LiveDataValidator
|
|
13
|
+
from .orderbook import OrderBookChannel
|
|
14
|
+
from .tick import TickChannel
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"TickChannel",
|
|
18
|
+
"OrderBookChannel",
|
|
19
|
+
"FundingRateChannel",
|
|
20
|
+
"ChannelBridge",
|
|
21
|
+
"LiveEventQueue",
|
|
22
|
+
"LiveDataValidator",
|
|
23
|
+
]
|