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,625 @@
|
|
|
1
|
+
"""Binance BBO/depth data loading for HFT backtesting.
|
|
2
|
+
|
|
3
|
+
Parses Binance best-bid/offer and depth/trade dumps (CSV/JSON/zip) into the
|
|
4
|
+
event format consumed by the tick-level matching engine, optionally bridging to
|
|
5
|
+
``hftbacktest``'s ``FuseMarketDepth`` when available.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import bisect
|
|
11
|
+
import csv
|
|
12
|
+
import io
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
import zipfile
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Iterable, Iterator, Optional
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from ...utils.log_message import get_logger
|
|
23
|
+
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from hftbacktest import FuseMarketDepth
|
|
28
|
+
except Exception:
|
|
29
|
+
logger.warning("binance_bbo:24 fallback on Exception")
|
|
30
|
+
FuseMarketDepth = None
|
|
31
|
+
|
|
32
|
+
EXCH_EVENT = 1 << 31
|
|
33
|
+
LOCAL_EVENT = 1 << 30
|
|
34
|
+
BUY_EVENT = 1 << 29
|
|
35
|
+
SELL_EVENT = 1 << 28
|
|
36
|
+
DEPTH_EVENT = 1
|
|
37
|
+
TRADE_EVENT = 2
|
|
38
|
+
DEPTH_BBO_EVENT = 5
|
|
39
|
+
|
|
40
|
+
EVENT_DTYPE = np.dtype(
|
|
41
|
+
[
|
|
42
|
+
("ev", "u8"),
|
|
43
|
+
("exch_ts", "i8"),
|
|
44
|
+
("local_ts", "i8"),
|
|
45
|
+
("px", "f8"),
|
|
46
|
+
("qty", "f8"),
|
|
47
|
+
("order_id", "u8"),
|
|
48
|
+
("ival", "i8"),
|
|
49
|
+
("fval", "f8"),
|
|
50
|
+
],
|
|
51
|
+
align=True,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
_LATENCY_DTYPE = np.dtype(
|
|
55
|
+
[("req_ts", "i8"), ("exch_ts", "i8"), ("resp_ts", "i8"), ("_padding", "i8")],
|
|
56
|
+
align=True,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
_FILENAME_RE = re.compile(
|
|
60
|
+
r"^(?P<symbol>[A-Z0-9]+)-(?P<kind>[A-Za-z]+)-(?P<date>\d{4}-\d{2}-\d{2})\.zip$"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True)
|
|
65
|
+
class BinanceBBOConversionResult:
|
|
66
|
+
"""Result of converting a Binance BBO + trades zip pair.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
symbol: Native exchange symbol extracted from the input zip
|
|
70
|
+
filename (for example ``"BTCUSDT"``).
|
|
71
|
+
bt_symbol: Backtrader-style symbol used for the generated tick
|
|
72
|
+
CSV and order-book JSONL files. Defaults to the
|
|
73
|
+
:func:`_default_bt_symbol` mapping if the caller did not
|
|
74
|
+
pass an explicit value.
|
|
75
|
+
date: ISO-style date (``YYYY-MM-DD``) parsed from the input zip
|
|
76
|
+
filename.
|
|
77
|
+
hft_npz_path: Path of the consolidated ``.npz`` file containing
|
|
78
|
+
the unified feed stream that downstream HFT tooling
|
|
79
|
+
(latency generators, matching engine) consumes.
|
|
80
|
+
backtrader_ticks_path: Path of the generated Backtrader tick
|
|
81
|
+
CSV file.
|
|
82
|
+
backtrader_orderbook_path: Path of the generated Backtrader
|
|
83
|
+
order-book JSONL file.
|
|
84
|
+
base_event_count: Number of raw feed events read from the input
|
|
85
|
+
zip files before the ``start_ms`` / ``end_ms`` window was
|
|
86
|
+
applied.
|
|
87
|
+
final_event_count: Number of events written to ``hft_npz_path``
|
|
88
|
+
after windowing, deduplication and book/trade merging.
|
|
89
|
+
book_rows: Number of best-bid/best-ask rows actually written to
|
|
90
|
+
``backtrader_orderbook_path``.
|
|
91
|
+
trade_rows: Number of trade rows actually written to
|
|
92
|
+
``backtrader_ticks_path``.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
symbol: str
|
|
96
|
+
bt_symbol: str
|
|
97
|
+
date: str
|
|
98
|
+
hft_npz_path: Path
|
|
99
|
+
backtrader_ticks_path: Path
|
|
100
|
+
backtrader_orderbook_path: Path
|
|
101
|
+
base_event_count: int
|
|
102
|
+
final_event_count: int
|
|
103
|
+
book_rows: int
|
|
104
|
+
trade_rows: int
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class BinanceBBOLatencyResult:
|
|
109
|
+
"""Result of :func:`generate_latency_from_hft_events`.
|
|
110
|
+
|
|
111
|
+
Attributes:
|
|
112
|
+
latency_npz_path: Path of the ``.npz`` file containing the
|
|
113
|
+
synthesised latency record. The file holds a single
|
|
114
|
+
``data`` array with the dtype defined by
|
|
115
|
+
``_LATENCY_DTYPE``.
|
|
116
|
+
row_count: Number of latency rows actually written to
|
|
117
|
+
``latency_npz_path`` — i.e. the number of dual-marked feed
|
|
118
|
+
events that were turned into latency records.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
latency_npz_path: Path
|
|
122
|
+
row_count: int
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass(frozen=True)
|
|
126
|
+
class _BookRow:
|
|
127
|
+
exch_ms: int
|
|
128
|
+
local_ms: int
|
|
129
|
+
bid_price: float
|
|
130
|
+
bid_qty: float
|
|
131
|
+
ask_price: float
|
|
132
|
+
ask_qty: float
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def convert_binance_bbo_zip_pair(
|
|
136
|
+
book_ticker_zip_path,
|
|
137
|
+
trades_zip_path,
|
|
138
|
+
output_directory,
|
|
139
|
+
bt_symbol: Optional[str] = None,
|
|
140
|
+
exchange: str = "binance",
|
|
141
|
+
asset_type: str = "futures",
|
|
142
|
+
start_ms: Optional[int] = None,
|
|
143
|
+
end_ms: Optional[int] = None,
|
|
144
|
+
max_book_rows: Optional[int] = None,
|
|
145
|
+
max_trade_rows: Optional[int] = None,
|
|
146
|
+
tick_size: float = 0.01,
|
|
147
|
+
lot_size: float = 0.001,
|
|
148
|
+
) -> BinanceBBOConversionResult:
|
|
149
|
+
"""Convert a Binance ``bookTicker`` + trades zip pair into HFT-ready artifacts.
|
|
150
|
+
|
|
151
|
+
The function reads the two zip archives produced by Binance's
|
|
152
|
+
public data export, applies an optional time window, and emits
|
|
153
|
+
three files inside ``output_directory``:
|
|
154
|
+
|
|
155
|
+
* a Backtrader-friendly tick CSV (``backtrader_ticks_path``),
|
|
156
|
+
* a Backtrader-friendly best-bid/ask JSONL stream
|
|
157
|
+
(``backtrader_orderbook_path``),
|
|
158
|
+
* a consolidated ``.npz`` file (``hft_npz_path``) used by the rest
|
|
159
|
+
of the HFT stack (latency synthesis, matching engine playback,
|
|
160
|
+
branch comparison).
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
book_ticker_zip_path: Path of the ``bookTicker`` zip archive
|
|
164
|
+
exported by Binance. The filename must match the
|
|
165
|
+
``<SYMBOL>-bookTicker-<DATE>.zip`` convention enforced by
|
|
166
|
+
``_SYMBOL_DATE_RE``.
|
|
167
|
+
trades_zip_path: Path of the matching ``trades`` zip archive.
|
|
168
|
+
Must share both the symbol and date of
|
|
169
|
+
``book_ticker_zip_path``.
|
|
170
|
+
output_directory: Destination directory. Created with
|
|
171
|
+
``parents=True`` if it does not already exist.
|
|
172
|
+
bt_symbol: Backtrader-style symbol written to the output files.
|
|
173
|
+
When ``None`` (the default) it is derived from ``symbol``
|
|
174
|
+
via :func:`_default_bt_symbol`.
|
|
175
|
+
exchange: Exchange tag written into the tick CSV.
|
|
176
|
+
asset_type: Asset-class tag written into the tick CSV
|
|
177
|
+
(``"futures"``, ``"spot"`` ...).
|
|
178
|
+
start_ms: Optional inclusive lower bound (epoch ms) for the
|
|
179
|
+
events to keep. ``None`` keeps everything from the start of
|
|
180
|
+
the archive.
|
|
181
|
+
end_ms: Optional inclusive upper bound (epoch ms) for the events
|
|
182
|
+
to keep. ``None`` keeps everything until the end of the
|
|
183
|
+
archive.
|
|
184
|
+
max_book_rows: Optional cap on the number of book rows written
|
|
185
|
+
to the order-book JSONL file (useful for tests).
|
|
186
|
+
max_trade_rows: Optional cap on the number of trade rows written
|
|
187
|
+
to the tick CSV (useful for tests).
|
|
188
|
+
tick_size: Minimum price increment for the symbol. Currently
|
|
189
|
+
informational — written into the consolidated ``.npz``
|
|
190
|
+
metadata so downstream consumers can use it.
|
|
191
|
+
lot_size: Minimum quantity increment for the symbol. Currently
|
|
192
|
+
informational — written into the consolidated ``.npz``
|
|
193
|
+
metadata so downstream consumers can use it.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
BinanceBBOConversionResult: Paths of every artifact that was
|
|
197
|
+
written together with the row/event counts for quick
|
|
198
|
+
verification.
|
|
199
|
+
|
|
200
|
+
Raises:
|
|
201
|
+
ValueError: If ``book_ticker_zip_path`` and ``trades_zip_path``
|
|
202
|
+
do not share the same symbol and date.
|
|
203
|
+
"""
|
|
204
|
+
book_ticker_zip_path = Path(book_ticker_zip_path)
|
|
205
|
+
trades_zip_path = Path(trades_zip_path)
|
|
206
|
+
output_directory = Path(output_directory)
|
|
207
|
+
output_directory.mkdir(parents=True, exist_ok=True)
|
|
208
|
+
|
|
209
|
+
symbol, date = _extract_symbol_and_date(book_ticker_zip_path)
|
|
210
|
+
trade_symbol, trade_date = _extract_symbol_and_date(trades_zip_path)
|
|
211
|
+
if trade_symbol != symbol or trade_date != date:
|
|
212
|
+
raise ValueError("bookTicker and trades zip files must have the same symbol and date")
|
|
213
|
+
|
|
214
|
+
bt_symbol = bt_symbol or _default_bt_symbol(symbol)
|
|
215
|
+
|
|
216
|
+
ticks_path = output_directory / f"tick_{symbol}_{date.replace('-', '')}.csv"
|
|
217
|
+
orderbook_path = output_directory / f"orderbook_{symbol}_{date.replace('-', '')}.jsonl"
|
|
218
|
+
hft_npz_path = output_directory / f"{symbol}_{date.replace('-', '')}.npz"
|
|
219
|
+
|
|
220
|
+
depth_events = []
|
|
221
|
+
trade_events = []
|
|
222
|
+
book_rows = []
|
|
223
|
+
latency_lookup_ts = []
|
|
224
|
+
latency_lookup_ms = []
|
|
225
|
+
|
|
226
|
+
with ticks_path.open("w", encoding="utf-8", newline="") as tick_file, orderbook_path.open(
|
|
227
|
+
"w", encoding="utf-8"
|
|
228
|
+
) as orderbook_file:
|
|
229
|
+
tick_writer = csv.DictWriter(
|
|
230
|
+
tick_file,
|
|
231
|
+
fieldnames=[
|
|
232
|
+
"timestamp",
|
|
233
|
+
"symbol",
|
|
234
|
+
"exchange",
|
|
235
|
+
"asset_type",
|
|
236
|
+
"price",
|
|
237
|
+
"volume",
|
|
238
|
+
"direction",
|
|
239
|
+
"trade_id",
|
|
240
|
+
"bid_price",
|
|
241
|
+
"ask_price",
|
|
242
|
+
"bid_volume",
|
|
243
|
+
"ask_volume",
|
|
244
|
+
],
|
|
245
|
+
)
|
|
246
|
+
tick_writer.writeheader()
|
|
247
|
+
|
|
248
|
+
for row in _iter_book_ticker_rows(book_ticker_zip_path):
|
|
249
|
+
exch_ms = row["transaction_time"]
|
|
250
|
+
if not _in_window(exch_ms, start_ms, end_ms):
|
|
251
|
+
continue
|
|
252
|
+
local_ms = max(row["event_time"], exch_ms)
|
|
253
|
+
book_row = _BookRow(
|
|
254
|
+
exch_ms=exch_ms,
|
|
255
|
+
local_ms=local_ms,
|
|
256
|
+
bid_price=row["best_bid_price"],
|
|
257
|
+
bid_qty=row["best_bid_qty"],
|
|
258
|
+
ask_price=row["best_ask_price"],
|
|
259
|
+
ask_qty=row["best_ask_qty"],
|
|
260
|
+
)
|
|
261
|
+
book_rows.append(book_row)
|
|
262
|
+
latency_lookup_ts.append(exch_ms)
|
|
263
|
+
latency_lookup_ms.append(local_ms - exch_ms)
|
|
264
|
+
depth_events.append(
|
|
265
|
+
(
|
|
266
|
+
DEPTH_BBO_EVENT | SELL_EVENT,
|
|
267
|
+
exch_ms * 1_000_000,
|
|
268
|
+
local_ms * 1_000_000,
|
|
269
|
+
book_row.ask_price,
|
|
270
|
+
book_row.ask_qty,
|
|
271
|
+
0,
|
|
272
|
+
0,
|
|
273
|
+
0.0,
|
|
274
|
+
)
|
|
275
|
+
)
|
|
276
|
+
depth_events.append(
|
|
277
|
+
(
|
|
278
|
+
DEPTH_BBO_EVENT | BUY_EVENT,
|
|
279
|
+
exch_ms * 1_000_000,
|
|
280
|
+
local_ms * 1_000_000,
|
|
281
|
+
book_row.bid_price,
|
|
282
|
+
book_row.bid_qty,
|
|
283
|
+
0,
|
|
284
|
+
0,
|
|
285
|
+
0.0,
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
if max_book_rows is not None and len(book_rows) >= max_book_rows:
|
|
289
|
+
break
|
|
290
|
+
|
|
291
|
+
if not book_rows:
|
|
292
|
+
raise ValueError("No bookTicker rows matched the selected window")
|
|
293
|
+
|
|
294
|
+
book_rows.sort(key=lambda row: (row.local_ms, row.exch_ms))
|
|
295
|
+
for book_row in book_rows:
|
|
296
|
+
orderbook_file.write(
|
|
297
|
+
json.dumps(
|
|
298
|
+
{
|
|
299
|
+
"timestamp": book_row.local_ms / 1000.0,
|
|
300
|
+
"symbol": bt_symbol,
|
|
301
|
+
"exchange": exchange,
|
|
302
|
+
"asset_type": asset_type,
|
|
303
|
+
"bids": [[book_row.bid_price, book_row.bid_qty]],
|
|
304
|
+
"asks": [[book_row.ask_price, book_row.ask_qty]],
|
|
305
|
+
},
|
|
306
|
+
separators=(",", ":"),
|
|
307
|
+
)
|
|
308
|
+
+ "\n"
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
book_event_times = [row.local_ms for row in book_rows]
|
|
312
|
+
|
|
313
|
+
trade_rows = 0
|
|
314
|
+
for row in _iter_trades_rows(trades_zip_path):
|
|
315
|
+
exch_ms = row["time"]
|
|
316
|
+
if not _in_window(exch_ms, start_ms, end_ms):
|
|
317
|
+
continue
|
|
318
|
+
latency_ms = _lookup_latency_ms(exch_ms, latency_lookup_ts, latency_lookup_ms)
|
|
319
|
+
local_ms = exch_ms + latency_ms
|
|
320
|
+
side = "sell" if row["is_buyer_maker"] else "buy"
|
|
321
|
+
trade_flag = SELL_EVENT if side == "sell" else BUY_EVENT
|
|
322
|
+
book_row = _lookup_book_row(local_ms, book_event_times, book_rows)
|
|
323
|
+
tick_writer.writerow(
|
|
324
|
+
{
|
|
325
|
+
"timestamp": f"{local_ms / 1000.0:.6f}",
|
|
326
|
+
"symbol": bt_symbol,
|
|
327
|
+
"exchange": exchange,
|
|
328
|
+
"asset_type": asset_type,
|
|
329
|
+
"price": _format_decimal(row["price"]),
|
|
330
|
+
"volume": _format_decimal(row["qty"]),
|
|
331
|
+
"direction": side,
|
|
332
|
+
"trade_id": row["id"],
|
|
333
|
+
"bid_price": _format_decimal(book_row.bid_price),
|
|
334
|
+
"ask_price": _format_decimal(book_row.ask_price),
|
|
335
|
+
"bid_volume": _format_decimal(book_row.bid_qty),
|
|
336
|
+
"ask_volume": _format_decimal(book_row.ask_qty),
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
trade_events.append(
|
|
340
|
+
(
|
|
341
|
+
TRADE_EVENT | trade_flag,
|
|
342
|
+
exch_ms * 1_000_000,
|
|
343
|
+
local_ms * 1_000_000,
|
|
344
|
+
row["price"],
|
|
345
|
+
row["qty"],
|
|
346
|
+
0,
|
|
347
|
+
0,
|
|
348
|
+
0.0,
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
trade_rows += 1
|
|
352
|
+
if max_trade_rows is not None and trade_rows >= max_trade_rows:
|
|
353
|
+
break
|
|
354
|
+
|
|
355
|
+
depth_array = np.array(depth_events, dtype=EVENT_DTYPE)
|
|
356
|
+
trade_array = np.array(trade_events, dtype=EVENT_DTYPE)
|
|
357
|
+
fused_depth_array = _fuse_depth_events(depth_array, tick_size=tick_size, lot_size=lot_size)
|
|
358
|
+
base_array = np.empty(len(trade_array) + len(fused_depth_array), dtype=EVENT_DTYPE)
|
|
359
|
+
if len(trade_array) > 0:
|
|
360
|
+
base_array[: len(trade_array)] = trade_array
|
|
361
|
+
if len(fused_depth_array) > 0:
|
|
362
|
+
base_array[len(trade_array) :] = fused_depth_array
|
|
363
|
+
base_array = _correct_local_timestamp(base_array)
|
|
364
|
+
data = _correct_event_order(base_array)
|
|
365
|
+
np.savez_compressed(hft_npz_path, data=data)
|
|
366
|
+
|
|
367
|
+
return BinanceBBOConversionResult(
|
|
368
|
+
symbol=symbol,
|
|
369
|
+
bt_symbol=bt_symbol,
|
|
370
|
+
date=date,
|
|
371
|
+
hft_npz_path=hft_npz_path,
|
|
372
|
+
backtrader_ticks_path=ticks_path,
|
|
373
|
+
backtrader_orderbook_path=orderbook_path,
|
|
374
|
+
base_event_count=len(base_array),
|
|
375
|
+
final_event_count=len(data),
|
|
376
|
+
book_rows=len(book_rows),
|
|
377
|
+
trade_rows=trade_rows,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def generate_latency_from_hft_events(
|
|
382
|
+
hft_npz_path,
|
|
383
|
+
output_path,
|
|
384
|
+
mul_entry: float = 4.0,
|
|
385
|
+
offset_entry_ns: int = 0,
|
|
386
|
+
mul_resp: float = 3.0,
|
|
387
|
+
offset_resp_ns: int = 0,
|
|
388
|
+
) -> BinanceBBOLatencyResult:
|
|
389
|
+
"""Synthesize a latency record from the dual-marked events of an HFT feed.
|
|
390
|
+
|
|
391
|
+
Only feed events that carry both the ``EXCH_EVENT`` and ``LOCAL_EVENT``
|
|
392
|
+
bits are considered (these are the rows that carry both the local
|
|
393
|
+
receive timestamp and the upstream exchange timestamp). For each such
|
|
394
|
+
row the function produces a latency tuple of
|
|
395
|
+
``(req_ts, exch_ts, resp_ts, reserved)`` where:
|
|
396
|
+
|
|
397
|
+
* ``req_ts`` is the local timestamp at which the strategy requested
|
|
398
|
+
the action,
|
|
399
|
+
* ``exch_ts`` is the exchange-side arrival timestamp computed as
|
|
400
|
+
``local_ts + feed_latency * mul_entry + offset_entry_ns``,
|
|
401
|
+
* ``resp_ts`` is the response-side arrival timestamp computed as
|
|
402
|
+
``exch_ts + feed_latency * mul_resp + offset_resp_ns``.
|
|
403
|
+
|
|
404
|
+
The numpy scalars in the input file are converted to ``int`` so the
|
|
405
|
+
output array has fixed-width integer fields.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
hft_npz_path: Path of the HFT ``.npz`` produced by
|
|
409
|
+
:func:`convert_binance_bbo_zip_pair`. The function looks for
|
|
410
|
+
the ``"data"`` array inside the archive.
|
|
411
|
+
output_path: Path of the latency ``.npz`` file to write. The
|
|
412
|
+
parent directory is created with ``parents=True`` if it does
|
|
413
|
+
not already exist. The file is written via
|
|
414
|
+
``np.savez_compressed`` and stores a single ``"data"`` array
|
|
415
|
+
using ``_LATENCY_DTYPE``.
|
|
416
|
+
mul_entry: Multiplier applied to the observed feed latency when
|
|
417
|
+
computing ``exch_ts``. Defaults to ``4.0``.
|
|
418
|
+
offset_entry_ns: Constant offset (ns) added to the entry
|
|
419
|
+
latency. Useful to inject a synthetic jitter or align the
|
|
420
|
+
latency trace against a benchmark. Defaults to ``0``.
|
|
421
|
+
mul_resp: Multiplier applied to the observed feed latency when
|
|
422
|
+
computing ``resp_ts``. Defaults to ``3.0``.
|
|
423
|
+
offset_resp_ns: Constant offset (ns) added to the response
|
|
424
|
+
latency. Defaults to ``0``.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
BinanceBBOLatencyResult: Path of the latency file and the
|
|
428
|
+
number of rows written.
|
|
429
|
+
|
|
430
|
+
Raises:
|
|
431
|
+
ValueError: If the input ``.npz`` does not contain any
|
|
432
|
+
dual-marked feed events.
|
|
433
|
+
"""
|
|
434
|
+
hft_npz_path = Path(hft_npz_path)
|
|
435
|
+
output_path = Path(output_path)
|
|
436
|
+
data = np.load(hft_npz_path)["data"]
|
|
437
|
+
mask = (data["ev"] & EXCH_EVENT == EXCH_EVENT) & (data["ev"] & LOCAL_EVENT == LOCAL_EVENT)
|
|
438
|
+
rows = data[mask]
|
|
439
|
+
if len(rows) == 0:
|
|
440
|
+
raise ValueError("No dual-marked feed events available for latency generation")
|
|
441
|
+
|
|
442
|
+
order_latency = np.zeros(
|
|
443
|
+
len(rows),
|
|
444
|
+
dtype=_LATENCY_DTYPE,
|
|
445
|
+
)
|
|
446
|
+
for index, row in enumerate(rows):
|
|
447
|
+
feed_latency = max(int(row["local_ts"] - row["exch_ts"]), 0)
|
|
448
|
+
entry_latency = int(feed_latency * mul_entry) + int(offset_entry_ns)
|
|
449
|
+
resp_latency = int(feed_latency * mul_resp) + int(offset_resp_ns)
|
|
450
|
+
req_ts = int(row["local_ts"])
|
|
451
|
+
exch_ts = req_ts + entry_latency
|
|
452
|
+
resp_ts = exch_ts + resp_latency
|
|
453
|
+
order_latency[index] = (req_ts, exch_ts, resp_ts, 0)
|
|
454
|
+
|
|
455
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
456
|
+
np.savez_compressed(output_path, data=order_latency)
|
|
457
|
+
return BinanceBBOLatencyResult(latency_npz_path=output_path, row_count=len(order_latency))
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _iter_book_ticker_rows(zip_path: Path) -> Iterator[dict]:
|
|
461
|
+
for row in _iter_zip_csv_rows(zip_path):
|
|
462
|
+
yield {
|
|
463
|
+
"transaction_time": int(row["transaction_time"]),
|
|
464
|
+
"event_time": int(row["event_time"]),
|
|
465
|
+
"best_bid_price": float(row["best_bid_price"]),
|
|
466
|
+
"best_bid_qty": float(row["best_bid_qty"]),
|
|
467
|
+
"best_ask_price": float(row["best_ask_price"]),
|
|
468
|
+
"best_ask_qty": float(row["best_ask_qty"]),
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _iter_trades_rows(zip_path: Path) -> Iterator[dict]:
|
|
473
|
+
for row in _iter_zip_csv_rows(zip_path):
|
|
474
|
+
yield {
|
|
475
|
+
"id": row["id"],
|
|
476
|
+
"price": float(row["price"]),
|
|
477
|
+
"qty": float(row["qty"]),
|
|
478
|
+
"time": int(row["time"]),
|
|
479
|
+
"is_buyer_maker": row["is_buyer_maker"].strip().lower() == "true",
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def _iter_zip_csv_rows(zip_path: Path) -> Iterable[dict]:
|
|
484
|
+
with zipfile.ZipFile(zip_path) as archive:
|
|
485
|
+
names = [name for name in archive.namelist() if name.lower().endswith(".csv")]
|
|
486
|
+
if len(names) != 1:
|
|
487
|
+
raise ValueError(f"Expected exactly one CSV entry in {zip_path}")
|
|
488
|
+
with archive.open(names[0], "r") as raw:
|
|
489
|
+
text_stream = io.TextIOWrapper(raw, encoding="utf-8", newline="")
|
|
490
|
+
reader = csv.DictReader(text_stream)
|
|
491
|
+
for row in reader:
|
|
492
|
+
yield row
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def _extract_symbol_and_date(zip_path: Path) -> tuple[str, str]:
|
|
496
|
+
match = _FILENAME_RE.match(zip_path.name)
|
|
497
|
+
if match is None:
|
|
498
|
+
raise ValueError(f"Unsupported zip filename format: {zip_path.name}")
|
|
499
|
+
return match.group("symbol"), match.group("date")
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _default_bt_symbol(symbol: str) -> str:
|
|
503
|
+
for quote in ("USDT", "USDC", "BUSD", "FDUSD", "BTC", "ETH", "BNB"):
|
|
504
|
+
if symbol.endswith(quote) and len(symbol) > len(quote):
|
|
505
|
+
return f"{symbol[: -len(quote)]}/{quote}"
|
|
506
|
+
return symbol
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def _in_window(timestamp_ms: int, start_ms: Optional[int], end_ms: Optional[int]) -> bool:
|
|
510
|
+
if start_ms is not None and timestamp_ms < start_ms:
|
|
511
|
+
return False
|
|
512
|
+
if end_ms is not None and timestamp_ms > end_ms:
|
|
513
|
+
return False
|
|
514
|
+
return True
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def _lookup_latency_ms(timestamp_ms: int, lookup_ts: list[int], lookup_latency: list[int]) -> int:
|
|
518
|
+
if not lookup_ts:
|
|
519
|
+
return 0
|
|
520
|
+
index = bisect.bisect_right(lookup_ts, timestamp_ms) - 1
|
|
521
|
+
if index < 0:
|
|
522
|
+
return lookup_latency[0]
|
|
523
|
+
return lookup_latency[index]
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _lookup_book_row(
|
|
527
|
+
timestamp_ms: int, book_event_times: list[int], book_rows: list[_BookRow]
|
|
528
|
+
) -> _BookRow:
|
|
529
|
+
index = bisect.bisect_right(book_event_times, timestamp_ms) - 1
|
|
530
|
+
if index < 0:
|
|
531
|
+
return book_rows[0]
|
|
532
|
+
return book_rows[index]
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _format_decimal(value: float) -> str:
|
|
536
|
+
return format(value, ".15g")
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def _fuse_depth_events(depth_array: np.ndarray, tick_size: float, lot_size: float) -> np.ndarray:
|
|
540
|
+
if len(depth_array) == 0:
|
|
541
|
+
return depth_array
|
|
542
|
+
if FuseMarketDepth is None:
|
|
543
|
+
fused = depth_array.copy()
|
|
544
|
+
fused["ev"] = (fused["ev"] & ~np.uint64(DEPTH_BBO_EVENT)) | np.uint64(DEPTH_EVENT)
|
|
545
|
+
return fused
|
|
546
|
+
|
|
547
|
+
fuse = FuseMarketDepth(float(tick_size), float(lot_size))
|
|
548
|
+
try:
|
|
549
|
+
for index in range(len(depth_array)):
|
|
550
|
+
fuse.process_event(depth_array, index, True)
|
|
551
|
+
return np.array(fuse.fused_events, dtype=EVENT_DTYPE, copy=True)
|
|
552
|
+
finally:
|
|
553
|
+
fuse.close()
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def _correct_local_timestamp(base_array: np.ndarray, base_latency_ns: int = 0) -> np.ndarray:
|
|
557
|
+
if len(base_array) == 0:
|
|
558
|
+
return base_array
|
|
559
|
+
min_latency = int(np.min(base_array["local_ts"] - base_array["exch_ts"]))
|
|
560
|
+
if min_latency >= 0:
|
|
561
|
+
return base_array
|
|
562
|
+
corrected = base_array.copy()
|
|
563
|
+
corrected["local_ts"] += -min_latency + int(base_latency_ns)
|
|
564
|
+
return corrected
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _correct_event_order(base_array: np.ndarray) -> np.ndarray:
|
|
568
|
+
sorted_exch_index = np.argsort(base_array["exch_ts"], kind="mergesort")
|
|
569
|
+
sorted_local_index = np.argsort(base_array["local_ts"], kind="mergesort")
|
|
570
|
+
output = np.zeros(len(base_array) * 2, dtype=EVENT_DTYPE)
|
|
571
|
+
|
|
572
|
+
out_pos = 0
|
|
573
|
+
exch_pos = 0
|
|
574
|
+
local_pos = 0
|
|
575
|
+
total = len(base_array)
|
|
576
|
+
|
|
577
|
+
while exch_pos < total or local_pos < total:
|
|
578
|
+
exch_row = base_array[sorted_exch_index[exch_pos]] if exch_pos < total else None
|
|
579
|
+
local_row = base_array[sorted_local_index[local_pos]] if local_pos < total else None
|
|
580
|
+
|
|
581
|
+
if exch_row is not None and local_row is not None:
|
|
582
|
+
same_event = (
|
|
583
|
+
exch_row["ev"] == local_row["ev"]
|
|
584
|
+
and exch_row["exch_ts"] == local_row["exch_ts"]
|
|
585
|
+
and exch_row["local_ts"] == local_row["local_ts"]
|
|
586
|
+
and exch_row["px"] == local_row["px"]
|
|
587
|
+
and exch_row["qty"] == local_row["qty"]
|
|
588
|
+
)
|
|
589
|
+
if same_event:
|
|
590
|
+
output[out_pos] = exch_row
|
|
591
|
+
output[out_pos]["ev"] = int(output[out_pos]["ev"]) | EXCH_EVENT | LOCAL_EVENT
|
|
592
|
+
out_pos += 1
|
|
593
|
+
exch_pos += 1
|
|
594
|
+
local_pos += 1
|
|
595
|
+
continue
|
|
596
|
+
|
|
597
|
+
if exch_row["exch_ts"] < local_row["exch_ts"] or (
|
|
598
|
+
exch_row["exch_ts"] == local_row["exch_ts"]
|
|
599
|
+
and exch_row["local_ts"] < local_row["local_ts"]
|
|
600
|
+
):
|
|
601
|
+
output[out_pos] = exch_row
|
|
602
|
+
output[out_pos]["ev"] = int(output[out_pos]["ev"]) | EXCH_EVENT
|
|
603
|
+
out_pos += 1
|
|
604
|
+
exch_pos += 1
|
|
605
|
+
continue
|
|
606
|
+
|
|
607
|
+
output[out_pos] = local_row
|
|
608
|
+
output[out_pos]["ev"] = int(output[out_pos]["ev"]) | LOCAL_EVENT
|
|
609
|
+
out_pos += 1
|
|
610
|
+
local_pos += 1
|
|
611
|
+
continue
|
|
612
|
+
|
|
613
|
+
if exch_row is not None:
|
|
614
|
+
output[out_pos] = exch_row
|
|
615
|
+
output[out_pos]["ev"] = int(output[out_pos]["ev"]) | EXCH_EVENT
|
|
616
|
+
out_pos += 1
|
|
617
|
+
exch_pos += 1
|
|
618
|
+
continue
|
|
619
|
+
|
|
620
|
+
output[out_pos] = local_row
|
|
621
|
+
output[out_pos]["ev"] = int(output[out_pos]["ev"]) | LOCAL_EVENT
|
|
622
|
+
out_pos += 1
|
|
623
|
+
local_pos += 1
|
|
624
|
+
|
|
625
|
+
return output[:out_pos]
|