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,1228 @@
|
|
|
1
|
+
"""Example HFT quote-builder strategies and input requirements.
|
|
2
|
+
|
|
3
|
+
Provides sample market-making/quoting components (e.g. a GLFT-style quote
|
|
4
|
+
builder) and the :class:`InputRequirement` descriptors that document the data
|
|
5
|
+
inputs each example needs. Reference material, not part of the core engine.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import math
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class InputRequirement:
|
|
17
|
+
"""Describes a required input for an HFT example.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
name: Human-readable name of the input requirement.
|
|
21
|
+
patterns: Tuple of glob patterns that matching input files must satisfy.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
name: str
|
|
25
|
+
patterns: tuple[str, ...]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class StrategyConfig:
|
|
30
|
+
"""Configuration for an HFT strategy.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
builder: Name of the quote builder class to use.
|
|
34
|
+
parameters: Dictionary of strategy parameter names to values.
|
|
35
|
+
interval_ns: Quote update interval in nanoseconds.
|
|
36
|
+
recorder_capacity: Capacity of the order event recorder.
|
|
37
|
+
expected_metrics: Expected performance metrics for validation.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
builder: str
|
|
41
|
+
parameters: dict[str, float]
|
|
42
|
+
interval_ns: int
|
|
43
|
+
recorder_capacity: int
|
|
44
|
+
expected_metrics: dict[str, float] = field(default_factory=dict)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class HFTExampleSpec:
|
|
49
|
+
"""Specification for an HFT backtest example.
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
name: Human-readable name of the example.
|
|
53
|
+
source_notebook: Path to the source notebook file.
|
|
54
|
+
symbol: Trading symbol for the example.
|
|
55
|
+
input_requirements: Tuple of input requirements for the example.
|
|
56
|
+
asset_parameters: Asset-specific parameters dictionary.
|
|
57
|
+
strategy: Strategy configuration for the example.
|
|
58
|
+
demo_supported: Whether demo mode is supported (default: False).
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
name: str
|
|
62
|
+
source_notebook: str
|
|
63
|
+
symbol: str
|
|
64
|
+
input_requirements: tuple[InputRequirement, ...]
|
|
65
|
+
asset_parameters: dict[str, object]
|
|
66
|
+
strategy: StrategyConfig
|
|
67
|
+
demo_supported: bool = False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _as_tuple(value):
|
|
71
|
+
if isinstance(value, tuple):
|
|
72
|
+
return value
|
|
73
|
+
return tuple(value)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _context_value(context, name, default=None):
|
|
77
|
+
if context is None:
|
|
78
|
+
return default
|
|
79
|
+
if isinstance(context, dict):
|
|
80
|
+
return context.get(name, default)
|
|
81
|
+
return getattr(context, name, default)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _measure_trading_intensity(order_arrival_depth, scale=0.5, size=500):
|
|
85
|
+
out = [0.0] * int(size)
|
|
86
|
+
max_tick = 0
|
|
87
|
+
for depth in order_arrival_depth:
|
|
88
|
+
if not math.isfinite(depth):
|
|
89
|
+
continue
|
|
90
|
+
tick = round(float(depth) / float(scale)) - 1
|
|
91
|
+
if tick < 0 or tick >= len(out):
|
|
92
|
+
continue
|
|
93
|
+
for index in range(tick):
|
|
94
|
+
out[index] += 1.0
|
|
95
|
+
max_tick = max(max_tick, tick)
|
|
96
|
+
return out[:max_tick]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _linear_regression(x_values, y_values):
|
|
100
|
+
if len(x_values) != len(y_values) or len(x_values) < 2:
|
|
101
|
+
return math.nan, math.nan
|
|
102
|
+
sx = sum(float(value) for value in x_values)
|
|
103
|
+
sy = sum(float(value) for value in y_values)
|
|
104
|
+
sx2 = sum(float(value) ** 2 for value in x_values)
|
|
105
|
+
sxy = sum(float(x) * float(y) for x, y in zip(x_values, y_values))
|
|
106
|
+
weight = float(len(x_values))
|
|
107
|
+
denominator = weight * sx2 - sx**2
|
|
108
|
+
if abs(denominator) <= 1e-12:
|
|
109
|
+
return math.nan, math.nan
|
|
110
|
+
slope = (weight * sxy - sx * sy) / denominator
|
|
111
|
+
intercept = (sy - slope * sx) / weight
|
|
112
|
+
return slope, intercept
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _compute_coeff(xi, gamma, delta, intensity_a, intensity_k):
|
|
116
|
+
if not all(math.isfinite(value) for value in (xi, gamma, delta, intensity_a, intensity_k)):
|
|
117
|
+
return math.nan, math.nan
|
|
118
|
+
if delta <= 0.0 or intensity_a <= 0.0 or intensity_k <= 0.0:
|
|
119
|
+
return math.nan, math.nan
|
|
120
|
+
inv_k = 1.0 / intensity_k
|
|
121
|
+
c1 = 1.0 / (xi * delta) * math.log(1.0 + xi * delta * inv_k)
|
|
122
|
+
c2 = math.sqrt(
|
|
123
|
+
gamma
|
|
124
|
+
/ (2.0 * intensity_a * delta * intensity_k)
|
|
125
|
+
* ((1.0 + xi * delta * inv_k) ** (intensity_k / (xi * delta) + 1.0))
|
|
126
|
+
)
|
|
127
|
+
return c1, c2
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class PlainGridQuoteBuilder:
|
|
131
|
+
"""Simple grid-based quote builder for market making.
|
|
132
|
+
|
|
133
|
+
Generates bid/ask quotes based on a fixed grid around the mid-price,
|
|
134
|
+
with optional skew adjustment based on position.
|
|
135
|
+
|
|
136
|
+
Attributes:
|
|
137
|
+
tick_size: Minimum price increment for quotes.
|
|
138
|
+
grid_num: Number of grid levels for each side.
|
|
139
|
+
max_position: Maximum position before reducing quote size.
|
|
140
|
+
grid_interval: Spacing between grid levels.
|
|
141
|
+
half_spread: Half spread from reservation price to first quote.
|
|
142
|
+
skew: Position skew coefficient.
|
|
143
|
+
order_qty: Base order quantity.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
def __init__(
|
|
147
|
+
self,
|
|
148
|
+
tick_size=1.0,
|
|
149
|
+
grid_num=20,
|
|
150
|
+
max_position=5.0,
|
|
151
|
+
grid_interval=10.0,
|
|
152
|
+
half_spread=20.0,
|
|
153
|
+
skew=0.0,
|
|
154
|
+
order_qty=0.1,
|
|
155
|
+
):
|
|
156
|
+
"""Initialize the PlainGridQuoteBuilder.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
tick_size: Minimum price increment (default: 1.0).
|
|
160
|
+
grid_num: Number of grid levels (default: 20).
|
|
161
|
+
max_position: Max position before reducing size (default: 5.0).
|
|
162
|
+
grid_interval: Grid level spacing (default: 10.0).
|
|
163
|
+
half_spread: Half spread from reservation price (default: 20.0).
|
|
164
|
+
skew: Position skew coefficient (default: 0.0).
|
|
165
|
+
order_qty: Base order quantity (default: 0.1).
|
|
166
|
+
"""
|
|
167
|
+
self.tick_size = tick_size
|
|
168
|
+
self.grid_num = int(grid_num)
|
|
169
|
+
self.max_position = float(max_position)
|
|
170
|
+
self.grid_interval = float(grid_interval)
|
|
171
|
+
self.half_spread = float(half_spread)
|
|
172
|
+
self.skew = float(skew)
|
|
173
|
+
self.order_qty = float(order_qty)
|
|
174
|
+
self.current_order_qty = float(order_qty)
|
|
175
|
+
|
|
176
|
+
def __call__(self, position, snapshot, context=None):
|
|
177
|
+
"""Generate grid quotes based on current position and market snapshot.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
position: Current position (positive for long, negative for short).
|
|
181
|
+
snapshot: Market snapshot with bids and asks.
|
|
182
|
+
context: Optional context dictionary.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Dictionary with 'buy' and 'sell' quote price lists.
|
|
186
|
+
"""
|
|
187
|
+
self.current_order_qty = self.order_qty
|
|
188
|
+
best_bid = snapshot.bids[0][0]
|
|
189
|
+
best_ask = snapshot.asks[0][0]
|
|
190
|
+
mid_price = (best_bid + best_ask) / 2.0
|
|
191
|
+
normalized_position = 0.0 if self.order_qty == 0.0 else position / self.order_qty
|
|
192
|
+
reservation_price = mid_price - self.skew * normalized_position
|
|
193
|
+
bid_price = min(reservation_price - self.half_spread, best_bid)
|
|
194
|
+
ask_price = max(reservation_price + self.half_spread, best_ask)
|
|
195
|
+
bid_price = (bid_price // self.grid_interval) * self.grid_interval
|
|
196
|
+
ask_price = (-(-ask_price // self.grid_interval)) * self.grid_interval
|
|
197
|
+
|
|
198
|
+
quotes = {}
|
|
199
|
+
if position < self.max_position:
|
|
200
|
+
quotes["buy"] = [
|
|
201
|
+
float(bid_price - i * self.grid_interval) for i in range(self.grid_num)
|
|
202
|
+
]
|
|
203
|
+
if position > -self.max_position:
|
|
204
|
+
quotes["sell"] = [
|
|
205
|
+
float(ask_price + i * self.grid_interval) for i in range(self.grid_num)
|
|
206
|
+
]
|
|
207
|
+
return quotes
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class QueueMarketMakingQuoteBuilder:
|
|
211
|
+
"""Queue-aware market making quote builder.
|
|
212
|
+
|
|
213
|
+
Generates quotes based on order book pressure and queue position,
|
|
214
|
+
with grid-based quote distribution.
|
|
215
|
+
|
|
216
|
+
Attributes:
|
|
217
|
+
tick_size: Minimum price increment.
|
|
218
|
+
order_qty: Base order quantity.
|
|
219
|
+
grid_num: Number of grid levels per side.
|
|
220
|
+
max_position: Maximum position before reducing size.
|
|
221
|
+
half_spread: Half spread from reservation price.
|
|
222
|
+
grid_interval: Grid level spacing.
|
|
223
|
+
skew_adj: Skew adjustment coefficient.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
def __init__(
|
|
227
|
+
self,
|
|
228
|
+
tick_size=1.0,
|
|
229
|
+
order_qty=1.0,
|
|
230
|
+
grid_num=10,
|
|
231
|
+
max_position=10.0,
|
|
232
|
+
half_spread=0.49,
|
|
233
|
+
grid_interval=1.0,
|
|
234
|
+
skew_adj=1.0,
|
|
235
|
+
):
|
|
236
|
+
"""Initialize the QueueMarketMakingQuoteBuilder.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
tick_size: Minimum price increment (default: 1.0).
|
|
240
|
+
order_qty: Base order quantity (default: 1.0).
|
|
241
|
+
grid_num: Number of grid levels (default: 10).
|
|
242
|
+
max_position: Max position (default: 10.0).
|
|
243
|
+
half_spread: Half spread (default: 0.49).
|
|
244
|
+
grid_interval: Grid spacing (default: 1.0).
|
|
245
|
+
skew_adj: Skew adjustment (default: 1.0).
|
|
246
|
+
"""
|
|
247
|
+
self.tick_size = float(tick_size)
|
|
248
|
+
self.order_qty = float(order_qty)
|
|
249
|
+
self.grid_num = int(grid_num)
|
|
250
|
+
self.max_position = float(max_position)
|
|
251
|
+
self.half_spread = float(half_spread)
|
|
252
|
+
self.grid_interval = float(grid_interval)
|
|
253
|
+
self.skew_adj = float(skew_adj)
|
|
254
|
+
self.current_order_qty = float(order_qty)
|
|
255
|
+
|
|
256
|
+
def __call__(self, position, snapshot, context=None):
|
|
257
|
+
"""Generate quotes based on order book pressure.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
position: Current position.
|
|
261
|
+
snapshot: Market snapshot with bids/asks.
|
|
262
|
+
context: Optional context dictionary.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
Dictionary with 'buy' and 'sell' quote price lists.
|
|
266
|
+
"""
|
|
267
|
+
self.current_order_qty = self.order_qty
|
|
268
|
+
best_bid = snapshot.bids[0][0]
|
|
269
|
+
best_ask = snapshot.asks[0][0]
|
|
270
|
+
best_bid_qty = snapshot.bids[0][1]
|
|
271
|
+
best_ask_qty = snapshot.asks[0][1]
|
|
272
|
+
book_pressure = (best_bid * best_ask_qty + best_ask * best_bid_qty) / (
|
|
273
|
+
best_bid_qty + best_ask_qty
|
|
274
|
+
)
|
|
275
|
+
skew = self.half_spread / self.grid_num * self.skew_adj
|
|
276
|
+
normalized_position = 0.0 if self.order_qty == 0.0 else position / self.order_qty
|
|
277
|
+
reservation_price = book_pressure - skew * normalized_position
|
|
278
|
+
bid_price = min(reservation_price - self.half_spread, best_bid)
|
|
279
|
+
ask_price = max(reservation_price + self.half_spread, best_ask)
|
|
280
|
+
bid_price = float((bid_price // self.grid_interval) * self.grid_interval)
|
|
281
|
+
ask_price = float((-(-ask_price // self.grid_interval)) * self.grid_interval)
|
|
282
|
+
|
|
283
|
+
quotes = {}
|
|
284
|
+
if position < self.max_position:
|
|
285
|
+
quotes["buy"] = [
|
|
286
|
+
float(bid_price - i * self.grid_interval) for i in range(self.grid_num)
|
|
287
|
+
]
|
|
288
|
+
if position > -self.max_position:
|
|
289
|
+
quotes["sell"] = [
|
|
290
|
+
float(ask_price + i * self.grid_interval) for i in range(self.grid_num)
|
|
291
|
+
]
|
|
292
|
+
return quotes
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class OBIAlphaQuoteBuilder:
|
|
296
|
+
"""Order Book Imbalance (OBI) alpha-based quote builder.
|
|
297
|
+
|
|
298
|
+
Uses order flow toxicity (imbalance) as an alpha signal to adjust quotes,
|
|
299
|
+
with statistical averaging over a rolling window.
|
|
300
|
+
|
|
301
|
+
Attributes:
|
|
302
|
+
tick_size: Minimum price increment.
|
|
303
|
+
depth_levels: Number of order book levels to consider.
|
|
304
|
+
half_spread: Half spread from reservation price.
|
|
305
|
+
skew: Position skew coefficient.
|
|
306
|
+
c1: Imbalance signal coefficient.
|
|
307
|
+
order_qty: Base order quantity.
|
|
308
|
+
max_position: Maximum position before reducing size.
|
|
309
|
+
window: Rolling window for imbalance statistics.
|
|
310
|
+
grid_num: Number of grid levels per side.
|
|
311
|
+
grid_interval: Spacing between grid levels.
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
def __init__(
|
|
315
|
+
self,
|
|
316
|
+
tick_size=1.0,
|
|
317
|
+
depth_levels=2,
|
|
318
|
+
half_spread=1.0,
|
|
319
|
+
skew=0.5,
|
|
320
|
+
c1=1.0,
|
|
321
|
+
order_qty=1.0,
|
|
322
|
+
max_position=1.0,
|
|
323
|
+
window=3,
|
|
324
|
+
grid_num=1,
|
|
325
|
+
grid_interval=None,
|
|
326
|
+
):
|
|
327
|
+
"""Initialize the OBIAlphaQuoteBuilder.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
tick_size: Minimum price increment (default: 1.0).
|
|
331
|
+
depth_levels: Order book levels to consider (default: 2).
|
|
332
|
+
half_spread: Half spread (default: 1.0).
|
|
333
|
+
skew: Position skew coefficient (default: 0.5).
|
|
334
|
+
c1: Imbalance signal coefficient (default: 1.0).
|
|
335
|
+
order_qty: Base order quantity (default: 1.0).
|
|
336
|
+
max_position: Max position (default: 1.0).
|
|
337
|
+
window: Rolling window size (default: 3).
|
|
338
|
+
grid_num: Grid levels per side (default: 1).
|
|
339
|
+
grid_interval: Grid spacing (default: tick_size).
|
|
340
|
+
"""
|
|
341
|
+
self.tick_size = float(tick_size)
|
|
342
|
+
self.depth_levels = int(depth_levels)
|
|
343
|
+
self.half_spread = float(half_spread)
|
|
344
|
+
self.skew = float(skew)
|
|
345
|
+
self.c1 = float(c1)
|
|
346
|
+
self.order_qty = float(order_qty)
|
|
347
|
+
self.max_position = float(max_position)
|
|
348
|
+
self.window = int(window)
|
|
349
|
+
self.grid_num = int(grid_num)
|
|
350
|
+
self.grid_interval = float(grid_interval if grid_interval is not None else tick_size)
|
|
351
|
+
self.imbalance_history = []
|
|
352
|
+
self.current_order_qty = float(order_qty)
|
|
353
|
+
|
|
354
|
+
def __call__(self, position, snapshot, context=None):
|
|
355
|
+
"""Generate quotes based on order book imbalance alpha.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
position: Current position.
|
|
359
|
+
snapshot: Market snapshot with bids/asks.
|
|
360
|
+
context: Optional context dictionary.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
Dictionary with 'buy' and 'sell' quote price lists.
|
|
364
|
+
"""
|
|
365
|
+
self.current_order_qty = self.order_qty
|
|
366
|
+
best_bid = snapshot.bids[0][0]
|
|
367
|
+
best_ask = snapshot.asks[0][0]
|
|
368
|
+
mid_price = (best_bid + best_ask) / 2.0
|
|
369
|
+
sum_bid_qty = sum(level[1] for level in snapshot.bids[: self.depth_levels])
|
|
370
|
+
sum_ask_qty = sum(level[1] for level in snapshot.asks[: self.depth_levels])
|
|
371
|
+
imbalance = sum_bid_qty - sum_ask_qty
|
|
372
|
+
self.imbalance_history.append(imbalance)
|
|
373
|
+
|
|
374
|
+
window_values = self.imbalance_history[-self.window :]
|
|
375
|
+
mean_value = sum(window_values) / len(window_values)
|
|
376
|
+
variance = sum((value - mean_value) ** 2 for value in window_values) / len(window_values)
|
|
377
|
+
std_value = variance**0.5
|
|
378
|
+
alpha = 0.0 if std_value == 0.0 else (imbalance - mean_value) / std_value
|
|
379
|
+
|
|
380
|
+
fair_price = mid_price + self.c1 * alpha
|
|
381
|
+
normalized_position = 0.0 if self.order_qty == 0.0 else position / self.order_qty
|
|
382
|
+
reservation_price = fair_price - self.skew * normalized_position
|
|
383
|
+
bid_price = min(round(reservation_price - self.half_spread), best_bid)
|
|
384
|
+
ask_price = max(round(reservation_price + self.half_spread), best_ask)
|
|
385
|
+
bid_price = float((bid_price // self.grid_interval) * self.grid_interval)
|
|
386
|
+
ask_price = float((-(-ask_price // self.grid_interval)) * self.grid_interval)
|
|
387
|
+
|
|
388
|
+
quotes = {}
|
|
389
|
+
if position < self.max_position:
|
|
390
|
+
quotes["buy"] = [
|
|
391
|
+
float(bid_price - i * self.grid_interval) for i in range(self.grid_num)
|
|
392
|
+
]
|
|
393
|
+
if position > -self.max_position:
|
|
394
|
+
quotes["sell"] = [
|
|
395
|
+
float(ask_price + i * self.grid_interval) for i in range(self.grid_num)
|
|
396
|
+
]
|
|
397
|
+
return quotes
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class BasisAlphaQuoteBuilder:
|
|
401
|
+
"""Basis alpha quote builder for spread products.
|
|
402
|
+
|
|
403
|
+
Uses basis (spread between spot and futures) as an alpha signal
|
|
404
|
+
to adjust quotes for calendar spread products.
|
|
405
|
+
|
|
406
|
+
Attributes:
|
|
407
|
+
tick_size: Minimum price increment.
|
|
408
|
+
lot_size: Minimum order quantity.
|
|
409
|
+
half_spread: Half spread from fair price.
|
|
410
|
+
skew: Position skew coefficient.
|
|
411
|
+
order_qty_dollar: Dollar-based order quantity.
|
|
412
|
+
max_position_dollar: Maximum position in dollars.
|
|
413
|
+
grid_num: Number of grid levels per side.
|
|
414
|
+
grid_interval: Spacing between grid levels.
|
|
415
|
+
precompute_data: Pre-computed basis data for backtesting.
|
|
416
|
+
"""
|
|
417
|
+
|
|
418
|
+
def __init__(
|
|
419
|
+
self,
|
|
420
|
+
tick_size=0.1,
|
|
421
|
+
lot_size=0.001,
|
|
422
|
+
half_spread=0.0003,
|
|
423
|
+
skew=0.000015,
|
|
424
|
+
order_qty_dollar=50_000.0,
|
|
425
|
+
max_position_dollar=1_000_000.0,
|
|
426
|
+
grid_num=1,
|
|
427
|
+
grid_interval=0.1,
|
|
428
|
+
precompute_data=None,
|
|
429
|
+
):
|
|
430
|
+
"""Initialize the BasisAlphaQuoteBuilder.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
tick_size: Minimum price increment (default: 0.1).
|
|
434
|
+
lot_size: Minimum order quantity (default: 0.001).
|
|
435
|
+
half_spread: Half spread (default: 0.0003).
|
|
436
|
+
skew: Position skew coefficient (default: 0.000015).
|
|
437
|
+
order_qty_dollar: Dollar-based order size (default: 50000.0).
|
|
438
|
+
max_position_dollar: Max position in dollars (default: 1000000.0).
|
|
439
|
+
grid_num: Grid levels per side (default: 1).
|
|
440
|
+
grid_interval: Grid spacing (default: 0.1).
|
|
441
|
+
precompute_data: Pre-computed [timestamp, spot, basis] data.
|
|
442
|
+
"""
|
|
443
|
+
self.tick_size = float(tick_size)
|
|
444
|
+
self.lot_size = float(lot_size)
|
|
445
|
+
self.half_spread = float(half_spread)
|
|
446
|
+
self.skew = float(skew)
|
|
447
|
+
self.order_qty_dollar = float(order_qty_dollar)
|
|
448
|
+
self.max_position_dollar = float(max_position_dollar)
|
|
449
|
+
self.grid_num = int(grid_num)
|
|
450
|
+
self.grid_interval = float(grid_interval)
|
|
451
|
+
self.precompute_data = precompute_data
|
|
452
|
+
self.data_index = 0
|
|
453
|
+
self.last_spot = math.nan
|
|
454
|
+
self.last_basis = math.nan
|
|
455
|
+
self.order_qty = float(lot_size)
|
|
456
|
+
self.current_order_qty = float(lot_size)
|
|
457
|
+
|
|
458
|
+
def _advance(self, timestamp_ns):
|
|
459
|
+
"""Advance the data index based on timestamp.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
timestamp_ns: Current timestamp in nanoseconds.
|
|
463
|
+
"""
|
|
464
|
+
if self.precompute_data is None or timestamp_ns is None:
|
|
465
|
+
return
|
|
466
|
+
while self.data_index < len(self.precompute_data):
|
|
467
|
+
if float(self.precompute_data[self.data_index][0]) > float(timestamp_ns):
|
|
468
|
+
if self.data_index > 0:
|
|
469
|
+
self.last_spot = float(self.precompute_data[self.data_index - 1][1])
|
|
470
|
+
self.last_basis = float(self.precompute_data[self.data_index - 1][2])
|
|
471
|
+
return
|
|
472
|
+
self.data_index += 1
|
|
473
|
+
if len(self.precompute_data) > 0:
|
|
474
|
+
self.last_spot = float(self.precompute_data[-1][1])
|
|
475
|
+
self.last_basis = float(self.precompute_data[-1][2])
|
|
476
|
+
|
|
477
|
+
def __call__(self, position, snapshot, context=None):
|
|
478
|
+
"""Generate quotes based on basis alpha signal.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
position: Current position.
|
|
482
|
+
snapshot: Market snapshot with bids/asks.
|
|
483
|
+
context: Optional context with timestamp_ns for basis lookup.
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
Dictionary with 'buy' and 'sell' quote price lists.
|
|
487
|
+
"""
|
|
488
|
+
best_bid = float(snapshot.bids[0][0])
|
|
489
|
+
best_ask = float(snapshot.asks[0][0])
|
|
490
|
+
mid_price = (best_bid + best_ask) / 2.0
|
|
491
|
+
self.current_order_qty = max(
|
|
492
|
+
round((self.order_qty_dollar / mid_price) / self.lot_size) * self.lot_size,
|
|
493
|
+
self.lot_size,
|
|
494
|
+
)
|
|
495
|
+
self.order_qty = self.current_order_qty
|
|
496
|
+
normalized_position = position / self.current_order_qty if self.current_order_qty else 0.0
|
|
497
|
+
self._advance(_context_value(context, "timestamp_ns"))
|
|
498
|
+
fair_price = (
|
|
499
|
+
self.last_spot + self.last_basis
|
|
500
|
+
if math.isfinite(self.last_spot) and math.isfinite(self.last_basis)
|
|
501
|
+
else mid_price
|
|
502
|
+
)
|
|
503
|
+
relative_bid_depth = self.half_spread + self.skew * normalized_position
|
|
504
|
+
relative_ask_depth = self.half_spread - self.skew * normalized_position
|
|
505
|
+
bid_price = min(fair_price * (1.0 - relative_bid_depth), best_bid)
|
|
506
|
+
ask_price = max(fair_price * (1.0 + relative_ask_depth), best_ask)
|
|
507
|
+
bid_price = math.floor(bid_price / self.tick_size) * self.tick_size
|
|
508
|
+
ask_price = math.ceil(ask_price / self.tick_size) * self.tick_size
|
|
509
|
+
quotes = {}
|
|
510
|
+
if position * mid_price < self.max_position_dollar and math.isfinite(bid_price):
|
|
511
|
+
quotes["buy"] = [
|
|
512
|
+
float(bid_price - i * self.grid_interval) for i in range(self.grid_num)
|
|
513
|
+
]
|
|
514
|
+
if position * mid_price > -self.max_position_dollar and math.isfinite(ask_price):
|
|
515
|
+
quotes["sell"] = [
|
|
516
|
+
float(ask_price + i * self.grid_interval) for i in range(self.grid_num)
|
|
517
|
+
]
|
|
518
|
+
return quotes
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
class APTQuoteBuilder:
|
|
522
|
+
"""Arbitrage Pricing Theory (APT) quote builder for futures.
|
|
523
|
+
|
|
524
|
+
Uses spot return and lagged futures price to estimate fair futures price
|
|
525
|
+
based on APT arbitrage-free pricing model.
|
|
526
|
+
|
|
527
|
+
Attributes:
|
|
528
|
+
tick_size: Minimum price increment.
|
|
529
|
+
lot_size: Minimum order quantity.
|
|
530
|
+
half_spread: Half spread from fair price.
|
|
531
|
+
skew: Position skew coefficient.
|
|
532
|
+
order_qty_dollar: Dollar-based order quantity.
|
|
533
|
+
max_position_dollar: Maximum position in dollars.
|
|
534
|
+
grid_num: Number of grid levels per side.
|
|
535
|
+
grid_interval: Base spacing between grid levels.
|
|
536
|
+
precompute_data: Pre-computed [timestamp, spot_return, ...] data.
|
|
537
|
+
"""
|
|
538
|
+
|
|
539
|
+
def __init__(
|
|
540
|
+
self,
|
|
541
|
+
tick_size=0.1,
|
|
542
|
+
lot_size=0.001,
|
|
543
|
+
half_spread=0.0003,
|
|
544
|
+
skew=0.000015,
|
|
545
|
+
order_qty_dollar=50_000.0,
|
|
546
|
+
max_position_dollar=1_000_000.0,
|
|
547
|
+
grid_num=1,
|
|
548
|
+
grid_interval=0.1,
|
|
549
|
+
precompute_data=None,
|
|
550
|
+
):
|
|
551
|
+
"""Initialize the APTQuoteBuilder.
|
|
552
|
+
|
|
553
|
+
Args:
|
|
554
|
+
tick_size: Minimum price increment (default: 0.1).
|
|
555
|
+
lot_size: Minimum order quantity (default: 0.001).
|
|
556
|
+
half_spread: Half spread (default: 0.0003).
|
|
557
|
+
skew: Position skew coefficient (default: 0.000015).
|
|
558
|
+
order_qty_dollar: Dollar-based order size (default: 50000.0).
|
|
559
|
+
max_position_dollar: Max position in dollars (default: 1000000.0).
|
|
560
|
+
grid_num: Grid levels per side (default: 1).
|
|
561
|
+
grid_interval: Base grid spacing (default: 0.1).
|
|
562
|
+
precompute_data: Pre-computed [timestamp, spot_return, ...] data.
|
|
563
|
+
"""
|
|
564
|
+
self.tick_size = float(tick_size)
|
|
565
|
+
self.lot_size = float(lot_size)
|
|
566
|
+
self.half_spread = float(half_spread)
|
|
567
|
+
self.skew = float(skew)
|
|
568
|
+
self.order_qty_dollar = float(order_qty_dollar)
|
|
569
|
+
self.max_position_dollar = float(max_position_dollar)
|
|
570
|
+
self.grid_num = int(grid_num)
|
|
571
|
+
self.grid_interval = float(grid_interval)
|
|
572
|
+
self.precompute_data = precompute_data
|
|
573
|
+
self.data_index = 0
|
|
574
|
+
self.spot_return = math.nan
|
|
575
|
+
self.futures_past_px = math.nan
|
|
576
|
+
self.order_qty = float(lot_size)
|
|
577
|
+
self.current_order_qty = float(lot_size)
|
|
578
|
+
|
|
579
|
+
def _advance(self, timestamp_ns):
|
|
580
|
+
"""Advance the data index based on timestamp.
|
|
581
|
+
|
|
582
|
+
Args:
|
|
583
|
+
timestamp_ns: Current timestamp in nanoseconds.
|
|
584
|
+
"""
|
|
585
|
+
if self.precompute_data is None or timestamp_ns is None:
|
|
586
|
+
return
|
|
587
|
+
while self.data_index < len(self.precompute_data):
|
|
588
|
+
if float(self.precompute_data[self.data_index][0]) > float(timestamp_ns):
|
|
589
|
+
if self.data_index > 0:
|
|
590
|
+
self.spot_return = float(self.precompute_data[self.data_index - 1][1])
|
|
591
|
+
self.futures_past_px = float(self.precompute_data[self.data_index - 1][4])
|
|
592
|
+
return
|
|
593
|
+
self.data_index += 1
|
|
594
|
+
if len(self.precompute_data) > 0:
|
|
595
|
+
self.spot_return = float(self.precompute_data[-1][1])
|
|
596
|
+
self.futures_past_px = float(self.precompute_data[-1][4])
|
|
597
|
+
|
|
598
|
+
def __call__(self, position, snapshot, context=None):
|
|
599
|
+
"""Generate quotes based on APT arbitrage-free pricing.
|
|
600
|
+
|
|
601
|
+
Args:
|
|
602
|
+
position: Current position.
|
|
603
|
+
snapshot: Market snapshot with bids/asks.
|
|
604
|
+
context: Optional context with timestamp_ns for data lookup.
|
|
605
|
+
|
|
606
|
+
Returns:
|
|
607
|
+
Dictionary with 'buy' and 'sell' quote price lists.
|
|
608
|
+
"""
|
|
609
|
+
best_bid = float(snapshot.bids[0][0])
|
|
610
|
+
best_ask = float(snapshot.asks[0][0])
|
|
611
|
+
mid_price = (best_bid + best_ask) / 2.0
|
|
612
|
+
self.current_order_qty = max(
|
|
613
|
+
round((self.order_qty_dollar / mid_price) / self.lot_size) * self.lot_size,
|
|
614
|
+
self.lot_size,
|
|
615
|
+
)
|
|
616
|
+
self.order_qty = self.current_order_qty
|
|
617
|
+
normalized_position = position / self.current_order_qty if self.current_order_qty else 0.0
|
|
618
|
+
self._advance(_context_value(context, "timestamp_ns"))
|
|
619
|
+
return_ = self.spot_return if math.isfinite(self.spot_return) else 0.0
|
|
620
|
+
fair_price = (
|
|
621
|
+
(1.0 + return_) * self.futures_past_px
|
|
622
|
+
if math.isfinite(self.futures_past_px)
|
|
623
|
+
else mid_price
|
|
624
|
+
)
|
|
625
|
+
relative_bid_depth = self.half_spread + self.skew * normalized_position
|
|
626
|
+
relative_ask_depth = self.half_spread - self.skew * normalized_position
|
|
627
|
+
bid_price = min(fair_price * (1.0 - relative_bid_depth), best_bid)
|
|
628
|
+
ask_price = max(fair_price * (1.0 + relative_ask_depth), best_ask)
|
|
629
|
+
bid_price = math.floor(bid_price / self.tick_size) * self.tick_size
|
|
630
|
+
ask_price = math.ceil(ask_price / self.tick_size) * self.tick_size
|
|
631
|
+
dynamic_grid_interval = max(
|
|
632
|
+
self.tick_size, round(self.grid_interval * fair_price / self.tick_size) * self.tick_size
|
|
633
|
+
)
|
|
634
|
+
bid_price = math.floor(bid_price / dynamic_grid_interval) * dynamic_grid_interval
|
|
635
|
+
ask_price = math.ceil(ask_price / dynamic_grid_interval) * dynamic_grid_interval
|
|
636
|
+
quotes = {}
|
|
637
|
+
if position * mid_price < self.max_position_dollar and math.isfinite(bid_price):
|
|
638
|
+
quotes["buy"] = [
|
|
639
|
+
float(bid_price - i * dynamic_grid_interval) for i in range(self.grid_num)
|
|
640
|
+
]
|
|
641
|
+
if position * mid_price > -self.max_position_dollar and math.isfinite(ask_price):
|
|
642
|
+
quotes["sell"] = [
|
|
643
|
+
float(ask_price + i * dynamic_grid_interval) for i in range(self.grid_num)
|
|
644
|
+
]
|
|
645
|
+
return quotes
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
class GLFTQuoteBuilder:
|
|
649
|
+
"""GLFT (Garman-Lucid-Frey-Li-Tou) quote builder.
|
|
650
|
+
|
|
651
|
+
Implements the GLFT market-making model which estimates fair prices using
|
|
652
|
+
a utility-maximization framework with arrival rate and order flow
|
|
653
|
+
intensity calculations.
|
|
654
|
+
|
|
655
|
+
Attributes:
|
|
656
|
+
tick_size: Minimum price increment.
|
|
657
|
+
lot_size: Minimum order quantity.
|
|
658
|
+
gamma: Risk aversion coefficient.
|
|
659
|
+
delta: Order processing time parameter.
|
|
660
|
+
order_qty: Base order quantity.
|
|
661
|
+
max_position: Maximum position before reducing size.
|
|
662
|
+
grid_num: Number of grid levels per side.
|
|
663
|
+
grid_interval: Spacing between grid levels.
|
|
664
|
+
"""
|
|
665
|
+
|
|
666
|
+
def __init__(
|
|
667
|
+
self,
|
|
668
|
+
tick_size=0.01,
|
|
669
|
+
lot_size=0.001,
|
|
670
|
+
gamma=0.05,
|
|
671
|
+
delta=1.0,
|
|
672
|
+
order_qty=1.0,
|
|
673
|
+
max_position=20.0,
|
|
674
|
+
grid_num=1,
|
|
675
|
+
grid_interval=None,
|
|
676
|
+
):
|
|
677
|
+
"""Initialize the GLFTQuoteBuilder.
|
|
678
|
+
|
|
679
|
+
Args:
|
|
680
|
+
tick_size: Minimum price increment (default: 0.01).
|
|
681
|
+
lot_size: Minimum order quantity (default: 0.001).
|
|
682
|
+
gamma: Risk aversion coefficient (default: 0.05).
|
|
683
|
+
delta: Order processing time (default: 1.0).
|
|
684
|
+
order_qty: Base order quantity (default: 1.0).
|
|
685
|
+
max_position: Max position (default: 20.0).
|
|
686
|
+
grid_num: Grid levels per side (default: 1).
|
|
687
|
+
grid_interval: Grid spacing (default: tick_size).
|
|
688
|
+
"""
|
|
689
|
+
self.tick_size = float(tick_size)
|
|
690
|
+
self.lot_size = float(lot_size)
|
|
691
|
+
self.gamma = float(gamma)
|
|
692
|
+
self.delta = float(delta)
|
|
693
|
+
self.order_qty = float(order_qty)
|
|
694
|
+
self.current_order_qty = float(order_qty)
|
|
695
|
+
self.max_position = float(max_position)
|
|
696
|
+
self.grid_num = int(grid_num)
|
|
697
|
+
self.grid_interval = float(grid_interval if grid_interval is not None else tick_size)
|
|
698
|
+
self.arrival_depth_history = []
|
|
699
|
+
self.mid_price_chg_history = []
|
|
700
|
+
self.mid_price_tick = math.nan
|
|
701
|
+
self.intensity_a = math.nan
|
|
702
|
+
self.intensity_k = math.nan
|
|
703
|
+
self.volatility = math.nan
|
|
704
|
+
self.step = 0
|
|
705
|
+
|
|
706
|
+
def __call__(self, position, snapshot, context=None):
|
|
707
|
+
"""Generate quotes based on GLFT utility-maximization model.
|
|
708
|
+
|
|
709
|
+
Args:
|
|
710
|
+
position: Current position.
|
|
711
|
+
snapshot: Market snapshot with bids/asks.
|
|
712
|
+
context: Optional context with last_trades for flow analysis.
|
|
713
|
+
|
|
714
|
+
Returns:
|
|
715
|
+
Dictionary with 'buy' and 'sell' quote price lists.
|
|
716
|
+
"""
|
|
717
|
+
last_trades = _context_value(context, "last_trades", ()) or ()
|
|
718
|
+
if math.isfinite(self.mid_price_tick):
|
|
719
|
+
arrival_depth = -math.inf
|
|
720
|
+
for trade in last_trades:
|
|
721
|
+
trade_price = float(getattr(trade, "price", getattr(trade, "px", math.nan)))
|
|
722
|
+
trade_direction = str(getattr(trade, "direction", "")).lower()
|
|
723
|
+
if not math.isfinite(trade_price):
|
|
724
|
+
continue
|
|
725
|
+
trade_tick = trade_price / self.tick_size
|
|
726
|
+
if trade_direction == "buy":
|
|
727
|
+
arrival_depth = max(trade_tick - self.mid_price_tick, arrival_depth)
|
|
728
|
+
else:
|
|
729
|
+
arrival_depth = max(self.mid_price_tick - trade_tick, arrival_depth)
|
|
730
|
+
self.arrival_depth_history.append(arrival_depth)
|
|
731
|
+
else:
|
|
732
|
+
self.arrival_depth_history.append(math.nan)
|
|
733
|
+
|
|
734
|
+
best_bid_tick = float(snapshot.bids[0][0]) / self.tick_size
|
|
735
|
+
best_ask_tick = float(snapshot.asks[0][0]) / self.tick_size
|
|
736
|
+
previous_mid_price_tick = self.mid_price_tick
|
|
737
|
+
self.mid_price_tick = (best_bid_tick + best_ask_tick) / 2.0
|
|
738
|
+
self.mid_price_chg_history.append(
|
|
739
|
+
self.mid_price_tick - previous_mid_price_tick
|
|
740
|
+
if math.isfinite(previous_mid_price_tick)
|
|
741
|
+
else math.nan
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
if self.step % 50 == 0 and self.step >= 5_999:
|
|
745
|
+
intensity_window = _measure_trading_intensity(self.arrival_depth_history[-6_000:])
|
|
746
|
+
if len(intensity_window) > 2:
|
|
747
|
+
x_values = []
|
|
748
|
+
y_values = []
|
|
749
|
+
for index, value in enumerate(intensity_window[:70]):
|
|
750
|
+
rate = float(value) / 600.0
|
|
751
|
+
if rate <= 0.0:
|
|
752
|
+
continue
|
|
753
|
+
x_values.append(index + 0.5)
|
|
754
|
+
y_values.append(math.log(rate))
|
|
755
|
+
slope, intercept = _linear_regression(x_values, y_values)
|
|
756
|
+
if math.isfinite(slope) and math.isfinite(intercept):
|
|
757
|
+
self.intensity_a = math.exp(intercept)
|
|
758
|
+
self.intensity_k = -slope
|
|
759
|
+
window = [
|
|
760
|
+
value for value in self.mid_price_chg_history[-6_000:] if math.isfinite(value)
|
|
761
|
+
]
|
|
762
|
+
if window:
|
|
763
|
+
mean_value = sum(window) / len(window)
|
|
764
|
+
variance = sum((value - mean_value) ** 2 for value in window) / len(window)
|
|
765
|
+
self.volatility = math.sqrt(variance) * math.sqrt(10.0)
|
|
766
|
+
|
|
767
|
+
c1, c2 = _compute_coeff(
|
|
768
|
+
self.gamma, self.gamma, self.delta, self.intensity_a, self.intensity_k
|
|
769
|
+
)
|
|
770
|
+
half_spread_tick = (
|
|
771
|
+
c1 + self.delta / 2.0 * c2 * self.volatility
|
|
772
|
+
if all(math.isfinite(value) for value in (c1, c2, self.volatility))
|
|
773
|
+
else 0.0
|
|
774
|
+
)
|
|
775
|
+
skew = (
|
|
776
|
+
c2 * self.volatility
|
|
777
|
+
if all(math.isfinite(value) for value in (c2, self.volatility))
|
|
778
|
+
else 0.0
|
|
779
|
+
)
|
|
780
|
+
reservation_price_tick = self.mid_price_tick - skew * position
|
|
781
|
+
bid_price_tick = min(round(reservation_price_tick - half_spread_tick), round(best_bid_tick))
|
|
782
|
+
ask_price_tick = max(round(reservation_price_tick + half_spread_tick), round(best_ask_tick))
|
|
783
|
+
bid_price = bid_price_tick * self.tick_size
|
|
784
|
+
ask_price = ask_price_tick * self.tick_size
|
|
785
|
+
self.step += 1
|
|
786
|
+
quotes = {}
|
|
787
|
+
if position < self.max_position and math.isfinite(bid_price):
|
|
788
|
+
quotes["buy"] = [
|
|
789
|
+
float(bid_price - i * self.grid_interval) for i in range(self.grid_num)
|
|
790
|
+
]
|
|
791
|
+
if position > -self.max_position and math.isfinite(ask_price):
|
|
792
|
+
quotes["sell"] = [
|
|
793
|
+
float(ask_price + i * self.grid_interval) for i in range(self.grid_num)
|
|
794
|
+
]
|
|
795
|
+
return quotes
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def get_hftbacktest_example_specs():
|
|
799
|
+
"""Get all HFT backtest example specifications.
|
|
800
|
+
|
|
801
|
+
Returns:
|
|
802
|
+
List of HFTExampleSpec objects defining available examples.
|
|
803
|
+
"""
|
|
804
|
+
return [
|
|
805
|
+
HFTExampleSpec(
|
|
806
|
+
name="plain_grid",
|
|
807
|
+
source_notebook="High-Frequency Grid Trading.ipynb",
|
|
808
|
+
symbol="ETHUSDT",
|
|
809
|
+
demo_supported=True,
|
|
810
|
+
input_requirements=(
|
|
811
|
+
InputRequirement(
|
|
812
|
+
name="market_data",
|
|
813
|
+
patterns=(
|
|
814
|
+
"data/ethusdt_20221003.npz",
|
|
815
|
+
"data/ethusdt_20221004.npz",
|
|
816
|
+
"data/ethusdt_20221005.npz",
|
|
817
|
+
"data/ethusdt_20221006.npz",
|
|
818
|
+
"data/ethusdt_20221007.npz",
|
|
819
|
+
),
|
|
820
|
+
),
|
|
821
|
+
InputRequirement(
|
|
822
|
+
name="initial_snapshot", patterns=("data/ethusdt_20221002_eod.npz",)
|
|
823
|
+
),
|
|
824
|
+
InputRequirement(
|
|
825
|
+
name="latency_data",
|
|
826
|
+
patterns=(
|
|
827
|
+
"latency/feed_latency_20221003.npz",
|
|
828
|
+
"latency/feed_latency_20221004.npz",
|
|
829
|
+
"latency/feed_latency_20221005.npz",
|
|
830
|
+
"latency/feed_latency_20221006.npz",
|
|
831
|
+
"latency/feed_latency_20221007.npz",
|
|
832
|
+
),
|
|
833
|
+
),
|
|
834
|
+
),
|
|
835
|
+
asset_parameters={
|
|
836
|
+
"asset_type": "linear",
|
|
837
|
+
"mult": 1.0,
|
|
838
|
+
"tick_size": 0.01,
|
|
839
|
+
"lot_size": 0.001,
|
|
840
|
+
"roi_lb": 0.0,
|
|
841
|
+
"roi_ub": 3000.0,
|
|
842
|
+
"queue_model": "power_prob_queue_model",
|
|
843
|
+
"queue_model_power": 2.0,
|
|
844
|
+
"exchange_model": "no_partial_fill_exchange",
|
|
845
|
+
"maker_commission": -0.00005,
|
|
846
|
+
"taker_commission": 0.0007,
|
|
847
|
+
},
|
|
848
|
+
strategy=StrategyConfig(
|
|
849
|
+
builder="PlainGridQuoteBuilder",
|
|
850
|
+
interval_ns=100_000_000,
|
|
851
|
+
recorder_capacity=5_000_000,
|
|
852
|
+
parameters={
|
|
853
|
+
"grid_num": 20,
|
|
854
|
+
"max_position": 5.0,
|
|
855
|
+
"grid_interval": 0.1,
|
|
856
|
+
"half_spread": 0.2,
|
|
857
|
+
"skew": 0.0,
|
|
858
|
+
"order_qty": 0.1,
|
|
859
|
+
},
|
|
860
|
+
),
|
|
861
|
+
),
|
|
862
|
+
HFTExampleSpec(
|
|
863
|
+
name="queue_market_making",
|
|
864
|
+
source_notebook="Queue-Based Market Making in Large Tick Size Assets.ipynb",
|
|
865
|
+
symbol="CRVUSDT",
|
|
866
|
+
demo_supported=True,
|
|
867
|
+
input_requirements=(
|
|
868
|
+
InputRequirement(
|
|
869
|
+
name="market_data",
|
|
870
|
+
patterns=_as_tuple(
|
|
871
|
+
[
|
|
872
|
+
f"data/CRVUSDT_{date}.npz"
|
|
873
|
+
for date in list(range(20240701, 20240732))
|
|
874
|
+
+ list(range(20240801, 20240832))
|
|
875
|
+
]
|
|
876
|
+
),
|
|
877
|
+
),
|
|
878
|
+
InputRequirement(
|
|
879
|
+
name="latency_data",
|
|
880
|
+
patterns=_as_tuple(
|
|
881
|
+
[
|
|
882
|
+
f"latency/amp_feed_latency_{date}.npz"
|
|
883
|
+
for date in list(range(20240701, 20240732))
|
|
884
|
+
+ list(range(20240801, 20240832))
|
|
885
|
+
]
|
|
886
|
+
),
|
|
887
|
+
),
|
|
888
|
+
),
|
|
889
|
+
asset_parameters={
|
|
890
|
+
"asset_type": "linear",
|
|
891
|
+
"mult": 1.0,
|
|
892
|
+
"tick_size": 0.001,
|
|
893
|
+
"lot_size": 0.1,
|
|
894
|
+
"roi_lb": 0.0,
|
|
895
|
+
"roi_ub": 2.0,
|
|
896
|
+
"queue_model": "power_prob_queue_model",
|
|
897
|
+
"queue_model_power": 3.0,
|
|
898
|
+
"exchange_model": "no_partial_fill_exchange",
|
|
899
|
+
"maker_commission": -0.00005,
|
|
900
|
+
"taker_commission": 0.0007,
|
|
901
|
+
"last_trades_capacity": 1000,
|
|
902
|
+
},
|
|
903
|
+
strategy=StrategyConfig(
|
|
904
|
+
builder="QueueMarketMakingQuoteBuilder",
|
|
905
|
+
interval_ns=100_000_000,
|
|
906
|
+
recorder_capacity=100_000_000,
|
|
907
|
+
parameters={
|
|
908
|
+
"order_qty": 1.0,
|
|
909
|
+
"grid_num": 10,
|
|
910
|
+
"max_position": 10.0,
|
|
911
|
+
"half_spread": 0.00049,
|
|
912
|
+
"grid_interval": 0.001,
|
|
913
|
+
"skew_adj": 1.0,
|
|
914
|
+
},
|
|
915
|
+
expected_metrics={
|
|
916
|
+
"Return": 2.848749,
|
|
917
|
+
"MaxDrawdown": 0.096359,
|
|
918
|
+
"DailyNumberOfTrades": 106.774393,
|
|
919
|
+
"DailyTradingValue": 30.241524,
|
|
920
|
+
},
|
|
921
|
+
),
|
|
922
|
+
),
|
|
923
|
+
HFTExampleSpec(
|
|
924
|
+
name="obi_alpha_market_making",
|
|
925
|
+
source_notebook="Market Making with Alpha - Order Book Imbalance.ipynb",
|
|
926
|
+
symbol="BTCUSDT",
|
|
927
|
+
demo_supported=True,
|
|
928
|
+
input_requirements=(
|
|
929
|
+
InputRequirement(
|
|
930
|
+
name="market_data",
|
|
931
|
+
patterns=_as_tuple(
|
|
932
|
+
[f"data2/btcusdt_{date}.npz" for date in range(20230501, 20230532)]
|
|
933
|
+
),
|
|
934
|
+
),
|
|
935
|
+
InputRequirement(
|
|
936
|
+
name="initial_snapshot", patterns=("data2/btcusdt_20230430_eod.npz",)
|
|
937
|
+
),
|
|
938
|
+
InputRequirement(
|
|
939
|
+
name="latency_data",
|
|
940
|
+
patterns=_as_tuple(
|
|
941
|
+
[
|
|
942
|
+
f"latency/live_order_latency_{date}.npz"
|
|
943
|
+
for date in range(20230501, 20230532)
|
|
944
|
+
]
|
|
945
|
+
),
|
|
946
|
+
),
|
|
947
|
+
),
|
|
948
|
+
asset_parameters={
|
|
949
|
+
"asset_type": "linear",
|
|
950
|
+
"mult": 1.0,
|
|
951
|
+
"tick_size": 0.1,
|
|
952
|
+
"lot_size": 0.001,
|
|
953
|
+
"roi_lb": 10000.0,
|
|
954
|
+
"roi_ub": 50000.0,
|
|
955
|
+
"queue_model": "power_prob_queue_model",
|
|
956
|
+
"queue_model_power": 2.0,
|
|
957
|
+
"exchange_model": "no_partial_fill_exchange",
|
|
958
|
+
"maker_commission": -0.00005,
|
|
959
|
+
"taker_commission": 0.0007,
|
|
960
|
+
},
|
|
961
|
+
strategy=StrategyConfig(
|
|
962
|
+
builder="OBIAlphaQuoteBuilder",
|
|
963
|
+
interval_ns=1_000_000_000,
|
|
964
|
+
recorder_capacity=30_000_000,
|
|
965
|
+
parameters={
|
|
966
|
+
"tick_size": 0.1,
|
|
967
|
+
"depth_levels": 2,
|
|
968
|
+
"half_spread": 80.0,
|
|
969
|
+
"skew": 3.5,
|
|
970
|
+
"c1": 160.0,
|
|
971
|
+
"order_qty": 0.001,
|
|
972
|
+
"max_position": 50.0,
|
|
973
|
+
"window": 3600,
|
|
974
|
+
"grid_num": 1,
|
|
975
|
+
"grid_interval": 0.1,
|
|
976
|
+
},
|
|
977
|
+
expected_metrics={
|
|
978
|
+
"Return": 0.342371,
|
|
979
|
+
"MaxDrawdown": 0.037249,
|
|
980
|
+
"DailyNumberOfTrades": 4119.876838,
|
|
981
|
+
"DailyTurnover": 82.397448,
|
|
982
|
+
},
|
|
983
|
+
),
|
|
984
|
+
),
|
|
985
|
+
HFTExampleSpec(
|
|
986
|
+
name="basis_alpha_market_making",
|
|
987
|
+
source_notebook="Market Making with Alpha - Basis.ipynb",
|
|
988
|
+
symbol="BTCUSDT",
|
|
989
|
+
input_requirements=(
|
|
990
|
+
InputRequirement(
|
|
991
|
+
name="market_data",
|
|
992
|
+
patterns=_as_tuple(
|
|
993
|
+
[
|
|
994
|
+
f"data2/btcusdt_{date}.npz"
|
|
995
|
+
for date in list(range(20240901, 20240931))
|
|
996
|
+
+ list(range(20241001, 20241032))
|
|
997
|
+
]
|
|
998
|
+
),
|
|
999
|
+
),
|
|
1000
|
+
InputRequirement(
|
|
1001
|
+
name="initial_snapshot", patterns=("data2/btcusdt_20240831_eod.npz",)
|
|
1002
|
+
),
|
|
1003
|
+
InputRequirement(
|
|
1004
|
+
name="latency_data",
|
|
1005
|
+
patterns=_as_tuple(
|
|
1006
|
+
[
|
|
1007
|
+
f"latency/order_latency_{date}.npz"
|
|
1008
|
+
for date in list(range(20240901, 20240931))
|
|
1009
|
+
+ list(range(20241001, 20241032))
|
|
1010
|
+
]
|
|
1011
|
+
),
|
|
1012
|
+
),
|
|
1013
|
+
InputRequirement(name="precompute_data", patterns=("px_basis_BTCUSDT_5m.npz",)),
|
|
1014
|
+
),
|
|
1015
|
+
asset_parameters={
|
|
1016
|
+
"asset_type": "linear",
|
|
1017
|
+
"mult": 1.0,
|
|
1018
|
+
"tick_size": 0.1,
|
|
1019
|
+
"lot_size": 0.001,
|
|
1020
|
+
"roi_lb": 10000.0,
|
|
1021
|
+
"roi_ub": 90000.0,
|
|
1022
|
+
"queue_model": "power_prob_queue_model",
|
|
1023
|
+
"queue_model_power": 3.0,
|
|
1024
|
+
"exchange_model": "no_partial_fill_exchange",
|
|
1025
|
+
"maker_commission": -0.00005,
|
|
1026
|
+
"taker_commission": 0.0007,
|
|
1027
|
+
},
|
|
1028
|
+
strategy=StrategyConfig(
|
|
1029
|
+
builder="BasisAlphaQuoteBuilder",
|
|
1030
|
+
interval_ns=100_000_000,
|
|
1031
|
+
recorder_capacity=60_000_000,
|
|
1032
|
+
parameters={
|
|
1033
|
+
"tick_size": 0.1,
|
|
1034
|
+
"lot_size": 0.001,
|
|
1035
|
+
"half_spread": 0.0003,
|
|
1036
|
+
"skew": 0.000015,
|
|
1037
|
+
"order_qty_dollar": 50_000.0,
|
|
1038
|
+
"max_position_dollar": 1_000_000.0,
|
|
1039
|
+
"grid_num": 1,
|
|
1040
|
+
"grid_interval": 0.1,
|
|
1041
|
+
},
|
|
1042
|
+
),
|
|
1043
|
+
),
|
|
1044
|
+
HFTExampleSpec(
|
|
1045
|
+
name="apt_alpha_market_making",
|
|
1046
|
+
source_notebook="Market Making with Alpha - APT.ipynb",
|
|
1047
|
+
symbol="BTCUSDT",
|
|
1048
|
+
input_requirements=(
|
|
1049
|
+
InputRequirement(
|
|
1050
|
+
name="market_data",
|
|
1051
|
+
patterns=_as_tuple(
|
|
1052
|
+
[
|
|
1053
|
+
f"data2/btcusdt_{date}.npz"
|
|
1054
|
+
for date in list(range(20240901, 20240931))
|
|
1055
|
+
+ list(range(20241001, 20241032))
|
|
1056
|
+
]
|
|
1057
|
+
),
|
|
1058
|
+
),
|
|
1059
|
+
InputRequirement(
|
|
1060
|
+
name="initial_snapshot", patterns=("data2/btcusdt_20240831_eod.npz",)
|
|
1061
|
+
),
|
|
1062
|
+
InputRequirement(
|
|
1063
|
+
name="latency_data",
|
|
1064
|
+
patterns=_as_tuple(
|
|
1065
|
+
[
|
|
1066
|
+
f"latency/order_latency_{date}.npz"
|
|
1067
|
+
for date in list(range(20240901, 20240931))
|
|
1068
|
+
+ list(range(20241001, 20241032))
|
|
1069
|
+
]
|
|
1070
|
+
),
|
|
1071
|
+
),
|
|
1072
|
+
InputRequirement(
|
|
1073
|
+
name="precompute_data", patterns=("precompute_px_return_BTCUSDT_5m.npz",)
|
|
1074
|
+
),
|
|
1075
|
+
),
|
|
1076
|
+
asset_parameters={
|
|
1077
|
+
"asset_type": "linear",
|
|
1078
|
+
"mult": 1.0,
|
|
1079
|
+
"tick_size": 0.1,
|
|
1080
|
+
"lot_size": 0.001,
|
|
1081
|
+
"roi_lb": 10000.0,
|
|
1082
|
+
"roi_ub": 90000.0,
|
|
1083
|
+
"queue_model": "power_prob_queue_model",
|
|
1084
|
+
"queue_model_power": 3.0,
|
|
1085
|
+
"exchange_model": "no_partial_fill_exchange",
|
|
1086
|
+
"maker_commission": -0.00005,
|
|
1087
|
+
"taker_commission": 0.0007,
|
|
1088
|
+
},
|
|
1089
|
+
strategy=StrategyConfig(
|
|
1090
|
+
builder="APTQuoteBuilder",
|
|
1091
|
+
interval_ns=100_000_000,
|
|
1092
|
+
recorder_capacity=60_000_000,
|
|
1093
|
+
parameters={
|
|
1094
|
+
"tick_size": 0.1,
|
|
1095
|
+
"lot_size": 0.001,
|
|
1096
|
+
"half_spread": 0.0003,
|
|
1097
|
+
"skew": 0.000015,
|
|
1098
|
+
"order_qty_dollar": 50_000.0,
|
|
1099
|
+
"max_position_dollar": 1_000_000.0,
|
|
1100
|
+
"grid_num": 1,
|
|
1101
|
+
"grid_interval": 0.1,
|
|
1102
|
+
},
|
|
1103
|
+
),
|
|
1104
|
+
),
|
|
1105
|
+
HFTExampleSpec(
|
|
1106
|
+
name="glft_market_making",
|
|
1107
|
+
source_notebook="GLFT Market Making Model and Grid Trading.ipynb",
|
|
1108
|
+
symbol="ETHUSDT",
|
|
1109
|
+
input_requirements=(
|
|
1110
|
+
InputRequirement(name="market_data", patterns=("data/ethusdt_20221003.npz",)),
|
|
1111
|
+
InputRequirement(
|
|
1112
|
+
name="initial_snapshot", patterns=("data/ethusdt_20221002_eod.npz",)
|
|
1113
|
+
),
|
|
1114
|
+
InputRequirement(
|
|
1115
|
+
name="latency_data", patterns=("latency/feed_latency_20221003.npz",)
|
|
1116
|
+
),
|
|
1117
|
+
),
|
|
1118
|
+
asset_parameters={
|
|
1119
|
+
"asset_type": "linear",
|
|
1120
|
+
"mult": 1.0,
|
|
1121
|
+
"tick_size": 0.01,
|
|
1122
|
+
"lot_size": 0.001,
|
|
1123
|
+
"roi_lb": 0.0,
|
|
1124
|
+
"roi_ub": 3000.0,
|
|
1125
|
+
"queue_model": "power_prob_queue_model",
|
|
1126
|
+
"queue_model_power": 2.0,
|
|
1127
|
+
"exchange_model": "no_partial_fill_exchange",
|
|
1128
|
+
"maker_commission": -0.00005,
|
|
1129
|
+
"taker_commission": 0.0007,
|
|
1130
|
+
"last_trades_capacity": 10000,
|
|
1131
|
+
},
|
|
1132
|
+
strategy=StrategyConfig(
|
|
1133
|
+
builder="GLFTQuoteBuilder",
|
|
1134
|
+
interval_ns=100_000_000,
|
|
1135
|
+
recorder_capacity=5_000_000,
|
|
1136
|
+
parameters={
|
|
1137
|
+
"tick_size": 0.01,
|
|
1138
|
+
"lot_size": 0.001,
|
|
1139
|
+
"gamma": 0.05,
|
|
1140
|
+
"delta": 1.0,
|
|
1141
|
+
"order_qty": 1.0,
|
|
1142
|
+
"max_position": 20.0,
|
|
1143
|
+
"grid_num": 1,
|
|
1144
|
+
"grid_interval": 0.01,
|
|
1145
|
+
},
|
|
1146
|
+
),
|
|
1147
|
+
),
|
|
1148
|
+
]
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
def get_hftbacktest_example_spec(name):
|
|
1152
|
+
"""Get a single HFT backtest example specification by name.
|
|
1153
|
+
|
|
1154
|
+
Args:
|
|
1155
|
+
name: Name of the example spec to retrieve.
|
|
1156
|
+
|
|
1157
|
+
Returns:
|
|
1158
|
+
HFTExampleSpec matching the given name.
|
|
1159
|
+
|
|
1160
|
+
Raises:
|
|
1161
|
+
StopIteration: If no spec with the given name exists.
|
|
1162
|
+
"""
|
|
1163
|
+
return next(spec for spec in get_hftbacktest_example_specs() if spec.name == name)
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
def get_hftbacktest_demo_example_specs():
|
|
1167
|
+
"""Get all demo-supported HFT backtest example specifications.
|
|
1168
|
+
|
|
1169
|
+
Returns:
|
|
1170
|
+
Tuple of HFTExampleSpec objects that have demo_supported=True.
|
|
1171
|
+
"""
|
|
1172
|
+
return tuple(spec for spec in get_hftbacktest_example_specs() if spec.demo_supported)
|
|
1173
|
+
|
|
1174
|
+
|
|
1175
|
+
def build_input_manifest(spec, base_path):
|
|
1176
|
+
"""Build a manifest of resolved and missing input files for a spec.
|
|
1177
|
+
|
|
1178
|
+
Args:
|
|
1179
|
+
spec: HFTExampleSpec to build manifest for.
|
|
1180
|
+
base_path: Base directory path to search for input files.
|
|
1181
|
+
|
|
1182
|
+
Returns:
|
|
1183
|
+
Dictionary with 'ready' (bool), 'resolved' (dict), and 'missing' (dict).
|
|
1184
|
+
"""
|
|
1185
|
+
base = Path(base_path)
|
|
1186
|
+
resolved = {}
|
|
1187
|
+
missing = {}
|
|
1188
|
+
for requirement in spec.input_requirements:
|
|
1189
|
+
resolved_paths = []
|
|
1190
|
+
missing_patterns = []
|
|
1191
|
+
for pattern in requirement.patterns:
|
|
1192
|
+
matches = sorted(str(path) for path in base.glob(pattern))
|
|
1193
|
+
if matches:
|
|
1194
|
+
resolved_paths.extend(matches)
|
|
1195
|
+
else:
|
|
1196
|
+
missing_patterns.append(pattern)
|
|
1197
|
+
resolved[requirement.name] = tuple(resolved_paths)
|
|
1198
|
+
if missing_patterns:
|
|
1199
|
+
missing[requirement.name] = tuple(missing_patterns)
|
|
1200
|
+
return {
|
|
1201
|
+
"ready": not missing,
|
|
1202
|
+
"resolved": resolved,
|
|
1203
|
+
"missing": missing,
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
def build_quote_builder(spec):
|
|
1208
|
+
"""Build a quote builder instance from a strategy specification.
|
|
1209
|
+
|
|
1210
|
+
Args:
|
|
1211
|
+
spec: HFTExampleSpec containing the strategy configuration.
|
|
1212
|
+
|
|
1213
|
+
Returns:
|
|
1214
|
+
Instance of the appropriate quote builder class.
|
|
1215
|
+
|
|
1216
|
+
Raises:
|
|
1217
|
+
KeyError: If the builder name is not recognized.
|
|
1218
|
+
"""
|
|
1219
|
+
mapping = {
|
|
1220
|
+
"PlainGridQuoteBuilder": PlainGridQuoteBuilder,
|
|
1221
|
+
"QueueMarketMakingQuoteBuilder": QueueMarketMakingQuoteBuilder,
|
|
1222
|
+
"OBIAlphaQuoteBuilder": OBIAlphaQuoteBuilder,
|
|
1223
|
+
"BasisAlphaQuoteBuilder": BasisAlphaQuoteBuilder,
|
|
1224
|
+
"APTQuoteBuilder": APTQuoteBuilder,
|
|
1225
|
+
"GLFTQuoteBuilder": GLFTQuoteBuilder,
|
|
1226
|
+
}
|
|
1227
|
+
builder_cls = mapping[spec.strategy.builder]
|
|
1228
|
+
return builder_cls(**spec.strategy.parameters)
|