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,572 @@
|
|
|
1
|
+
"""Order-matching core for the tick-level broker.
|
|
2
|
+
|
|
3
|
+
Defines the fill/match data structures (:class:`FillReport`, :class:`MatchResult`)
|
|
4
|
+
and the matching engine that turns tick/depth events plus pending orders into
|
|
5
|
+
fills, applying the configured exchange and queue models.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
|
|
10
|
+
from backtrader.order import Order
|
|
11
|
+
|
|
12
|
+
from .exchange import FillRole
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class FillReport:
|
|
17
|
+
"""Single execution report produced by the matching engine.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
order: The :class:`backtrader.order.Order` instance that was
|
|
21
|
+
filled (kept by reference so callers can correlate the fill
|
|
22
|
+
back to the originating strategy order).
|
|
23
|
+
fill_price: Trade price at which this fill leg executed.
|
|
24
|
+
fill_size: Signed quantity executed on this fill leg. Always
|
|
25
|
+
positive; the side (buy/sell) is carried by ``order``.
|
|
26
|
+
role: Whether this fill came from a taker or maker execution.
|
|
27
|
+
``"taker"`` for liquidity-taking orders and ``"maker"`` for
|
|
28
|
+
passive orders that were resting in the book.
|
|
29
|
+
timestamp: Simulation timestamp at which the fill happened
|
|
30
|
+
(typically the upstream exchange or local-receive timestamp).
|
|
31
|
+
source: Origin tag of the event that triggered the fill — usually
|
|
32
|
+
``"tick"`` for trade prints or ``"orderbook_depth"`` for
|
|
33
|
+
top-of-book updates.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
order: object
|
|
37
|
+
fill_price: float
|
|
38
|
+
fill_size: float
|
|
39
|
+
role: str = "taker"
|
|
40
|
+
timestamp: float = 0.0
|
|
41
|
+
source: str = "tick"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class MatchResult:
|
|
46
|
+
"""Outcome of an operation against the matching engine.
|
|
47
|
+
|
|
48
|
+
Attributes:
|
|
49
|
+
action: One of ``"ACCEPTED"``, ``"REJECT"``, ``"MODIFIED"``,
|
|
50
|
+
``"CANCELED"``, ``"FILL"`` or ``"PENDING"`` describing what
|
|
51
|
+
the matching engine did with the request/event.
|
|
52
|
+
fills: List of :class:`FillReport` entries produced by this
|
|
53
|
+
operation. Empty when no fill happened (e.g. an order was
|
|
54
|
+
accepted but no market data was available yet).
|
|
55
|
+
reject_reason: Human-readable explanation populated when
|
|
56
|
+
``action == "REJECT"``. Empty otherwise.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
action: str
|
|
60
|
+
fills: list = field(default_factory=list)
|
|
61
|
+
reject_reason: str = ""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class CancelResult:
|
|
66
|
+
"""Outcome of an order cancellation request.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
success: ``True`` if the order was found and removed from the
|
|
70
|
+
matching engine's pending book; ``False`` otherwise.
|
|
71
|
+
reason: Human-readable explanation populated when ``success`` is
|
|
72
|
+
``False`` (currently ``"ORDER_NOT_FOUND"`` when the order
|
|
73
|
+
is not in the pending set). Empty for successful cancels.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
success: bool
|
|
77
|
+
reason: str = ""
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class MatchingCore:
|
|
81
|
+
"""Tick/depth matching engine for the HFT broker.
|
|
82
|
+
|
|
83
|
+
The matching core owns the pending-order book (per symbol) and
|
|
84
|
+
reacts to incoming tick and order-book events by matching them
|
|
85
|
+
against pending orders. The actual fill logic (price-time priority,
|
|
86
|
+
slippage, queue position, etc.) is delegated to two pluggable
|
|
87
|
+
helpers:
|
|
88
|
+
|
|
89
|
+
* ``latency_engine`` — when provided, decides when a freshly
|
|
90
|
+
submitted order becomes "visible" to the matching engine. While
|
|
91
|
+
invisible the order is parked in the latency engine rather than
|
|
92
|
+
in the core's pending buckets, and :meth:`activate_orders`
|
|
93
|
+
promotes it once it becomes visible.
|
|
94
|
+
* ``exchange_model`` — when provided, decides whether an incoming
|
|
95
|
+
tick or order-book event actually triggers a fill for a pending
|
|
96
|
+
order. The default internal matcher (``_match_tick_order`` /
|
|
97
|
+
``_match_orderbook_order``) implements a simple limit/market
|
|
98
|
+
price-time matching, which is bypassed when the exchange model
|
|
99
|
+
returns ``"FILL"`` or ``"REJECT"`` for the order.
|
|
100
|
+
|
|
101
|
+
Attributes:
|
|
102
|
+
_latency: Pluggable latency engine (``None`` to skip latency).
|
|
103
|
+
_exchange_model: Pluggable exchange model (``None`` to use the
|
|
104
|
+
built-in matcher).
|
|
105
|
+
_pending_by_symbol: Mapping ``symbol -> list[Order]`` of orders
|
|
106
|
+
that are currently sitting in the matching engine's book.
|
|
107
|
+
_order_to_symbol: Mapping ``id(order) -> symbol`` used to look
|
|
108
|
+
up the symbol of an order that the caller hands back via
|
|
109
|
+
:meth:`cancel_order` / :meth:`remove_order` (the order
|
|
110
|
+
itself does not always expose its symbol).
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, latency_engine=None, exchange_model=None):
|
|
114
|
+
"""Initialize the matching core.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
latency_engine: Optional pluggable latency engine. When
|
|
118
|
+
supplied, freshly submitted orders are routed through it
|
|
119
|
+
and only become visible to the matcher after the
|
|
120
|
+
configured entry latency elapses (see
|
|
121
|
+
:meth:`activate_orders`).
|
|
122
|
+
exchange_model: Optional pluggable exchange model. When
|
|
123
|
+
supplied, ``on_tick`` and ``on_orderbook`` delegate the
|
|
124
|
+
fill decision to ``exchange_model.on_trade`` /
|
|
125
|
+
``exchange_model.on_new_order`` instead of using the
|
|
126
|
+
built-in limit/market matcher.
|
|
127
|
+
"""
|
|
128
|
+
self._latency = latency_engine
|
|
129
|
+
self._exchange_model = exchange_model
|
|
130
|
+
self._pending_by_symbol = {}
|
|
131
|
+
self._order_to_symbol = {}
|
|
132
|
+
|
|
133
|
+
def _get_symbol(self, order):
|
|
134
|
+
data = getattr(order, "data", None)
|
|
135
|
+
if data is None:
|
|
136
|
+
return ""
|
|
137
|
+
return getattr(data, "_name", None) or getattr(data, "symbol", str(data))
|
|
138
|
+
|
|
139
|
+
def _bucket(self, symbol):
|
|
140
|
+
if symbol not in self._pending_by_symbol:
|
|
141
|
+
self._pending_by_symbol[symbol] = []
|
|
142
|
+
return self._pending_by_symbol[symbol]
|
|
143
|
+
|
|
144
|
+
def _add_pending(self, order, symbol=None):
|
|
145
|
+
symbol = symbol or self._get_symbol(order)
|
|
146
|
+
bucket = self._bucket(symbol)
|
|
147
|
+
if order not in bucket:
|
|
148
|
+
bucket.append(order)
|
|
149
|
+
self._order_to_symbol[id(order)] = symbol
|
|
150
|
+
|
|
151
|
+
def submit_order(self, order, current_ts=0.0):
|
|
152
|
+
"""Submit a new order to the matching engine.
|
|
153
|
+
|
|
154
|
+
If a latency engine is configured the order is first routed
|
|
155
|
+
through it; orders that have not yet become visible (latency
|
|
156
|
+
still pending) are kept inside the latency engine and will
|
|
157
|
+
be promoted to the matching book later via
|
|
158
|
+
:meth:`activate_orders`. Orders that are immediately visible —
|
|
159
|
+
or for which no latency engine is configured — are appended
|
|
160
|
+
to the per-symbol pending bucket.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
order: Order object to submit. ``order.data`` is consulted
|
|
164
|
+
to derive its trading symbol.
|
|
165
|
+
current_ts: Simulation timestamp at which the submission
|
|
166
|
+
happens. Forwarded to ``latency_engine.delay_order``
|
|
167
|
+
when a latency engine is configured.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
MatchResult: ``MatchResult(action="ACCEPTED")``. The result
|
|
171
|
+
is always ``ACCEPTED`` — rejections are signalled by the
|
|
172
|
+
caller (e.g. broker) after inspecting fill notifications.
|
|
173
|
+
"""
|
|
174
|
+
symbol = self._get_symbol(order)
|
|
175
|
+
if self._latency is not None:
|
|
176
|
+
visible_ts = self._latency.delay_order(order, current_ts, symbol)
|
|
177
|
+
if visible_ts is not None:
|
|
178
|
+
self._order_to_symbol[id(order)] = symbol
|
|
179
|
+
return MatchResult(action="ACCEPTED")
|
|
180
|
+
self._add_pending(order, symbol)
|
|
181
|
+
return MatchResult(action="ACCEPTED")
|
|
182
|
+
|
|
183
|
+
def modify_order(self, order, replacement_order, current_ts=0.0):
|
|
184
|
+
"""Replace ``order`` with ``replacement_order``.
|
|
185
|
+
|
|
186
|
+
Modification is implemented as "cancel + resubmit": the original
|
|
187
|
+
order is removed from the matching book and, if a replacement
|
|
188
|
+
was supplied, it is re-submitted using the same
|
|
189
|
+
:meth:`submit_order` path so that latency-engine rules apply
|
|
190
|
+
uniformly.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
order: Existing order to replace. Looked up in the pending
|
|
194
|
+
book (and the latency engine when relevant).
|
|
195
|
+
replacement_order: New order to submit in place of
|
|
196
|
+
``order``. ``None`` means "cancel only" — the operation
|
|
197
|
+
succeeds but no replacement is queued.
|
|
198
|
+
current_ts: Simulation timestamp at which the modification
|
|
199
|
+
is being applied. Forwarded to :meth:`submit_order` for
|
|
200
|
+
the replacement.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
MatchResult: ``MODIFIED`` when the original was successfully
|
|
204
|
+
cancelled and the replacement was queued, ``CANCELED`` when
|
|
205
|
+
no replacement was supplied, ``REJECT`` if the original
|
|
206
|
+
could not be found in either the pending book or the
|
|
207
|
+
latency engine.
|
|
208
|
+
"""
|
|
209
|
+
cancel_result = self.cancel_order(order)
|
|
210
|
+
if not cancel_result.success:
|
|
211
|
+
return MatchResult(action="REJECT", reject_reason=cancel_result.reason)
|
|
212
|
+
if replacement_order is None:
|
|
213
|
+
return MatchResult(action="CANCELED")
|
|
214
|
+
self.submit_order(replacement_order, current_ts=current_ts)
|
|
215
|
+
return MatchResult(action="MODIFIED")
|
|
216
|
+
|
|
217
|
+
def activate_orders(self, current_ts):
|
|
218
|
+
"""Promote orders whose entry latency has elapsed into the matching book.
|
|
219
|
+
|
|
220
|
+
For every order that the latency engine reports as having
|
|
221
|
+
become visible at ``current_ts``, the order is moved into the
|
|
222
|
+
per-symbol pending bucket and returned in the activation list
|
|
223
|
+
so that the caller (typically the broker) can run post-activation
|
|
224
|
+
hooks (queue-position computation, etc.).
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
current_ts: Current simulation timestamp. Compared against
|
|
228
|
+
the visibility timestamp returned by the latency
|
|
229
|
+
engine.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
list[Order]: Orders that were activated at ``current_ts``.
|
|
233
|
+
Empty when no latency engine is configured or when nothing
|
|
234
|
+
has yet become visible.
|
|
235
|
+
"""
|
|
236
|
+
if self._latency is None:
|
|
237
|
+
return []
|
|
238
|
+
activated = []
|
|
239
|
+
for order, symbol in self._latency.get_visible_orders(current_ts):
|
|
240
|
+
self._add_pending(order, symbol)
|
|
241
|
+
activated.append(order)
|
|
242
|
+
return activated
|
|
243
|
+
|
|
244
|
+
def cancel_order(self, order):
|
|
245
|
+
"""Remove ``order`` from the matching engine (pending book or latency engine).
|
|
246
|
+
|
|
247
|
+
The method first looks up the order's symbol via the
|
|
248
|
+
``_order_to_symbol`` cache (falling back to deriving it from
|
|
249
|
+
``order.data``), then attempts to remove it from the per-symbol
|
|
250
|
+
pending bucket. If the order is not in the pending bucket but a
|
|
251
|
+
latency engine is configured, the latency engine is asked to
|
|
252
|
+
cancel it instead — useful for orders that have been submitted
|
|
253
|
+
but not yet become visible.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
order: Order to cancel. Must be the same object that was
|
|
257
|
+
passed to :meth:`submit_order` / :meth:`modify_order`,
|
|
258
|
+
because the engine identifies orders by ``id(order)``.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
CancelResult: ``CancelResult(success=True)`` when the order
|
|
262
|
+
was removed from either the pending book or the latency
|
|
263
|
+
engine; ``CancelResult(success=False,
|
|
264
|
+
reason="ORDER_NOT_FOUND")`` otherwise.
|
|
265
|
+
"""
|
|
266
|
+
symbol = self._order_to_symbol.get(id(order), self._get_symbol(order))
|
|
267
|
+
bucket = self._pending_by_symbol.get(symbol, [])
|
|
268
|
+
try:
|
|
269
|
+
bucket.remove(order)
|
|
270
|
+
self._order_to_symbol.pop(id(order), None)
|
|
271
|
+
if not bucket and symbol in self._pending_by_symbol:
|
|
272
|
+
del self._pending_by_symbol[symbol]
|
|
273
|
+
return CancelResult(success=True)
|
|
274
|
+
except ValueError:
|
|
275
|
+
if self._latency is not None:
|
|
276
|
+
self._latency.cancel_order(order)
|
|
277
|
+
self._order_to_symbol.pop(id(order), None)
|
|
278
|
+
return CancelResult(success=True)
|
|
279
|
+
return CancelResult(success=False, reason="ORDER_NOT_FOUND")
|
|
280
|
+
|
|
281
|
+
def remove_order(self, order):
|
|
282
|
+
"""Silently drop ``order`` from the matching book without raising.
|
|
283
|
+
|
|
284
|
+
Used by the broker when an order transitions to a terminal state
|
|
285
|
+
(rejected, expired, margin-called) and the matching engine
|
|
286
|
+
should forget about it. Unlike :meth:`cancel_order`, this method
|
|
287
|
+
never returns a result and never raises — the absence of the
|
|
288
|
+
order is not an error condition.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
order: Order to forget. Looked up by ``id(order)`` first and
|
|
292
|
+
falls back to deriving the symbol from ``order.data``.
|
|
293
|
+
"""
|
|
294
|
+
symbol = self._order_to_symbol.pop(id(order), self._get_symbol(order))
|
|
295
|
+
bucket = self._pending_by_symbol.get(symbol, [])
|
|
296
|
+
try:
|
|
297
|
+
bucket.remove(order)
|
|
298
|
+
except ValueError:
|
|
299
|
+
return
|
|
300
|
+
if not bucket and symbol in self._pending_by_symbol:
|
|
301
|
+
del self._pending_by_symbol[symbol]
|
|
302
|
+
|
|
303
|
+
def pending_for_symbol(self, symbol):
|
|
304
|
+
"""Return a copy of the pending book for ``symbol``.
|
|
305
|
+
|
|
306
|
+
The returned list is a snapshot; mutating it does not affect
|
|
307
|
+
the engine's internal state.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
symbol: Trading symbol to look up.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
list[Order]: Pending orders in arrival order for ``symbol``.
|
|
314
|
+
Empty list if the symbol has no pending orders.
|
|
315
|
+
"""
|
|
316
|
+
return list(self._pending_by_symbol.get(symbol, []))
|
|
317
|
+
|
|
318
|
+
def pending_orders(self):
|
|
319
|
+
"""Return a flat snapshot of every pending order across all symbols.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
list[Order]: Pending orders across all symbols, in an
|
|
323
|
+
unspecified but stable order (one bucket after another, in
|
|
324
|
+
``_pending_by_symbol`` insertion order).
|
|
325
|
+
"""
|
|
326
|
+
result = []
|
|
327
|
+
for bucket in self._pending_by_symbol.values():
|
|
328
|
+
result.extend(bucket)
|
|
329
|
+
return result
|
|
330
|
+
|
|
331
|
+
def on_tick(self, tick_event):
|
|
332
|
+
"""Match a single trade tick against the pending book of its symbol.
|
|
333
|
+
|
|
334
|
+
When an exchange model is configured, its
|
|
335
|
+
``on_trade(tick_event, pending_orders)`` is consulted first to
|
|
336
|
+
generate fills; when no exchange model is set, the built-in
|
|
337
|
+
limit/market matcher (:meth:`_match_tick_order`) is used
|
|
338
|
+
instead. Orders whose ``_fill_role`` is :data:`FillRole.MAKER`
|
|
339
|
+
are skipped from the built-in matcher because the exchange
|
|
340
|
+
model is expected to drive their lifecycle.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
tick_event: Tick-like object exposing ``symbol``,
|
|
344
|
+
``timestamp`` and ``price``. The exact type is not
|
|
345
|
+
enforced — the engine only relies on these three
|
|
346
|
+
attributes.
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
MatchResult: ``MatchResult(action="FILL", fills=[...])`` if
|
|
350
|
+
at least one fill was produced; otherwise
|
|
351
|
+
``MatchResult(action="PENDING", fills=[])``. The fills list
|
|
352
|
+
holds one :class:`FillReport` per matched order.
|
|
353
|
+
"""
|
|
354
|
+
symbol = getattr(tick_event, "symbol", "")
|
|
355
|
+
fills = []
|
|
356
|
+
pending = list(self.pending_for_symbol(symbol))
|
|
357
|
+
if self._exchange_model is not None:
|
|
358
|
+
for order, price, size, role in self._exchange_model.on_trade(tick_event, pending):
|
|
359
|
+
fills.append(
|
|
360
|
+
self._build_fill(
|
|
361
|
+
order, price, size, tick_event.timestamp, source=role.value, role=role.value
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
for order in pending:
|
|
366
|
+
if (
|
|
367
|
+
self._exchange_model is not None
|
|
368
|
+
and getattr(order, "_fill_role", None) == FillRole.MAKER
|
|
369
|
+
):
|
|
370
|
+
continue
|
|
371
|
+
result = self._match_tick_order(order, tick_event)
|
|
372
|
+
if result is None:
|
|
373
|
+
continue
|
|
374
|
+
price, size = result
|
|
375
|
+
fills.append(self._build_fill(order, price, size, tick_event.timestamp))
|
|
376
|
+
|
|
377
|
+
return MatchResult(action="FILL" if fills else "PENDING", fills=fills)
|
|
378
|
+
|
|
379
|
+
def on_orderbook(self, ob_event):
|
|
380
|
+
"""Match a single order-book snapshot against the pending book of its symbol.
|
|
381
|
+
|
|
382
|
+
For each pending order of ``ob_event.symbol`` the method first
|
|
383
|
+
consults the configured exchange model (``on_new_order``). If
|
|
384
|
+
the model reports ``"REJECT"`` the engine short-circuits and
|
|
385
|
+
returns a :class:`MatchResult` with that reject reason; if it
|
|
386
|
+
reports ``"FILL"`` the aggregated fill price/size is recorded
|
|
387
|
+
and the loop continues with the next order. Otherwise the
|
|
388
|
+
built-in order-book matcher (:meth:`_match_orderbook_order`)
|
|
389
|
+
runs and may produce a fill.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
ob_event: Order-book event exposing ``symbol``,
|
|
393
|
+
``timestamp``, ``asks`` and ``bids``. ``asks``/``bids``
|
|
394
|
+
are expected to be sequences of ``(price, qty)`` tuples
|
|
395
|
+
sorted from best to worst.
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
MatchResult: ``MatchResult(action="FILL", fills=[...])``
|
|
399
|
+
when at least one fill was produced;
|
|
400
|
+
``MatchResult(action="REJECT", reject_reason=...)`` when
|
|
401
|
+
the exchange model rejected an order;
|
|
402
|
+
``MatchResult(action="PENDING", fills=[])`` when no fill
|
|
403
|
+
happened. The ``"REJECT"`` path returns immediately, so any
|
|
404
|
+
remaining orders are not processed in the same call.
|
|
405
|
+
"""
|
|
406
|
+
symbol = getattr(ob_event, "symbol", "")
|
|
407
|
+
fills = []
|
|
408
|
+
for order in list(self.pending_for_symbol(symbol)):
|
|
409
|
+
if self._exchange_model is not None and order.exectype in (Order.Market, Order.Limit):
|
|
410
|
+
exchange_result = self._exchange_model.on_new_order(order, ob_event)
|
|
411
|
+
if exchange_result.action == "REJECT":
|
|
412
|
+
return MatchResult(action="REJECT", reject_reason=exchange_result.reject_reason)
|
|
413
|
+
if exchange_result.action == "FILL":
|
|
414
|
+
price, size = self._aggregate_exchange_fills(exchange_result.fills)
|
|
415
|
+
if size > 0:
|
|
416
|
+
fills.append(
|
|
417
|
+
self._build_fill(
|
|
418
|
+
order, price, size, ob_event.timestamp, source="orderbook_depth"
|
|
419
|
+
)
|
|
420
|
+
)
|
|
421
|
+
continue
|
|
422
|
+
if getattr(order, "_fill_role", None) == FillRole.MAKER:
|
|
423
|
+
continue
|
|
424
|
+
|
|
425
|
+
result = self._match_orderbook_order(order, ob_event)
|
|
426
|
+
if result is None:
|
|
427
|
+
continue
|
|
428
|
+
price, size = result
|
|
429
|
+
fills.append(
|
|
430
|
+
self._build_fill(order, price, size, ob_event.timestamp, source="orderbook_depth")
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
return MatchResult(action="FILL" if fills else "PENDING", fills=fills)
|
|
434
|
+
|
|
435
|
+
def _build_fill(self, order, price, size, timestamp, source="tick", role="taker"):
|
|
436
|
+
return FillReport(
|
|
437
|
+
order=order,
|
|
438
|
+
fill_price=price,
|
|
439
|
+
fill_size=size,
|
|
440
|
+
role=role,
|
|
441
|
+
timestamp=timestamp,
|
|
442
|
+
source=source,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
@staticmethod
|
|
446
|
+
def _remaining_size(order):
|
|
447
|
+
remaining = getattr(getattr(order, "executed", None), "remsize", None)
|
|
448
|
+
if remaining is None:
|
|
449
|
+
remaining = getattr(order, "size", 0.0)
|
|
450
|
+
if remaining is None:
|
|
451
|
+
remaining = 0.0
|
|
452
|
+
return abs(float(remaining))
|
|
453
|
+
|
|
454
|
+
@staticmethod
|
|
455
|
+
def _aggregate_exchange_fills(fills):
|
|
456
|
+
total_size = 0.0
|
|
457
|
+
total_value = 0.0
|
|
458
|
+
for price, size, _role in fills:
|
|
459
|
+
total_value += price * size
|
|
460
|
+
total_size += size
|
|
461
|
+
if total_size <= 0.0:
|
|
462
|
+
return (0.0, 0.0)
|
|
463
|
+
return (total_value / total_size, total_size)
|
|
464
|
+
|
|
465
|
+
def _match_tick_order(self, order, tick_event):
|
|
466
|
+
exectype = self._effective_exectype(order, tick_event.price)
|
|
467
|
+
size = self._remaining_size(order)
|
|
468
|
+
price = tick_event.price
|
|
469
|
+
|
|
470
|
+
if exectype == Order.Market:
|
|
471
|
+
return (price, size)
|
|
472
|
+
|
|
473
|
+
if exectype == Order.Limit:
|
|
474
|
+
limit_price = (
|
|
475
|
+
order.pricelimit
|
|
476
|
+
if getattr(order, "_stop_triggered", False) and order.exectype == Order.StopLimit
|
|
477
|
+
else order.price
|
|
478
|
+
)
|
|
479
|
+
if order.isbuy() and price <= limit_price:
|
|
480
|
+
return (min(price, limit_price), size)
|
|
481
|
+
if not order.isbuy() and price >= limit_price:
|
|
482
|
+
return (max(price, limit_price), size)
|
|
483
|
+
return None
|
|
484
|
+
|
|
485
|
+
def _match_orderbook_order(self, order, ob_event):
|
|
486
|
+
exectype = self._effective_exectype(order, self._trigger_reference_price(order, ob_event))
|
|
487
|
+
size = self._remaining_size(order)
|
|
488
|
+
|
|
489
|
+
if exectype == Order.Market:
|
|
490
|
+
if order.isbuy():
|
|
491
|
+
return self._match_buy_depth(ob_event.asks, size, None)
|
|
492
|
+
return self._match_sell_depth(ob_event.bids, size, None)
|
|
493
|
+
|
|
494
|
+
if exectype == Order.Limit:
|
|
495
|
+
limit_price = (
|
|
496
|
+
order.pricelimit
|
|
497
|
+
if getattr(order, "_stop_triggered", False) and order.exectype == Order.StopLimit
|
|
498
|
+
else order.price
|
|
499
|
+
)
|
|
500
|
+
if order.isbuy() and ob_event.asks and ob_event.asks[0][0] <= limit_price:
|
|
501
|
+
return self._match_buy_depth(ob_event.asks, size, limit_price)
|
|
502
|
+
if (not order.isbuy()) and ob_event.bids and ob_event.bids[0][0] >= limit_price:
|
|
503
|
+
return self._match_sell_depth(ob_event.bids, size, limit_price)
|
|
504
|
+
return None
|
|
505
|
+
|
|
506
|
+
def _effective_exectype(self, order, reference_price):
|
|
507
|
+
exectype = order.exectype
|
|
508
|
+
if exectype == Order.Stop:
|
|
509
|
+
if self._check_stop_trigger(order, reference_price):
|
|
510
|
+
return Order.Market
|
|
511
|
+
return None
|
|
512
|
+
if exectype == Order.StopLimit:
|
|
513
|
+
if self._check_stop_trigger(order, reference_price):
|
|
514
|
+
return Order.Limit
|
|
515
|
+
return None
|
|
516
|
+
return exectype
|
|
517
|
+
|
|
518
|
+
def _check_stop_trigger(self, order, reference_price):
|
|
519
|
+
if getattr(order, "_stop_triggered", False):
|
|
520
|
+
return True
|
|
521
|
+
stop_price = getattr(order, "price", None)
|
|
522
|
+
if stop_price is None or reference_price is None:
|
|
523
|
+
return False
|
|
524
|
+
if order.isbuy() and reference_price >= stop_price:
|
|
525
|
+
order._stop_triggered = True
|
|
526
|
+
return True
|
|
527
|
+
if (not order.isbuy()) and reference_price <= stop_price:
|
|
528
|
+
order._stop_triggered = True
|
|
529
|
+
return True
|
|
530
|
+
return False
|
|
531
|
+
|
|
532
|
+
@staticmethod
|
|
533
|
+
def _trigger_reference_price(order, ob_event):
|
|
534
|
+
if order.isbuy():
|
|
535
|
+
return ob_event.asks[0][0] if ob_event.asks else None
|
|
536
|
+
return ob_event.bids[0][0] if ob_event.bids else None
|
|
537
|
+
|
|
538
|
+
@staticmethod
|
|
539
|
+
def _match_buy_depth(asks, target_size, limit_price):
|
|
540
|
+
total_filled = 0.0
|
|
541
|
+
total_cost = 0.0
|
|
542
|
+
for price, qty in asks:
|
|
543
|
+
if limit_price is not None and price > limit_price:
|
|
544
|
+
break
|
|
545
|
+
fill = min(target_size - total_filled, qty)
|
|
546
|
+
if fill <= 0:
|
|
547
|
+
continue
|
|
548
|
+
total_cost += price * fill
|
|
549
|
+
total_filled += fill
|
|
550
|
+
if total_filled >= target_size:
|
|
551
|
+
break
|
|
552
|
+
if total_filled <= 0:
|
|
553
|
+
return None
|
|
554
|
+
return (total_cost / total_filled, total_filled)
|
|
555
|
+
|
|
556
|
+
@staticmethod
|
|
557
|
+
def _match_sell_depth(bids, target_size, limit_price):
|
|
558
|
+
total_filled = 0.0
|
|
559
|
+
total_value = 0.0
|
|
560
|
+
for price, qty in bids:
|
|
561
|
+
if limit_price is not None and price < limit_price:
|
|
562
|
+
break
|
|
563
|
+
fill = min(target_size - total_filled, qty)
|
|
564
|
+
if fill <= 0:
|
|
565
|
+
continue
|
|
566
|
+
total_value += price * fill
|
|
567
|
+
total_filled += fill
|
|
568
|
+
if total_filled >= target_size:
|
|
569
|
+
break
|
|
570
|
+
if total_filled <= 0:
|
|
571
|
+
return None
|
|
572
|
+
return (total_value / total_filled, total_filled)
|