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,380 @@
|
|
|
1
|
+
"""Exchange matching models for HFT simulation.
|
|
2
|
+
|
|
3
|
+
Defines :class:`ExchangeModel` and concrete variants that decide how an order
|
|
4
|
+
interacts with an order book / trade stream (maker vs taker fills, rejections),
|
|
5
|
+
plus :class:`FillRole` and :class:`OrderResult`. Used by the tick broker's
|
|
6
|
+
matching core.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from enum import Enum
|
|
11
|
+
|
|
12
|
+
from backtrader.order import Order
|
|
13
|
+
|
|
14
|
+
from .queue import ProbQueueModel
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FillRole(Enum):
|
|
18
|
+
"""Side of liquidity the order provided when it got filled.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
MAKER: The order was resting on the book and provided liquidity.
|
|
22
|
+
TAKER: The order aggressed against the book and consumed liquidity.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
MAKER = "maker"
|
|
26
|
+
TAKER = "taker"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class OrderResult:
|
|
31
|
+
"""Outcome of an exchange model's processing of an order.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
action: High-level outcome keyword. One of ``"FILL"`` (the order was
|
|
35
|
+
fully or partially filled), ``"PENDING"`` (the order is resting on
|
|
36
|
+
the book and may fill later) or ``"REJECT"`` (the order was
|
|
37
|
+
rejected, see ``reject_reason`` for the reason code).
|
|
38
|
+
fills: List of fill tuples produced for this order. Each tuple's
|
|
39
|
+
shape is model-specific; for example,
|
|
40
|
+
:class:`SimpleExchangeModel` uses ``(price, quantity, role)``
|
|
41
|
+
while :class:`QueueExchangeModel` prepends the originating
|
|
42
|
+
``order`` object.
|
|
43
|
+
reject_reason: Short code explaining the rejection, populated when
|
|
44
|
+
``action == "REJECT"``. Empty otherwise.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
action: str
|
|
48
|
+
fills: list = field(default_factory=list)
|
|
49
|
+
reject_reason: str = ""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ExchangeModel:
|
|
53
|
+
"""Abstract interface for exchange matching behavior.
|
|
54
|
+
|
|
55
|
+
Subclasses describe how an incoming order interacts with the current
|
|
56
|
+
order book and trade stream, and how subsequent market data updates
|
|
57
|
+
drive fills. All three hook methods receive a snapshot/pending-orders
|
|
58
|
+
view from the matching core.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def on_new_order(self, order, ob_snapshot):
|
|
62
|
+
"""Handle a newly accepted order against ``ob_snapshot``.
|
|
63
|
+
|
|
64
|
+
Subclasses must implement this and return an :class:`OrderResult`
|
|
65
|
+
describing whether the order was filled, pended, or rejected.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
order: The newly accepted order. Provides ``exectype``,
|
|
69
|
+
``isbuy()``, ``price`` and ``size`` accessors.
|
|
70
|
+
ob_snapshot: Order book snapshot at the time the order was
|
|
71
|
+
accepted. Provides ``bids`` and ``asks``.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
OrderResult: Outcome of the matching attempt.
|
|
75
|
+
"""
|
|
76
|
+
raise NotImplementedError
|
|
77
|
+
|
|
78
|
+
def on_trade(self, trade_event, pending_orders):
|
|
79
|
+
"""Process a trade event for any resting pending orders.
|
|
80
|
+
|
|
81
|
+
Subclasses may consume the trade and emit maker-style fills when
|
|
82
|
+
one of the ``pending_orders`` was at the trade price.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
trade_event: The trade event to consume. Provides ``price`` and
|
|
86
|
+
``volume``.
|
|
87
|
+
pending_orders: Iterable of resting orders that may be filled by
|
|
88
|
+
this trade.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
list: Fill tuples produced by the subclass (possibly empty).
|
|
92
|
+
"""
|
|
93
|
+
raise NotImplementedError
|
|
94
|
+
|
|
95
|
+
def on_depth_update(self, ob_event, pending_orders):
|
|
96
|
+
"""Process a depth update to refresh queue-ahead estimates.
|
|
97
|
+
|
|
98
|
+
The default implementation is a no-op. Models that track per-order
|
|
99
|
+
queue position (e.g. :class:`QueueExchangeModel`) override this to
|
|
100
|
+
reconcile each resting order's queue-ahead against the new depth.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
ob_event: Depth update event. May carry ``previous_bids`` /
|
|
104
|
+
``previous_asks`` and ``bids`` / ``asks``.
|
|
105
|
+
pending_orders: Iterable of resting orders that may be affected
|
|
106
|
+
by the depth update.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
list: Fill tuples produced by the subclass (possibly empty).
|
|
110
|
+
"""
|
|
111
|
+
_ = (ob_event, pending_orders)
|
|
112
|
+
return []
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class SimpleExchangeModel(ExchangeModel):
|
|
116
|
+
"""Exchange model that walks the book level-by-level with no queueing.
|
|
117
|
+
|
|
118
|
+
Market orders sweep liquidity against the opposite side of the book.
|
|
119
|
+
Limit orders either fill immediately if they cross the spread or sit
|
|
120
|
+
pending without any queue-position tracking. This model is the right
|
|
121
|
+
choice when queue dynamics are not needed (e.g. fast smoke tests).
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def on_new_order(self, order, ob_snapshot):
|
|
125
|
+
"""Match a new order against the current book with no queueing.
|
|
126
|
+
|
|
127
|
+
Market orders are filled against the opposite side; limit orders
|
|
128
|
+
that cross the spread are matched as takers. Limit orders that do
|
|
129
|
+
not cross the spread are returned as ``PENDING``.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
order: The newly accepted order.
|
|
133
|
+
ob_snapshot: Order book snapshot at acceptance.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
OrderResult: ``"FILL"`` with taker fills when the order matches
|
|
137
|
+
against depth, otherwise ``"PENDING"``.
|
|
138
|
+
"""
|
|
139
|
+
if order.exectype == Order.Market:
|
|
140
|
+
return self._match_against_depth(order, ob_snapshot, FillRole.TAKER)
|
|
141
|
+
if order.exectype == Order.Limit and self._crosses_spread(order, ob_snapshot):
|
|
142
|
+
return self._match_against_depth(order, ob_snapshot, FillRole.TAKER)
|
|
143
|
+
return OrderResult(action="PENDING")
|
|
144
|
+
|
|
145
|
+
def on_trade(self, trade_event, pending_orders):
|
|
146
|
+
"""Ignore trade events (queueing is not simulated).
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
trade_event: The trade event (unused).
|
|
150
|
+
pending_orders: The resting orders (unused).
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
list: Always empty for this model.
|
|
154
|
+
"""
|
|
155
|
+
_ = (trade_event, pending_orders)
|
|
156
|
+
return []
|
|
157
|
+
|
|
158
|
+
def _crosses_spread(self, order, ob_snapshot):
|
|
159
|
+
"""Return True when the order's limit price crosses the best quote.
|
|
160
|
+
|
|
161
|
+
A buy limit crosses when its price is greater than or equal to the
|
|
162
|
+
best ask; a sell limit crosses when its price is less than or equal
|
|
163
|
+
to the best bid.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
order: The order being evaluated. Must expose ``isbuy()`` and
|
|
167
|
+
``price``.
|
|
168
|
+
ob_snapshot: Order book snapshot providing ``bids`` and
|
|
169
|
+
``asks``.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
bool: ``True`` if the limit order can be filled immediately.
|
|
173
|
+
"""
|
|
174
|
+
if order.isbuy():
|
|
175
|
+
best_ask = ob_snapshot.asks[0][0] if ob_snapshot.asks else None
|
|
176
|
+
return best_ask is not None and order.price >= best_ask
|
|
177
|
+
best_bid = ob_snapshot.bids[0][0] if ob_snapshot.bids else None
|
|
178
|
+
return best_bid is not None and order.price <= best_bid
|
|
179
|
+
|
|
180
|
+
def _match_against_depth(self, order, ob_snapshot, role):
|
|
181
|
+
"""Walk the book and fill the order as a taker up to its size.
|
|
182
|
+
|
|
183
|
+
Market orders consume every level until the size is met; limit
|
|
184
|
+
orders stop when the next level's price is on the wrong side of
|
|
185
|
+
``order.price``.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
order: The aggressive order to fill. Provides ``isbuy()``,
|
|
189
|
+
``size`` and, for limits, ``price``.
|
|
190
|
+
ob_snapshot: Order book snapshot to consume.
|
|
191
|
+
role: The :class:`FillRole` to attach to each fill produced
|
|
192
|
+
(typically :attr:`FillRole.TAKER`).
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
OrderResult: ``"FILL"`` with the per-level fills when at least
|
|
196
|
+
one level matched, otherwise ``"PENDING"``.
|
|
197
|
+
"""
|
|
198
|
+
levels = ob_snapshot.asks if order.isbuy() else ob_snapshot.bids
|
|
199
|
+
remaining = getattr(getattr(order, "executed", None), "remsize", None)
|
|
200
|
+
if remaining is None:
|
|
201
|
+
remaining = getattr(order, "size", 0.0)
|
|
202
|
+
remaining = abs(remaining)
|
|
203
|
+
fills = []
|
|
204
|
+
for price, qty in levels:
|
|
205
|
+
if order.exectype == Order.Limit:
|
|
206
|
+
if order.isbuy() and price > order.price:
|
|
207
|
+
break
|
|
208
|
+
if not order.isbuy() and price < order.price:
|
|
209
|
+
break
|
|
210
|
+
fill_qty = min(remaining, qty)
|
|
211
|
+
if fill_qty <= 0:
|
|
212
|
+
continue
|
|
213
|
+
fills.append((price, fill_qty, role))
|
|
214
|
+
remaining -= fill_qty
|
|
215
|
+
if remaining <= 0:
|
|
216
|
+
break
|
|
217
|
+
if not fills:
|
|
218
|
+
return OrderResult(action="PENDING")
|
|
219
|
+
return OrderResult(action="FILL", fills=fills)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class QueueExchangeModel(SimpleExchangeModel):
|
|
223
|
+
"""Exchange model that maintains per-order queue position for maker fills.
|
|
224
|
+
|
|
225
|
+
Limit orders that cross the spread follow :class:`SimpleExchangeModel`'s
|
|
226
|
+
taker semantics (with extra handling for ``GTX``/``FOK`` time-in-force
|
|
227
|
+
flags). Limit orders that do not cross the spread are parked as maker
|
|
228
|
+
orders and their queue-ahead is tracked via the injected queue model
|
|
229
|
+
as trades and depth updates arrive.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
def __init__(
|
|
233
|
+
self,
|
|
234
|
+
queue_model=None,
|
|
235
|
+
queue_model_power: float = 2.0,
|
|
236
|
+
lot_size: float = 1.0,
|
|
237
|
+
tick_size: float = None,
|
|
238
|
+
):
|
|
239
|
+
"""Initialize the queue-aware exchange model.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
queue_model: Optional pre-built queue model. When ``None``, a
|
|
243
|
+
:class:`backtrader.brokers.hft.queue.ProbQueueModel` is
|
|
244
|
+
constructed from ``queue_model_power`` and ``lot_size``.
|
|
245
|
+
queue_model_power: Power exponent forwarded to the default
|
|
246
|
+
:class:`ProbQueueModel` when ``queue_model`` is ``None``.
|
|
247
|
+
lot_size: Default lot size forwarded to the default
|
|
248
|
+
:class:`ProbQueueModel` when ``queue_model`` is ``None``.
|
|
249
|
+
tick_size: Optional price tick size. When provided, price
|
|
250
|
+
equality is computed after rounding to this granularity so
|
|
251
|
+
floating-point noise does not break maker/trade matching.
|
|
252
|
+
``None`` disables tick-based matching.
|
|
253
|
+
"""
|
|
254
|
+
self._queue_model = queue_model or ProbQueueModel(
|
|
255
|
+
power=queue_model_power, lot_size=lot_size
|
|
256
|
+
)
|
|
257
|
+
self._tick_size = float(tick_size) if tick_size is not None else None
|
|
258
|
+
|
|
259
|
+
def on_new_order(self, order, ob_snapshot):
|
|
260
|
+
"""Park the order as a maker or fill it as a taker.
|
|
261
|
+
|
|
262
|
+
Market orders are filled against depth. Crossing limit orders
|
|
263
|
+
follow the taker path and honor ``GTX`` (reject when crossing)
|
|
264
|
+
and ``FOK`` (reject when the full size cannot be filled) flags.
|
|
265
|
+
Non-crossing limit orders are recorded by the queue model and
|
|
266
|
+
returned as ``PENDING`` with the ``_fill_role`` tag set to
|
|
267
|
+
:attr:`FillRole.MAKER`.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
order: The newly accepted order.
|
|
271
|
+
ob_snapshot: Order book snapshot at acceptance.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
OrderResult: ``"FILL"`` for takers, ``"PENDING"`` for resting
|
|
275
|
+
makers, or ``"REJECT"`` for ``GTX``/``FOK`` violations.
|
|
276
|
+
"""
|
|
277
|
+
if order.exectype == Order.Market:
|
|
278
|
+
return self._match_against_depth(order, ob_snapshot, FillRole.TAKER)
|
|
279
|
+
|
|
280
|
+
if order.exectype == Order.Limit:
|
|
281
|
+
if self._crosses_spread(order, ob_snapshot):
|
|
282
|
+
tif = getattr(order, "time_in_force", "GTC")
|
|
283
|
+
taker_result = self._match_against_depth(order, ob_snapshot, FillRole.TAKER)
|
|
284
|
+
filled_qty = sum(fill[1] for fill in taker_result.fills)
|
|
285
|
+
order_qty = abs(getattr(order, "size", 0.0))
|
|
286
|
+
if tif == "GTX":
|
|
287
|
+
return OrderResult(action="REJECT", reject_reason="GTX_CROSSED")
|
|
288
|
+
if tif == "FOK" and filled_qty < order_qty:
|
|
289
|
+
return OrderResult(action="REJECT", reject_reason="FOK_INSUFFICIENT")
|
|
290
|
+
return taker_result
|
|
291
|
+
|
|
292
|
+
self._queue_model.on_new_order(order, ob_snapshot)
|
|
293
|
+
order._fill_role = FillRole.MAKER
|
|
294
|
+
return OrderResult(action="PENDING")
|
|
295
|
+
|
|
296
|
+
return OrderResult(action="PENDING")
|
|
297
|
+
|
|
298
|
+
def on_trade(self, trade_event, pending_orders):
|
|
299
|
+
"""Drive maker fills from incoming trade events.
|
|
300
|
+
|
|
301
|
+
For each resting maker order, check whether the trade price
|
|
302
|
+
matches the order's price (within ``_tick_size`` if configured) and,
|
|
303
|
+
if so, delegate to the queue model's ``update_on_trade``. Any
|
|
304
|
+
resulting fill is appended in the form
|
|
305
|
+
``(order, price, quantity, role)``.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
trade_event: The trade event to consume. Provides ``price`` and
|
|
309
|
+
``volume``.
|
|
310
|
+
pending_orders: Resting orders that may be filled by this trade.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
list: ``(order, price, fillable_qty, FillRole.MAKER)`` tuples
|
|
314
|
+
for orders that filled, possibly empty.
|
|
315
|
+
"""
|
|
316
|
+
fills = []
|
|
317
|
+
for order in pending_orders:
|
|
318
|
+
if getattr(order, "_fill_role", None) != FillRole.MAKER:
|
|
319
|
+
continue
|
|
320
|
+
trade_price = getattr(trade_event, "price", None)
|
|
321
|
+
order_price = getattr(order, "price", None)
|
|
322
|
+
if trade_price is None or order_price is None:
|
|
323
|
+
continue
|
|
324
|
+
if self._tick_size is not None and self._tick_size > 0:
|
|
325
|
+
if round(float(trade_price) / self._tick_size) != round(
|
|
326
|
+
float(order_price) / self._tick_size
|
|
327
|
+
):
|
|
328
|
+
continue
|
|
329
|
+
elif float(trade_price) != float(order_price):
|
|
330
|
+
continue
|
|
331
|
+
fillable = self._queue_model.update_on_trade(order, trade_event)
|
|
332
|
+
if fillable > 0:
|
|
333
|
+
fills.append((order, trade_price, fillable, FillRole.MAKER))
|
|
334
|
+
return fills
|
|
335
|
+
|
|
336
|
+
def on_depth_update(self, ob_event, pending_orders):
|
|
337
|
+
"""Refresh each maker order's queue-ahead from the new depth.
|
|
338
|
+
|
|
339
|
+
For every resting maker order, compute the previous and current
|
|
340
|
+
depth at the order's price (respecting ``_tick_size``) and forward
|
|
341
|
+
the pair to the queue model's ``update_on_depth``.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
ob_event: Depth update event providing ``previous_bids`` /
|
|
345
|
+
``previous_asks`` (or empty) and ``bids`` / ``asks``.
|
|
346
|
+
pending_orders: Resting orders to reconcile.
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
list: Always empty in the current implementation; the method
|
|
350
|
+
is called for its side effect of updating queue-ahead state.
|
|
351
|
+
"""
|
|
352
|
+
fills: list = []
|
|
353
|
+
prev_bids = getattr(ob_event, "previous_bids", None) or []
|
|
354
|
+
prev_asks = getattr(ob_event, "previous_asks", None) or []
|
|
355
|
+
curr_bids = getattr(ob_event, "bids", None) or []
|
|
356
|
+
curr_asks = getattr(ob_event, "asks", None) or []
|
|
357
|
+
|
|
358
|
+
def level_qty(levels, price):
|
|
359
|
+
for level_price, level_qty_value in levels:
|
|
360
|
+
if self._tick_size is not None and self._tick_size > 0:
|
|
361
|
+
if round(float(level_price) / self._tick_size) == round(
|
|
362
|
+
float(price) / self._tick_size
|
|
363
|
+
):
|
|
364
|
+
return float(level_qty_value)
|
|
365
|
+
elif float(level_price) == float(price):
|
|
366
|
+
return float(level_qty_value)
|
|
367
|
+
return 0.0
|
|
368
|
+
|
|
369
|
+
for order in pending_orders:
|
|
370
|
+
if getattr(order, "_fill_role", None) != FillRole.MAKER:
|
|
371
|
+
continue
|
|
372
|
+
price = getattr(order, "price", None)
|
|
373
|
+
if price is None:
|
|
374
|
+
continue
|
|
375
|
+
prev_qty = level_qty(prev_bids if order.isbuy() else prev_asks, price)
|
|
376
|
+
new_qty = level_qty(curr_bids if order.isbuy() else curr_asks, price)
|
|
377
|
+
if abs(prev_qty - new_qty) <= 1e-12:
|
|
378
|
+
continue
|
|
379
|
+
self._queue_model.update_on_depth(order, prev_qty, new_qty)
|
|
380
|
+
return fills
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""Latency models for HFT simulation.
|
|
2
|
+
|
|
3
|
+
Defines :class:`LatencyModel` and implementations (e.g.
|
|
4
|
+
:class:`ConstantLatencyModel`) that model feed, order-entry and order-response
|
|
5
|
+
delays so the tick matching engine can simulate realistic exchange round-trips.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import bisect
|
|
9
|
+
import heapq
|
|
10
|
+
import math
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LatencyModel:
|
|
14
|
+
"""Abstract interface for exchange round-trip latency models.
|
|
15
|
+
|
|
16
|
+
A latency model returns, in seconds, how much delay a market-data feed,
|
|
17
|
+
an order entry or an order response should incur. The three methods are
|
|
18
|
+
kept separate so models can differentiate feed jitter from order-entry
|
|
19
|
+
latency from exchange ack latency.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def feed_latency(self, exch_ts, symbol):
|
|
23
|
+
"""Return the feed latency (in seconds) for ``symbol`` at ``exch_ts``.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
exch_ts: Exchange-side timestamp of the market-data event.
|
|
27
|
+
symbol: Symbol the event is for.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
float: Latency in seconds to be added to ``exch_ts`` to obtain
|
|
31
|
+
the local time at which the event is observed.
|
|
32
|
+
"""
|
|
33
|
+
raise NotImplementedError
|
|
34
|
+
|
|
35
|
+
def order_entry_latency(self, local_ts, symbol):
|
|
36
|
+
"""Return the order-entry latency (in seconds) for ``symbol``.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
local_ts: Local time at which the order is submitted.
|
|
40
|
+
symbol: Symbol the order is for.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
float: Latency in seconds between local submission and the
|
|
44
|
+
order being visible to the exchange.
|
|
45
|
+
"""
|
|
46
|
+
raise NotImplementedError
|
|
47
|
+
|
|
48
|
+
def order_response_latency(self, exch_ts, symbol):
|
|
49
|
+
"""Return the order-response latency (in seconds) for ``symbol``.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
exch_ts: Exchange-side timestamp of the acknowledgement.
|
|
53
|
+
symbol: Symbol the response is for.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
float: Latency in seconds between the exchange producing the
|
|
57
|
+
response and it being received locally.
|
|
58
|
+
"""
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ConstantLatencyModel(LatencyModel):
|
|
63
|
+
"""Constant-latency model: a single value per stage, regardless of time.
|
|
64
|
+
|
|
65
|
+
All three stages (feed, order entry, order response) use fixed
|
|
66
|
+
per-stage values supplied in milliseconds at construction time. This
|
|
67
|
+
is the simplest model and is typically used for deterministic tests.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, feed_latency_ms=0, order_entry_latency_ms=0, order_response_latency_ms=0):
|
|
71
|
+
"""Initialize the constant-latency model.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
feed_latency_ms: Feed latency in milliseconds (default ``0``).
|
|
75
|
+
order_entry_latency_ms: Order-entry latency in milliseconds
|
|
76
|
+
(default ``0``).
|
|
77
|
+
order_response_latency_ms: Order-response latency in
|
|
78
|
+
milliseconds (default ``0``).
|
|
79
|
+
"""
|
|
80
|
+
self._feed_lat = feed_latency_ms / 1000.0
|
|
81
|
+
self._entry_lat = order_entry_latency_ms / 1000.0
|
|
82
|
+
self._resp_lat = order_response_latency_ms / 1000.0
|
|
83
|
+
|
|
84
|
+
def feed_latency(self, exch_ts, symbol):
|
|
85
|
+
"""Return the configured constant feed latency in seconds."""
|
|
86
|
+
_ = (exch_ts, symbol)
|
|
87
|
+
return self._feed_lat
|
|
88
|
+
|
|
89
|
+
def order_entry_latency(self, local_ts, symbol):
|
|
90
|
+
"""Return the configured constant order-entry latency in seconds."""
|
|
91
|
+
_ = (local_ts, symbol)
|
|
92
|
+
return self._entry_lat
|
|
93
|
+
|
|
94
|
+
def order_response_latency(self, exch_ts, symbol):
|
|
95
|
+
"""Return the configured constant order-response latency in seconds."""
|
|
96
|
+
_ = (exch_ts, symbol)
|
|
97
|
+
return self._resp_lat
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class IntpLatencyModel(LatencyModel):
|
|
101
|
+
"""Time-varying latency model that linearly interpolates measured samples.
|
|
102
|
+
|
|
103
|
+
Accepts a sequence of ``(timestamp, feed, entry, resp)`` rows. The
|
|
104
|
+
rows are sorted by timestamp at construction time and lookups for an
|
|
105
|
+
arbitrary query time are performed via linear interpolation between
|
|
106
|
+
the two surrounding samples. The optional ``latency_offset`` is added
|
|
107
|
+
to the query timestamp before lookup, so the same data set can be
|
|
108
|
+
time-shifted without rebuilding the model.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self, latency_data, latency_offset=0.0):
|
|
112
|
+
"""Initialize the interpolated latency model.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
latency_data: Iterable of ``(timestamp, feed_latency,
|
|
116
|
+
entry_latency, resp_latency)`` rows. The first column is
|
|
117
|
+
the exchange-side timestamp (in seconds), the other three
|
|
118
|
+
are the corresponding stage latencies in seconds.
|
|
119
|
+
latency_offset: Optional offset (in seconds) added to the
|
|
120
|
+
query timestamp before the binary search; useful for
|
|
121
|
+
time-shifting an existing profile.
|
|
122
|
+
"""
|
|
123
|
+
self._offset = float(latency_offset)
|
|
124
|
+
rows = sorted(
|
|
125
|
+
(float(row[0]), float(row[1]), float(row[2]), float(row[3])) for row in latency_data
|
|
126
|
+
)
|
|
127
|
+
self._ts = [row[0] for row in rows]
|
|
128
|
+
self._feed = [row[1] for row in rows]
|
|
129
|
+
self._entry = [row[2] for row in rows]
|
|
130
|
+
self._resp = [row[3] for row in rows]
|
|
131
|
+
|
|
132
|
+
def _interp(self, ts, values):
|
|
133
|
+
"""Linearly interpolate ``values`` at the time-shifted query time.
|
|
134
|
+
|
|
135
|
+
Returns the first sample for queries before the first row, the
|
|
136
|
+
last sample for queries past the last row, and a clamped linear
|
|
137
|
+
interpolation in between. Returns ``0.0`` when the model has no
|
|
138
|
+
samples at all.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
ts: Query timestamp (will be offset by ``_offset``).
|
|
142
|
+
values: Per-sample list of latencies (in seconds) sorted in
|
|
143
|
+
the same order as ``_ts``.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
float: Interpolated latency in seconds.
|
|
147
|
+
"""
|
|
148
|
+
if not self._ts:
|
|
149
|
+
return 0.0
|
|
150
|
+
lookup_ts = float(ts) + self._offset
|
|
151
|
+
idx = bisect.bisect_left(self._ts, lookup_ts)
|
|
152
|
+
if idx <= 0:
|
|
153
|
+
return values[0]
|
|
154
|
+
if idx >= len(self._ts):
|
|
155
|
+
return values[-1]
|
|
156
|
+
left_ts = self._ts[idx - 1]
|
|
157
|
+
right_ts = self._ts[idx]
|
|
158
|
+
left_val = values[idx - 1]
|
|
159
|
+
right_val = values[idx]
|
|
160
|
+
if right_ts == left_ts:
|
|
161
|
+
return right_val
|
|
162
|
+
ratio = (lookup_ts - left_ts) / (right_ts - left_ts)
|
|
163
|
+
return left_val + (right_val - left_val) * ratio
|
|
164
|
+
|
|
165
|
+
def feed_latency(self, exch_ts, symbol):
|
|
166
|
+
"""Return the interpolated feed latency for ``exch_ts``."""
|
|
167
|
+
_ = symbol
|
|
168
|
+
return self._interp(exch_ts, self._feed)
|
|
169
|
+
|
|
170
|
+
def order_entry_latency(self, local_ts, symbol):
|
|
171
|
+
"""Return the interpolated order-entry latency for ``local_ts``."""
|
|
172
|
+
_ = symbol
|
|
173
|
+
return self._interp(local_ts, self._entry)
|
|
174
|
+
|
|
175
|
+
def order_response_latency(self, exch_ts, symbol):
|
|
176
|
+
"""Return the interpolated order-response latency for ``exch_ts``."""
|
|
177
|
+
_ = symbol
|
|
178
|
+
return self._interp(exch_ts, self._resp)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class LatencyEngine:
|
|
182
|
+
"""Drives the per-stage latencies for orders and events.
|
|
183
|
+
|
|
184
|
+
Holds the priority queue of in-flight orders (sorted by the time at
|
|
185
|
+
which they should become visible to the exchange) and the set of
|
|
186
|
+
order ids that have been cancelled before they became visible. When
|
|
187
|
+
no latency model is supplied, the engine degenerates to a pass-through
|
|
188
|
+
that returns events and orders without applying any delay.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
def __init__(self, latency_model=None):
|
|
192
|
+
"""Initialize the latency engine.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
latency_model: Optional :class:`LatencyModel` used to compute
|
|
196
|
+
per-stage delays. ``None`` disables all delays.
|
|
197
|
+
"""
|
|
198
|
+
self._model = latency_model
|
|
199
|
+
self._pending_orders = []
|
|
200
|
+
self._cancelled_order_ids = set()
|
|
201
|
+
self._seq = 0
|
|
202
|
+
|
|
203
|
+
def delay_order(self, order, submit_ts, symbol):
|
|
204
|
+
"""Park an order in the visibility queue based on entry latency.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
order: The order that was just submitted locally.
|
|
208
|
+
submit_ts: Local timestamp of the submission.
|
|
209
|
+
symbol: Symbol the order is for.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
float | None: The exchange-visible timestamp the order will
|
|
213
|
+
become visible at, or ``None`` if latency is disabled
|
|
214
|
+
(``latency_model is None``) or the computed delay is
|
|
215
|
+
non-positive.
|
|
216
|
+
"""
|
|
217
|
+
if self._model is None:
|
|
218
|
+
return None
|
|
219
|
+
visible_ts = float(submit_ts) + float(self._model.order_entry_latency(submit_ts, symbol))
|
|
220
|
+
if visible_ts <= float(submit_ts):
|
|
221
|
+
return None
|
|
222
|
+
heapq.heappush(self._pending_orders, (visible_ts, self._seq, order, symbol))
|
|
223
|
+
self._seq += 1
|
|
224
|
+
return visible_ts
|
|
225
|
+
|
|
226
|
+
def cancel_order(self, order):
|
|
227
|
+
"""Mark an in-flight order as cancelled.
|
|
228
|
+
|
|
229
|
+
Cancelled orders are removed from the pending queue the next time
|
|
230
|
+
they would have become visible, so the matching engine never sees
|
|
231
|
+
them.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
order: The order to cancel (identified by ``id(order)``).
|
|
235
|
+
"""
|
|
236
|
+
self._cancelled_order_ids.add(id(order))
|
|
237
|
+
|
|
238
|
+
def get_visible_orders(self, current_ts):
|
|
239
|
+
"""Pop and return all orders whose visibility time has been reached.
|
|
240
|
+
|
|
241
|
+
The order heap is sorted by ``(visible_ts, sequence)``, so equal
|
|
242
|
+
timestamps are popped in submission order. Any order whose id was
|
|
243
|
+
previously passed to :meth:`cancel_order` is discarded and its id
|
|
244
|
+
is removed from the cancelled set.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
current_ts: Current local time used to determine which orders
|
|
248
|
+
have become visible.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
list[tuple]: ``(order, symbol)`` tuples for the orders that
|
|
252
|
+
should be released to the matching engine.
|
|
253
|
+
"""
|
|
254
|
+
visible = []
|
|
255
|
+
now = float(current_ts)
|
|
256
|
+
while self._pending_orders and self._pending_orders[0][0] <= now:
|
|
257
|
+
_, _, order, symbol = heapq.heappop(self._pending_orders)
|
|
258
|
+
if id(order) in self._cancelled_order_ids:
|
|
259
|
+
self._cancelled_order_ids.discard(id(order))
|
|
260
|
+
continue
|
|
261
|
+
visible.append((order, symbol))
|
|
262
|
+
return visible
|
|
263
|
+
|
|
264
|
+
def apply_feed_latency(self, event):
|
|
265
|
+
"""Set ``local_time`` on ``event`` after applying feed latency.
|
|
266
|
+
|
|
267
|
+
The event is mutated in place by setting a ``local_time`` attribute
|
|
268
|
+
equal to the exchange timestamp plus the model's feed latency. If no
|
|
269
|
+
model is configured, a valid receive timestamp already supplied by a
|
|
270
|
+
live feed is preserved; otherwise ``local_time`` falls back to the
|
|
271
|
+
event's exchange timestamp.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
event: The market-data event to adjust. Must expose
|
|
275
|
+
``timestamp`` and (optionally) ``symbol``.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
object: The same event object, for convenient chaining.
|
|
279
|
+
"""
|
|
280
|
+
if self._model is None:
|
|
281
|
+
local_time = getattr(event, "local_time", None)
|
|
282
|
+
if (
|
|
283
|
+
isinstance(local_time, (int, float))
|
|
284
|
+
and not isinstance(local_time, bool)
|
|
285
|
+
and math.isfinite(local_time)
|
|
286
|
+
and local_time > 0
|
|
287
|
+
):
|
|
288
|
+
return event
|
|
289
|
+
setattr(event, "local_time", getattr(event, "timestamp", 0.0))
|
|
290
|
+
return event
|
|
291
|
+
exch_ts = getattr(event, "timestamp", 0.0)
|
|
292
|
+
symbol = getattr(event, "symbol", "")
|
|
293
|
+
setattr(event, "local_time", exch_ts + float(self._model.feed_latency(exch_ts, symbol)))
|
|
294
|
+
return event
|
|
295
|
+
|
|
296
|
+
def get_response_time(self, exch_ts, symbol):
|
|
297
|
+
"""Return the local time at which an exchange ack is received.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
exch_ts: Exchange-side timestamp of the acknowledgement.
|
|
301
|
+
symbol: Symbol the response is for.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
float: ``exch_ts`` plus the model's order-response latency, or
|
|
305
|
+
``exch_ts`` itself when no model is configured.
|
|
306
|
+
"""
|
|
307
|
+
if self._model is None:
|
|
308
|
+
return float(exch_ts)
|
|
309
|
+
return float(exch_ts) + float(self._model.order_response_latency(exch_ts, symbol))
|