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,186 @@
|
|
|
1
|
+
"""Channel-to-LineSeries bridge for indicator compatibility.
|
|
2
|
+
|
|
3
|
+
ChannelBridge optionally bridges data from Channel events to LineSeries,
|
|
4
|
+
enabling the use of traditional backtrader indicators (SMA, RSI, etc.)
|
|
5
|
+
on tick-level data. This comes with a performance overhead warning.
|
|
6
|
+
|
|
7
|
+
Example::
|
|
8
|
+
|
|
9
|
+
bridge = ChannelBridge(tick_channel, fields=['price', 'volume'])
|
|
10
|
+
bridge.update(tick_event)
|
|
11
|
+
# Now bridge.lines.price[0] contains the latest tick price
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from collections import deque
|
|
15
|
+
|
|
16
|
+
from ..utils.log_message import get_logger
|
|
17
|
+
|
|
18
|
+
logger = get_logger(__name__)
|
|
19
|
+
|
|
20
|
+
__all__ = ["ChannelBridge"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ChannelBridge:
|
|
24
|
+
"""Bridge between DataChannel events and a simple line-like interface.
|
|
25
|
+
|
|
26
|
+
Buffers specified fields from channel events into deque-based lines
|
|
27
|
+
that can be accessed with [0], [-1] indexing. This is a lightweight
|
|
28
|
+
bridge - it does NOT create full LineSeries objects but provides
|
|
29
|
+
a compatible enough interface for simple indicator usage.
|
|
30
|
+
|
|
31
|
+
Note:
|
|
32
|
+
Using ChannelBridge adds overhead. Only use it when you need
|
|
33
|
+
indicators on channel data. For pure tick processing, use
|
|
34
|
+
channel events directly.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
channel: The DataChannel to bridge from.
|
|
38
|
+
fields: List of field names to bridge (e.g., ['price', 'volume']).
|
|
39
|
+
maxlen: Maximum number of values to retain per line.
|
|
40
|
+
|
|
41
|
+
Example::
|
|
42
|
+
|
|
43
|
+
bridge = ChannelBridge(tick_ch, fields=['price', 'volume'])
|
|
44
|
+
bridge.update(tick_event)
|
|
45
|
+
print(bridge['price'][0]) # latest price
|
|
46
|
+
print(bridge['price'][-1]) # previous price
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, channel, fields=None, maxlen=10000):
|
|
50
|
+
"""Initialize the channel bridge.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
channel: The DataChannel instance to bridge.
|
|
54
|
+
fields: Optional list of field names to extract.
|
|
55
|
+
maxlen: Maximum deque length for each field.
|
|
56
|
+
"""
|
|
57
|
+
self.channel = channel
|
|
58
|
+
self._maxlen = maxlen
|
|
59
|
+
self._fields = fields or self._default_fields(channel)
|
|
60
|
+
self._lines: dict = {f: deque(maxlen=maxlen) for f in self._fields}
|
|
61
|
+
self._count = 0
|
|
62
|
+
|
|
63
|
+
def _default_fields(self, channel):
|
|
64
|
+
"""Determine default fields based on channel type.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
channel: The DataChannel instance to introspect.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
List of default field names for the channel type.
|
|
71
|
+
"""
|
|
72
|
+
type_fields = {
|
|
73
|
+
"tick": ["price", "volume", "timestamp"],
|
|
74
|
+
"orderbook": ["timestamp"],
|
|
75
|
+
"funding": ["rate", "mark_price", "timestamp"],
|
|
76
|
+
"bar": ["open", "high", "low", "close", "volume", "timestamp"],
|
|
77
|
+
}
|
|
78
|
+
return type_fields.get(channel.channel_type, ["timestamp"])
|
|
79
|
+
|
|
80
|
+
def update(self, event):
|
|
81
|
+
"""Update bridge lines with data from a new event.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
event: An EventData instance whose fields are extracted.
|
|
85
|
+
"""
|
|
86
|
+
for field in self._fields:
|
|
87
|
+
value = getattr(event, field, None)
|
|
88
|
+
if value is not None:
|
|
89
|
+
self._lines[field].append(value)
|
|
90
|
+
else:
|
|
91
|
+
self._lines[field].append(float("nan"))
|
|
92
|
+
self._count += 1
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def fields(self):
|
|
96
|
+
"""List of bridged field names."""
|
|
97
|
+
return list(self._fields)
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def count(self):
|
|
101
|
+
"""Number of events bridged."""
|
|
102
|
+
return self._count
|
|
103
|
+
|
|
104
|
+
def __getitem__(self, field):
|
|
105
|
+
"""Get a line by field name.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
field: Field name string.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
_BridgeLine accessor for the field.
|
|
112
|
+
"""
|
|
113
|
+
if field not in self._lines:
|
|
114
|
+
raise KeyError(f"Field '{field}' not bridged. Available: {list(self._lines.keys())}")
|
|
115
|
+
return _BridgeLine(self._lines[field])
|
|
116
|
+
|
|
117
|
+
def __len__(self):
|
|
118
|
+
"""Number of data points in the bridge."""
|
|
119
|
+
if self._lines:
|
|
120
|
+
return len(next(iter(self._lines.values())))
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
def __repr__(self):
|
|
124
|
+
"""Return a string representation of the bridge.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
str: Representation showing channel, fields, and event count.
|
|
128
|
+
"""
|
|
129
|
+
return (
|
|
130
|
+
f"ChannelBridge(channel={self.channel!r}, fields={self._fields}, count={self._count})"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class _BridgeLine:
|
|
135
|
+
"""Accessor for a single bridged line with [-n] indexing.
|
|
136
|
+
|
|
137
|
+
Supports [0] for latest, [-1] for previous, etc. Mimics
|
|
138
|
+
backtrader's line indexing convention.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
def __init__(self, data):
|
|
142
|
+
"""Initialize the bridge line accessor.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
data: Deque containing the historical values for this line.
|
|
146
|
+
"""
|
|
147
|
+
self._data = data
|
|
148
|
+
|
|
149
|
+
def __getitem__(self, index):
|
|
150
|
+
"""Access line values with backtrader-style indexing.
|
|
151
|
+
|
|
152
|
+
Supports backtrader's line indexing convention where:
|
|
153
|
+
[0] = latest value (last element)
|
|
154
|
+
[-1] = previous value (second to last)
|
|
155
|
+
[-2] = two values back
|
|
156
|
+
[n] = future offset from current (only 0 supported)
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
index: Integer index (0 for latest, negative for historical).
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
float: The value at the specified index, or NaN if out of bounds.
|
|
163
|
+
"""
|
|
164
|
+
if not self._data:
|
|
165
|
+
return float("nan")
|
|
166
|
+
if index == 0:
|
|
167
|
+
return self._data[-1]
|
|
168
|
+
if index < 0:
|
|
169
|
+
# -1 = previous, -2 = two back, etc.
|
|
170
|
+
pos = len(self._data) + index - 1
|
|
171
|
+
if 0 <= pos < len(self._data):
|
|
172
|
+
return self._data[pos]
|
|
173
|
+
return float("nan")
|
|
174
|
+
return float("nan")
|
|
175
|
+
|
|
176
|
+
def __len__(self):
|
|
177
|
+
"""Return the number of data points in the line."""
|
|
178
|
+
return len(self._data)
|
|
179
|
+
|
|
180
|
+
def __repr__(self):
|
|
181
|
+
"""Return a string representation of the bridge line.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
str: Representation showing data length.
|
|
185
|
+
"""
|
|
186
|
+
return f"_BridgeLine(length={len(self._data)})"
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Funding rate data channel for perpetual contract funding rates.
|
|
2
|
+
|
|
3
|
+
Provides FundingRateChannel for loading, validating, and buffering
|
|
4
|
+
funding rate data from CSV/JSONL files or other sources.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
Loading funding rate data from CSV::
|
|
8
|
+
|
|
9
|
+
channel = FundingRateChannel(
|
|
10
|
+
symbol='BTC/USDT',
|
|
11
|
+
dataname='data/btc_funding_20210101.csv'
|
|
12
|
+
)
|
|
13
|
+
for event in channel.load():
|
|
14
|
+
print(event.rate, event.mark_price)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import csv
|
|
18
|
+
import gzip
|
|
19
|
+
import json
|
|
20
|
+
import math
|
|
21
|
+
from typing import Iterator, Optional
|
|
22
|
+
|
|
23
|
+
from ..channel import DataChannel, DataValidationResult
|
|
24
|
+
from ..events import FundingEvent
|
|
25
|
+
from ..utils.log_message import get_logger
|
|
26
|
+
|
|
27
|
+
logger = get_logger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class FundingRateChannel(DataChannel):
|
|
31
|
+
"""Funding rate data channel for perpetual contracts.
|
|
32
|
+
|
|
33
|
+
Loads funding rate data from CSV or JSONL files. Validates funding
|
|
34
|
+
rate ranges and mark price consistency.
|
|
35
|
+
|
|
36
|
+
CSV format expects columns: timestamp, rate, mark_price
|
|
37
|
+
Optional columns: next_funding_time, predicted_rate, symbol, exchange
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
symbol: Trading pair symbol (e.g., 'BTC/USDT').
|
|
41
|
+
dataname: Path to the data file (CSV or JSONL).
|
|
42
|
+
maxlen: Maximum buffer size.
|
|
43
|
+
validate: Whether to validate incoming events.
|
|
44
|
+
auto_fix: Whether to auto-fix invalid data.
|
|
45
|
+
rate_threshold: Max absolute funding rate for anomaly warning.
|
|
46
|
+
**kwargs: Additional parameters passed to DataChannel.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
channel_type = "funding"
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
symbol,
|
|
54
|
+
dataname=None,
|
|
55
|
+
maxlen=10000,
|
|
56
|
+
validate=True,
|
|
57
|
+
auto_fix=True,
|
|
58
|
+
rate_threshold=0.01,
|
|
59
|
+
**kwargs,
|
|
60
|
+
):
|
|
61
|
+
"""Initialize the funding rate channel.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
symbol: Trading pair symbol (e.g., 'BTC/USDT:USDT').
|
|
65
|
+
dataname: Optional data name for the feed.
|
|
66
|
+
maxlen: Maximum number of events to buffer.
|
|
67
|
+
validate: Whether to validate incoming events.
|
|
68
|
+
auto_fix: Whether to attempt auto-fixing invalid data.
|
|
69
|
+
rate_threshold: Maximum allowed rate difference threshold.
|
|
70
|
+
**kwargs: Additional arguments passed to parent.
|
|
71
|
+
"""
|
|
72
|
+
super().__init__(
|
|
73
|
+
symbol=symbol,
|
|
74
|
+
maxlen=maxlen,
|
|
75
|
+
validate=validate,
|
|
76
|
+
auto_fix=auto_fix,
|
|
77
|
+
dataname=dataname,
|
|
78
|
+
**kwargs,
|
|
79
|
+
)
|
|
80
|
+
self._dataname = dataname
|
|
81
|
+
self._rate_threshold = rate_threshold
|
|
82
|
+
self._last_rate = None
|
|
83
|
+
|
|
84
|
+
def _validate_event(self, event) -> DataValidationResult:
|
|
85
|
+
"""Validate funding rate specific fields beyond base validation.
|
|
86
|
+
|
|
87
|
+
Additional checks:
|
|
88
|
+
- Rate change anomaly detection
|
|
89
|
+
"""
|
|
90
|
+
result = super()._validate_event(event)
|
|
91
|
+
if not result.valid:
|
|
92
|
+
return result
|
|
93
|
+
|
|
94
|
+
# Rate anomaly detection
|
|
95
|
+
if self._last_rate is not None and abs(event.rate) > self._rate_threshold:
|
|
96
|
+
result.warnings.append(
|
|
97
|
+
f"High funding rate: {event.rate} (threshold: {self._rate_threshold})"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self._last_rate = event.rate
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
def load(self) -> Iterator[FundingEvent]:
|
|
104
|
+
"""Load funding rate events from file.
|
|
105
|
+
|
|
106
|
+
Supports CSV and JSONL formats.
|
|
107
|
+
|
|
108
|
+
Yields:
|
|
109
|
+
FundingEvent instances.
|
|
110
|
+
"""
|
|
111
|
+
if self._dataname is None:
|
|
112
|
+
raise ValueError("dataname (file path) is required for loading")
|
|
113
|
+
|
|
114
|
+
if self._dataname.endswith(".jsonl") or self._dataname.endswith(".jsonl.gz"):
|
|
115
|
+
yield from self._load_jsonl()
|
|
116
|
+
else:
|
|
117
|
+
yield from self._load_csv()
|
|
118
|
+
|
|
119
|
+
def _load_csv(self) -> Iterator[FundingEvent]:
|
|
120
|
+
"""Load funding rate events from CSV format.
|
|
121
|
+
|
|
122
|
+
Supports both plain CSV and gzip-compressed CSV (.csv.gz).
|
|
123
|
+
Expects columns: timestamp, rate, mark_price. Optional columns:
|
|
124
|
+
next_funding_time, predicted_rate, symbol, exchange.
|
|
125
|
+
|
|
126
|
+
Yields:
|
|
127
|
+
FundingEvent instances.
|
|
128
|
+
"""
|
|
129
|
+
if self._dataname is None:
|
|
130
|
+
raise ValueError("FundingChannel requires a 'dataname' (CSV path) to load from")
|
|
131
|
+
open_func = gzip.open if self._dataname.endswith(".gz") else open
|
|
132
|
+
open_kwargs = (
|
|
133
|
+
{"mode": "rt", "encoding": "utf-8"}
|
|
134
|
+
if self._dataname.endswith(".gz")
|
|
135
|
+
else {"mode": "r", "encoding": "utf-8", "newline": ""}
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# open_func is gzip.open or builtin open; the conditionally-built
|
|
139
|
+
# open_kwargs dict can't be matched to a specific overload by mypy.
|
|
140
|
+
with open_func(self._dataname, **open_kwargs) as f: # type: ignore[call-overload]
|
|
141
|
+
reader = csv.DictReader(f)
|
|
142
|
+
|
|
143
|
+
required = {"timestamp", "rate", "mark_price"}
|
|
144
|
+
if reader.fieldnames:
|
|
145
|
+
missing = required - set(reader.fieldnames)
|
|
146
|
+
if missing:
|
|
147
|
+
raise ValueError(
|
|
148
|
+
f"Missing required columns: {missing}. Found: {reader.fieldnames}"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
for row in reader:
|
|
152
|
+
try:
|
|
153
|
+
fe = FundingEvent(
|
|
154
|
+
timestamp=_parse_required_float(row["timestamp"]),
|
|
155
|
+
symbol=row.get("symbol", self.symbol),
|
|
156
|
+
exchange=row.get("exchange", ""),
|
|
157
|
+
asset_type=row.get("asset_type", "swap"),
|
|
158
|
+
rate=_parse_required_float(row["rate"]),
|
|
159
|
+
mark_price=_parse_required_float(row["mark_price"]),
|
|
160
|
+
next_funding_time=_parse_optional_float(row.get("next_funding_time"))
|
|
161
|
+
or 0.0,
|
|
162
|
+
predicted_rate=_parse_optional_float(row.get("predicted_rate")) or 0.0,
|
|
163
|
+
)
|
|
164
|
+
yield fe
|
|
165
|
+
except (ValueError, KeyError) as e:
|
|
166
|
+
logger.warning("Skipping invalid funding row: %s (error: %s)", row, e)
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
def _load_jsonl(self) -> Iterator[FundingEvent]:
|
|
170
|
+
"""Load funding rate events from JSONL format.
|
|
171
|
+
|
|
172
|
+
Supports both plain JSONL and gzip-compressed JSONL (.jsonl.gz).
|
|
173
|
+
Each line should contain a JSON object with timestamp, rate,
|
|
174
|
+
mark_price, and optional fields.
|
|
175
|
+
|
|
176
|
+
Yields:
|
|
177
|
+
FundingEvent instances.
|
|
178
|
+
"""
|
|
179
|
+
if self._dataname is None:
|
|
180
|
+
raise ValueError("FundingChannel requires a 'dataname' (JSONL path) to load from")
|
|
181
|
+
open_func = gzip.open if self._dataname.endswith(".gz") else open
|
|
182
|
+
open_kwargs = {"mode": "rt", "encoding": "utf-8"}
|
|
183
|
+
|
|
184
|
+
# open_func is gzip.open or builtin open; the kwargs dict can't be
|
|
185
|
+
# matched to a specific overload by mypy.
|
|
186
|
+
with open_func(self._dataname, **open_kwargs) as f: # type: ignore[call-overload]
|
|
187
|
+
for line_num, line in enumerate(f, 1):
|
|
188
|
+
line = line.strip()
|
|
189
|
+
if not line:
|
|
190
|
+
continue
|
|
191
|
+
try:
|
|
192
|
+
data = json.loads(line)
|
|
193
|
+
fe = FundingEvent(
|
|
194
|
+
timestamp=_parse_required_float(data["timestamp"]),
|
|
195
|
+
symbol=data.get("symbol", self.symbol),
|
|
196
|
+
exchange=data.get("exchange", ""),
|
|
197
|
+
asset_type=data.get("asset_type", "swap"),
|
|
198
|
+
rate=_parse_required_float(data["rate"]),
|
|
199
|
+
mark_price=_parse_required_float(data["mark_price"]),
|
|
200
|
+
next_funding_time=_parse_optional_float(data.get("next_funding_time"))
|
|
201
|
+
or 0.0,
|
|
202
|
+
predicted_rate=_parse_optional_float(data.get("predicted_rate")) or 0.0,
|
|
203
|
+
)
|
|
204
|
+
yield fe
|
|
205
|
+
except (ValueError, KeyError, json.JSONDecodeError) as e:
|
|
206
|
+
logger.warning("Skipping invalid funding JSONL line %d: %s", line_num, e)
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
def __repr__(self):
|
|
210
|
+
"""Return a string representation of the channel.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
str: Representation showing symbol, dataname, and buffer stats.
|
|
214
|
+
"""
|
|
215
|
+
return (
|
|
216
|
+
f"FundingRateChannel(symbol={self.symbol!r}, "
|
|
217
|
+
f"dataname={self._dataname!r}, "
|
|
218
|
+
f"events={self._event_count}, "
|
|
219
|
+
f"buffered={len(self._buffer)})"
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _parse_optional_float(value) -> Optional[float]:
|
|
224
|
+
"""Parse an optional float value, returning None for empty/missing.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
value: The value to parse as a float. Can be None, empty string,
|
|
228
|
+
or any value that can be converted to float.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
The float value if parsing succeeds, None otherwise.
|
|
232
|
+
"""
|
|
233
|
+
if value is None or value == "":
|
|
234
|
+
return None
|
|
235
|
+
try:
|
|
236
|
+
number = float(value)
|
|
237
|
+
except (ValueError, TypeError):
|
|
238
|
+
return None
|
|
239
|
+
if not math.isfinite(number):
|
|
240
|
+
return None
|
|
241
|
+
return number
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _parse_required_float(value) -> float:
|
|
245
|
+
number = float(value)
|
|
246
|
+
if not math.isfinite(number):
|
|
247
|
+
raise ValueError(f"Non-finite float value: {value}")
|
|
248
|
+
return number
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Thread-safe live event queue for real-time trading.
|
|
2
|
+
|
|
3
|
+
LiveEventQueue provides a thread-safe priority queue for handling events
|
|
4
|
+
from multiple WebSocket connections or data sources in real-time. It uses
|
|
5
|
+
threading locks and supports timeout-based blocking reads.
|
|
6
|
+
|
|
7
|
+
Example::
|
|
8
|
+
|
|
9
|
+
queue = LiveEventQueue(maxsize=100000)
|
|
10
|
+
|
|
11
|
+
# Producer thread (WebSocket callback)
|
|
12
|
+
queue.put(tick_event, priority=EventPriority.TICK)
|
|
13
|
+
|
|
14
|
+
# Consumer thread (strategy processing)
|
|
15
|
+
event = queue.get(timeout=1.0)
|
|
16
|
+
if event:
|
|
17
|
+
process(event)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import heapq
|
|
21
|
+
import threading
|
|
22
|
+
import time
|
|
23
|
+
from typing import Optional, cast
|
|
24
|
+
|
|
25
|
+
from ..channel import Event, EventPriority
|
|
26
|
+
from ..utils.log_message import get_logger
|
|
27
|
+
|
|
28
|
+
logger = get_logger(__name__)
|
|
29
|
+
|
|
30
|
+
__all__ = ["LiveEventQueue"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LiveEventQueue:
|
|
34
|
+
"""Thread-safe priority event queue for live trading.
|
|
35
|
+
|
|
36
|
+
Events are ordered by (timestamp, priority, sequence) just like
|
|
37
|
+
StreamingEventQueue, but with thread-safety for concurrent producers
|
|
38
|
+
and consumers.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
maxsize: Maximum queue capacity. 0 = unlimited.
|
|
42
|
+
drop_policy: What to do when full: 'drop_oldest' or 'drop_newest'.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, maxsize=0, drop_policy="drop_oldest"):
|
|
46
|
+
"""Initialize the priority queue for live events.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
maxsize: Maximum queue size (0 for unlimited).
|
|
50
|
+
drop_policy: Policy for dropping events when full ('drop_oldest' or 'drop_newest').
|
|
51
|
+
"""
|
|
52
|
+
self._heap = []
|
|
53
|
+
self._lock = threading.Lock()
|
|
54
|
+
self._not_empty = threading.Condition(self._lock)
|
|
55
|
+
self._sequence = 0
|
|
56
|
+
self._maxsize = maxsize
|
|
57
|
+
self._drop_policy = drop_policy
|
|
58
|
+
self._total_put = 0
|
|
59
|
+
self._total_get = 0
|
|
60
|
+
self._total_dropped = 0
|
|
61
|
+
self._closed = False
|
|
62
|
+
|
|
63
|
+
def put(
|
|
64
|
+
self,
|
|
65
|
+
event_data,
|
|
66
|
+
priority=EventPriority.TICK,
|
|
67
|
+
channel_type="",
|
|
68
|
+
channel_name="",
|
|
69
|
+
timestamp=None,
|
|
70
|
+
):
|
|
71
|
+
"""Add an event to the queue (thread-safe).
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
event_data: The event data (EventData subclass).
|
|
75
|
+
priority: Event priority.
|
|
76
|
+
channel_type: Source channel type.
|
|
77
|
+
channel_name: Source channel/symbol name.
|
|
78
|
+
timestamp: Event timestamp. If None, uses event_data.timestamp
|
|
79
|
+
or current time.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
True if the event was added, False if dropped.
|
|
83
|
+
"""
|
|
84
|
+
if self._closed:
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
if timestamp is None:
|
|
88
|
+
timestamp = getattr(event_data, "timestamp", time.time())
|
|
89
|
+
|
|
90
|
+
with self._lock:
|
|
91
|
+
event = Event(
|
|
92
|
+
timestamp=timestamp,
|
|
93
|
+
priority=priority,
|
|
94
|
+
sequence=self._sequence,
|
|
95
|
+
channel_type=channel_type,
|
|
96
|
+
channel_name=channel_name,
|
|
97
|
+
data=event_data,
|
|
98
|
+
)
|
|
99
|
+
self._sequence += 1
|
|
100
|
+
|
|
101
|
+
if self._maxsize > 0 and len(self._heap) >= self._maxsize:
|
|
102
|
+
if self._drop_policy == "drop_oldest":
|
|
103
|
+
heapq.heapreplace(self._heap, event)
|
|
104
|
+
self._total_dropped += 1
|
|
105
|
+
else:
|
|
106
|
+
self._total_dropped += 1
|
|
107
|
+
return False
|
|
108
|
+
else:
|
|
109
|
+
heapq.heappush(self._heap, event)
|
|
110
|
+
|
|
111
|
+
self._total_put += 1
|
|
112
|
+
self._not_empty.notify()
|
|
113
|
+
return True
|
|
114
|
+
|
|
115
|
+
def get(self, timeout=None) -> Optional[Event]:
|
|
116
|
+
"""Get the next event from the queue (thread-safe, blocking).
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
timeout: Maximum seconds to wait. None = block forever.
|
|
120
|
+
0 = non-blocking.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
The next Event, or None if timeout expired or queue is closed.
|
|
124
|
+
"""
|
|
125
|
+
with self._not_empty:
|
|
126
|
+
if timeout == 0:
|
|
127
|
+
if not self._heap:
|
|
128
|
+
return None
|
|
129
|
+
elif timeout is None:
|
|
130
|
+
while not self._heap and not self._closed:
|
|
131
|
+
self._not_empty.wait()
|
|
132
|
+
else:
|
|
133
|
+
end_time = time.monotonic() + timeout
|
|
134
|
+
while not self._heap and not self._closed:
|
|
135
|
+
remaining = end_time - time.monotonic()
|
|
136
|
+
if remaining <= 0:
|
|
137
|
+
return None
|
|
138
|
+
self._not_empty.wait(timeout=remaining)
|
|
139
|
+
|
|
140
|
+
if not self._heap:
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
event = heapq.heappop(self._heap)
|
|
144
|
+
self._total_get += 1
|
|
145
|
+
return cast(Event, event)
|
|
146
|
+
|
|
147
|
+
def peek(self) -> Optional[Event]:
|
|
148
|
+
"""Peek at the next event without removing it (thread-safe)."""
|
|
149
|
+
with self._lock:
|
|
150
|
+
return self._heap[0] if self._heap else None
|
|
151
|
+
|
|
152
|
+
def close(self):
|
|
153
|
+
"""Close the queue, unblocking any waiting consumers."""
|
|
154
|
+
with self._not_empty:
|
|
155
|
+
self._closed = True
|
|
156
|
+
self._not_empty.notify_all()
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def closed(self):
|
|
160
|
+
"""Whether the queue has been closed."""
|
|
161
|
+
return self._closed
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def size(self):
|
|
165
|
+
"""Current number of events in the queue."""
|
|
166
|
+
with self._lock:
|
|
167
|
+
return len(self._heap)
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def empty(self):
|
|
171
|
+
"""Whether the queue is empty."""
|
|
172
|
+
with self._lock:
|
|
173
|
+
return len(self._heap) == 0
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def stats(self):
|
|
177
|
+
"""Queue statistics."""
|
|
178
|
+
with self._lock:
|
|
179
|
+
return {
|
|
180
|
+
"total_put": self._total_put,
|
|
181
|
+
"total_get": self._total_get,
|
|
182
|
+
"total_dropped": self._total_dropped,
|
|
183
|
+
"current_size": len(self._heap),
|
|
184
|
+
"closed": self._closed,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
def __len__(self):
|
|
188
|
+
"""Return the current number of events in the queue.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
int: Number of events currently in the queue.
|
|
192
|
+
"""
|
|
193
|
+
with self._lock:
|
|
194
|
+
return len(self._heap)
|
|
195
|
+
|
|
196
|
+
def __bool__(self):
|
|
197
|
+
"""Return whether the queue has any events.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
bool: True if the queue has at least one event, False otherwise.
|
|
201
|
+
"""
|
|
202
|
+
with self._lock:
|
|
203
|
+
return len(self._heap) > 0
|
|
204
|
+
|
|
205
|
+
def __repr__(self):
|
|
206
|
+
"""Return a string representation of the queue.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
str: Representation showing size and statistics.
|
|
210
|
+
"""
|
|
211
|
+
with self._lock:
|
|
212
|
+
return (
|
|
213
|
+
f"LiveEventQueue(size={len(self._heap)}, "
|
|
214
|
+
f"put={self._total_put}, get={self._total_get}, "
|
|
215
|
+
f"dropped={self._total_dropped})"
|
|
216
|
+
)
|