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.
Files changed (465) hide show
  1. back_trader_python-1.4.0.dist-info/METADATA +1491 -0
  2. back_trader_python-1.4.0.dist-info/RECORD +465 -0
  3. back_trader_python-1.4.0.dist-info/WHEEL +5 -0
  4. back_trader_python-1.4.0.dist-info/licenses/LICENSE +674 -0
  5. back_trader_python-1.4.0.dist-info/top_level.txt +1 -0
  6. backtrader/__init__.py +148 -0
  7. backtrader/_cerebro/__init__.py +5 -0
  8. backtrader/_cerebro/channel.py +382 -0
  9. backtrader/_cerebro/execution.py +377 -0
  10. backtrader/_cerebro/lifecycle.py +143 -0
  11. backtrader/_cerebro/notifications.py +150 -0
  12. backtrader/_cerebro/presentation.py +230 -0
  13. backtrader/_cerebro/registry.py +593 -0
  14. backtrader/_cerebro/runnext.py +551 -0
  15. backtrader/_cerebro/runonce.py +142 -0
  16. backtrader/analyzer.py +594 -0
  17. backtrader/analyzers/__init__.py +50 -0
  18. backtrader/analyzers/annualreturn.py +226 -0
  19. backtrader/analyzers/calmar.py +165 -0
  20. backtrader/analyzers/drawdown.py +287 -0
  21. backtrader/analyzers/leverage.py +112 -0
  22. backtrader/analyzers/logreturnsrolling.py +190 -0
  23. backtrader/analyzers/periodstats.py +153 -0
  24. backtrader/analyzers/positions.py +119 -0
  25. backtrader/analyzers/pyfolio.py +470 -0
  26. backtrader/analyzers/returns.py +192 -0
  27. backtrader/analyzers/sharpe.py +307 -0
  28. backtrader/analyzers/sharpe_ratio_stats.py +534 -0
  29. backtrader/analyzers/sqn.py +112 -0
  30. backtrader/analyzers/timereturn.py +192 -0
  31. backtrader/analyzers/total_value.py +75 -0
  32. backtrader/analyzers/tradeanalyzer.py +278 -0
  33. backtrader/analyzers/transactions.py +141 -0
  34. backtrader/analyzers/vwr.py +245 -0
  35. backtrader/bokeh/__init__.py +155 -0
  36. backtrader/bokeh/analyzers/__init__.py +13 -0
  37. backtrader/bokeh/analyzers/plot.py +192 -0
  38. backtrader/bokeh/analyzers/recorder.py +181 -0
  39. backtrader/bokeh/app.py +1094 -0
  40. backtrader/bokeh/live/__init__.py +11 -0
  41. backtrader/bokeh/live/client.py +352 -0
  42. backtrader/bokeh/live/datahandler.py +346 -0
  43. backtrader/bokeh/plot_adapter.py +200 -0
  44. backtrader/bokeh/schemes/__init__.py +14 -0
  45. backtrader/bokeh/schemes/blackly.py +76 -0
  46. backtrader/bokeh/schemes/scheme.py +150 -0
  47. backtrader/bokeh/schemes/tradimo.py +82 -0
  48. backtrader/bokeh/tab.py +125 -0
  49. backtrader/bokeh/tabs/__init__.py +30 -0
  50. backtrader/bokeh/tabs/analyzer.py +120 -0
  51. backtrader/bokeh/tabs/config.py +154 -0
  52. backtrader/bokeh/tabs/live.py +109 -0
  53. backtrader/bokeh/tabs/log.py +185 -0
  54. backtrader/bokeh/tabs/metadata.py +182 -0
  55. backtrader/bokeh/tabs/performance.py +359 -0
  56. backtrader/bokeh/tabs/source.py +70 -0
  57. backtrader/bokeh/utils/__init__.py +8 -0
  58. backtrader/bokeh/utils/helpers.py +167 -0
  59. backtrader/bokeh/webapp.py +164 -0
  60. backtrader/broker.py +478 -0
  61. backtrader/brokers/__init__.py +36 -0
  62. backtrader/brokers/bbroker.py +2576 -0
  63. backtrader/brokers/btapibroker.py +8227 -0
  64. backtrader/brokers/hft/__init__.py +89 -0
  65. backtrader/brokers/hft/binance_bbo.py +625 -0
  66. backtrader/brokers/hft/binance_bbo_compare.py +1398 -0
  67. backtrader/brokers/hft/examples.py +1228 -0
  68. backtrader/brokers/hft/exchange.py +380 -0
  69. backtrader/brokers/hft/latency.py +309 -0
  70. backtrader/brokers/hft/matching_core.py +572 -0
  71. backtrader/brokers/hft/queue.py +238 -0
  72. backtrader/brokers/hft/recorder.py +88 -0
  73. backtrader/brokers/hft/state.py +138 -0
  74. backtrader/brokers/impact_models.py +118 -0
  75. backtrader/brokers/mixbroker.py +895 -0
  76. backtrader/brokers/tickbroker.py +1991 -0
  77. backtrader/btrun/__init__.py +12 -0
  78. backtrader/btrun/btrun.py +1218 -0
  79. backtrader/cerebro.py +828 -0
  80. backtrader/channel.py +682 -0
  81. backtrader/channels/__init__.py +23 -0
  82. backtrader/channels/bridge.py +186 -0
  83. backtrader/channels/funding.py +248 -0
  84. backtrader/channels/live_queue.py +216 -0
  85. backtrader/channels/live_validator.py +294 -0
  86. backtrader/channels/orderbook.py +257 -0
  87. backtrader/channels/tick.py +202 -0
  88. backtrader/comminfo.py +665 -0
  89. backtrader/commissions/__init__.py +106 -0
  90. backtrader/commissions/ctpoption.py +993 -0
  91. backtrader/configs/account_config_example.yaml +8 -0
  92. backtrader/dataseries.py +379 -0
  93. backtrader/errors.py +106 -0
  94. backtrader/events.py +980 -0
  95. backtrader/feed.py +1523 -0
  96. backtrader/feeds/__init__.py +75 -0
  97. backtrader/feeds/barrier.py +2006 -0
  98. backtrader/feeds/blaze.py +118 -0
  99. backtrader/feeds/btapifeed.py +1538 -0
  100. backtrader/feeds/btcsv.py +203 -0
  101. backtrader/feeds/chainer.py +114 -0
  102. backtrader/feeds/cryptohftdata.py +164 -0
  103. backtrader/feeds/csvgeneric.py +1205 -0
  104. backtrader/feeds/ctpcohort.py +1051 -0
  105. backtrader/feeds/influxfeed.py +158 -0
  106. backtrader/feeds/livefeed.py +71 -0
  107. backtrader/feeds/mixed_channel.py +108 -0
  108. backtrader/feeds/mt4csv.py +42 -0
  109. backtrader/feeds/pandafeed.py +381 -0
  110. backtrader/feeds/quandl.py +256 -0
  111. backtrader/feeds/rollover.py +229 -0
  112. backtrader/feeds/sierrachart.py +30 -0
  113. backtrader/feeds/vchart.py +162 -0
  114. backtrader/feeds/vchartcsv.py +84 -0
  115. backtrader/feeds/vchartfile.py +153 -0
  116. backtrader/feeds/yahoo.py +399 -0
  117. backtrader/fillers.py +148 -0
  118. backtrader/filters/__init__.py +34 -0
  119. backtrader/filters/bsplitter.py +127 -0
  120. backtrader/filters/calendardays.py +121 -0
  121. backtrader/filters/datafiller.py +192 -0
  122. backtrader/filters/datafilter.py +74 -0
  123. backtrader/filters/daysteps.py +96 -0
  124. backtrader/filters/heikinashi.py +63 -0
  125. backtrader/filters/renko.py +164 -0
  126. backtrader/filters/session.py +289 -0
  127. backtrader/flt.py +80 -0
  128. backtrader/functions.py +960 -0
  129. backtrader/indicator.py +449 -0
  130. backtrader/indicators/__init__.py +148 -0
  131. backtrader/indicators/accdecoscillator.py +110 -0
  132. backtrader/indicators/aroon.py +300 -0
  133. backtrader/indicators/atr.py +315 -0
  134. backtrader/indicators/awesomeoscillator.py +122 -0
  135. backtrader/indicators/basicops.py +834 -0
  136. backtrader/indicators/bollinger.py +223 -0
  137. backtrader/indicators/cci.py +89 -0
  138. backtrader/indicators/channels_ext.py +83 -0
  139. backtrader/indicators/contrib/__init__.py +228 -0
  140. backtrader/indicators/contrib/absolutely_no_lag_lwma.py +28 -0
  141. backtrader/indicators/contrib/absolutely_no_lag_lwma_color.py +44 -0
  142. backtrader/indicators/contrib/accumulation_distribution_line.py +92 -0
  143. backtrader/indicators/contrib/adx_cross_hull_style_indicator.py +249 -0
  144. backtrader/indicators/contrib/adxdmi.py +34 -0
  145. backtrader/indicators/contrib/ai_acceleration_deceleration_oscillator.py +34 -0
  146. backtrader/indicators/contrib/altr_trend_signal_v22.py +85 -0
  147. backtrader/indicators/contrib/anchored_momentum_line.py +115 -0
  148. backtrader/indicators/contrib/any_range_cld_tail_indicator.py +82 -0
  149. backtrader/indicators/contrib/aroon_horn_sign_indicator.py +96 -0
  150. backtrader/indicators/contrib/aroon_oscillator_sign_alert.py +50 -0
  151. backtrader/indicators/contrib/arrows_curves_indicator.py +112 -0
  152. backtrader/indicators/contrib/as_ctrend_indicator.py +143 -0
  153. backtrader/indicators/contrib/asimmetric_stoch_nr_indicator.py +187 -0
  154. backtrader/indicators/contrib/atr_normalize_histogram.py +118 -0
  155. backtrader/indicators/contrib/average_change_candle.py +165 -0
  156. backtrader/indicators/contrib/bb_squeeze_indicator.py +60 -0
  157. backtrader/indicators/contrib/bezier_st_dev_indicator.py +135 -0
  158. backtrader/indicators/contrib/binary_wave_indicator.py +233 -0
  159. backtrader/indicators/contrib/blau_c_momentum_indicator.py +123 -0
  160. backtrader/indicators/contrib/blau_cmi_indicator.py +141 -0
  161. backtrader/indicators/contrib/blau_csi.py +76 -0
  162. backtrader/indicators/contrib/blau_ergodic.py +53 -0
  163. backtrader/indicators/contrib/blau_t_stoch_i.py +72 -0
  164. backtrader/indicators/contrib/blau_ts_stochastic.py +85 -0
  165. backtrader/indicators/contrib/blau_tvi.py +55 -0
  166. backtrader/indicators/contrib/brain_trend2_indicator.py +128 -0
  167. backtrader/indicators/contrib/brain_trend_signal_proxy.py +47 -0
  168. backtrader/indicators/contrib/brake_parb_indicator.py +85 -0
  169. backtrader/indicators/contrib/breakout_bars_trend_v2.py +121 -0
  170. backtrader/indicators/contrib/bsi_indicator.py +87 -0
  171. backtrader/indicators/contrib/bulls_bears_eyes.py +67 -0
  172. backtrader/indicators/contrib/bulls_power.py +56 -0
  173. backtrader/indicators/contrib/bw_wise_man1_signal.py +102 -0
  174. backtrader/indicators/contrib/bykov_trend_indicator.py +85 -0
  175. backtrader/indicators/contrib/candle_stop_color.py +46 -0
  176. backtrader/indicators/contrib/candles_x_smoothed_indicator.py +69 -0
  177. backtrader/indicators/contrib/candlesticks_bw.py +45 -0
  178. backtrader/indicators/contrib/caudate_x_period_candle_color.py +56 -0
  179. backtrader/indicators/contrib/cci_histogram_indicator.py +53 -0
  180. backtrader/indicators/contrib/cci_woodies_indicator.py +80 -0
  181. backtrader/indicators/contrib/center_of_gravity_candle_indicator.py +83 -0
  182. backtrader/indicators/contrib/center_of_gravity_indicator.py +70 -0
  183. backtrader/indicators/contrib/cg_oscillator.py +40 -0
  184. backtrader/indicators/contrib/close_line_cci.py +38 -0
  185. backtrader/indicators/contrib/close_price_fractals.py +47 -0
  186. backtrader/indicators/contrib/color3rd_gen_xma_indicator.py +122 -0
  187. backtrader/indicators/contrib/color_bb_candles_indicator.py +108 -0
  188. backtrader/indicators/contrib/color_coppock_indicator.py +157 -0
  189. backtrader/indicators/contrib/color_hma.py +71 -0
  190. backtrader/indicators/contrib/color_j_variation_indicator.py +53 -0
  191. backtrader/indicators/contrib/color_metro_de_marker_indicator.py +78 -0
  192. backtrader/indicators/contrib/color_metro_stochastic_indicator.py +93 -0
  193. backtrader/indicators/contrib/color_metro_wpr_indicator.py +85 -0
  194. backtrader/indicators/contrib/color_schaff_de_marker_trend_cycle.py +92 -0
  195. backtrader/indicators/contrib/color_schaff_trend_cycle_indicator.py +203 -0
  196. backtrader/indicators/contrib/color_step_xccx_indicator.py +193 -0
  197. backtrader/indicators/contrib/color_x2_ma.py +49 -0
  198. backtrader/indicators/contrib/color_x_derivative.py +63 -0
  199. backtrader/indicators/contrib/color_zerolag_de_marker.py +84 -0
  200. backtrader/indicators/contrib/corrected_average_indicator.py +127 -0
  201. backtrader/indicators/contrib/darvas_boxes_system.py +73 -0
  202. backtrader/indicators/contrib/dema_range_channel_color.py +42 -0
  203. backtrader/indicators/contrib/derivative_indicator.py +95 -0
  204. backtrader/indicators/contrib/digital_ft01_indicator.py +112 -0
  205. backtrader/indicators/contrib/digital_macd.py +200 -0
  206. backtrader/indicators/contrib/donchian_channels_system.py +45 -0
  207. backtrader/indicators/contrib/dots_indicator.py +93 -0
  208. backtrader/indicators/contrib/ef_distance_indicator.py +82 -0
  209. backtrader/indicators/contrib/ema_rsi_va.py +80 -0
  210. backtrader/indicators/contrib/envelopes_jp_alonso.py +32 -0
  211. backtrader/indicators/contrib/f2a_ao_indicator.py +120 -0
  212. backtrader/indicators/contrib/fatl_filter.py +179 -0
  213. backtrader/indicators/contrib/fibo_candles_indicator.py +78 -0
  214. backtrader/indicators/contrib/fine_tuning_ma.py +100 -0
  215. backtrader/indicators/contrib/fisher_org_v1.py +102 -0
  216. backtrader/indicators/contrib/fisher_org_v1_sign.py +118 -0
  217. backtrader/indicators/contrib/force_index_ema.py +96 -0
  218. backtrader/indicators/contrib/force_index_ema_2.py +27 -0
  219. backtrader/indicators/contrib/forecast_oscilator.py +145 -0
  220. backtrader/indicators/contrib/fractal_amambk.py +81 -0
  221. backtrader/indicators/contrib/frama_series.py +84 -0
  222. backtrader/indicators/contrib/frasm_av2_indicator.py +104 -0
  223. backtrader/indicators/contrib/go_indicator.py +93 -0
  224. backtrader/indicators/contrib/hlr_indicator.py +95 -0
  225. backtrader/indicators/contrib/hma.py +50 -0
  226. backtrader/indicators/contrib/i4_drfv2.py +34 -0
  227. backtrader/indicators/contrib/i4_drfv3.py +38 -0
  228. backtrader/indicators/contrib/i_anch_mom_indicator.py +72 -0
  229. backtrader/indicators/contrib/i_de_marker_sign_indicator.py +64 -0
  230. backtrader/indicators/contrib/i_gap_indicator.py +45 -0
  231. backtrader/indicators/contrib/i_stoch_komposter_indicator.py +77 -0
  232. backtrader/indicators/contrib/i_trend_indicator.py +125 -0
  233. backtrader/indicators/contrib/iamma_indicator.py +39 -0
  234. backtrader/indicators/contrib/indexed_moving_average.py +33 -0
  235. backtrader/indicators/contrib/instantaneous_trend_filter_indicator.py +51 -0
  236. backtrader/indicators/contrib/inverse_reaction_indicator.py +41 -0
  237. backtrader/indicators/contrib/irsi_sign_indicator.py +95 -0
  238. backtrader/indicators/contrib/iwpr_sign_indicator.py +59 -0
  239. backtrader/indicators/contrib/j_brain_trend1_sig_indicator.py +233 -0
  240. backtrader/indicators/contrib/j_tpo_proxy.py +32 -0
  241. backtrader/indicators/contrib/jma_slope_indicator.py +73 -0
  242. backtrader/indicators/contrib/kalman_filter_indicator.py +119 -0
  243. backtrader/indicators/contrib/kalman_filter_line.py +127 -0
  244. backtrader/indicators/contrib/kama_indicator.py +150 -0
  245. backtrader/indicators/contrib/karacatica_indicator.py +99 -0
  246. backtrader/indicators/contrib/kdj_indicator.py +59 -0
  247. backtrader/indicators/contrib/kwan_ccc_indicator.py +195 -0
  248. backtrader/indicators/contrib/kwan_nrp_indicator.py +113 -0
  249. backtrader/indicators/contrib/kwan_rdp_indicator.py +192 -0
  250. backtrader/indicators/contrib/laguerre_adx_indicator.py +85 -0
  251. backtrader/indicators/contrib/laguerre_filter_indicator.py +66 -0
  252. backtrader/indicators/contrib/laguerre_plus_di_proxy.py +57 -0
  253. backtrader/indicators/contrib/laguerre_roc_indicator.py +81 -0
  254. backtrader/indicators/contrib/le_man_signal_indicator.py +63 -0
  255. backtrader/indicators/contrib/linear_reg_slope_v2_indicator.py +136 -0
  256. backtrader/indicators/contrib/loco_indicator.py +88 -0
  257. backtrader/indicators/contrib/lrma_indicator.py +185 -0
  258. backtrader/indicators/contrib/lsma_angle_indicator.py +106 -0
  259. backtrader/indicators/contrib/ma_rounding_channel_indicator.py +149 -0
  260. backtrader/indicators/contrib/macd2_indicator.py +61 -0
  261. backtrader/indicators/contrib/macd_candle_indicator.py +80 -0
  262. backtrader/indicators/contrib/malr_indicator.py +77 -0
  263. backtrader/indicators/contrib/momentum_candle_sign_indicator.py +51 -0
  264. backtrader/indicators/contrib/moving_average_fn_indicator.py +139 -0
  265. backtrader/indicators/contrib/mt5_stochastic_close_close.py +57 -0
  266. backtrader/indicators/contrib/muv_nor_diff_cloud_indicator.py +107 -0
  267. backtrader/indicators/contrib/non_lag_dot_indicator.py +124 -0
  268. backtrader/indicators/contrib/nrtr_extr_indicator.py +95 -0
  269. backtrader/indicators/contrib/nrtr_indicator.py +95 -0
  270. backtrader/indicators/contrib/p_channel_system.py +40 -0
  271. backtrader/indicators/contrib/percent_envelope.py +37 -0
  272. backtrader/indicators/contrib/percentage_crossover_channel.py +47 -0
  273. backtrader/indicators/contrib/pivot_zig_zag_proxy.py +47 -0
  274. backtrader/indicators/contrib/price_channel_stop_indicator.py +104 -0
  275. backtrader/indicators/contrib/price_extreme_channel.py +35 -0
  276. backtrader/indicators/contrib/qqe_cloud_indicator.py +129 -0
  277. backtrader/indicators/contrib/ravi_indicator.py +40 -0
  278. backtrader/indicators/contrib/raw_close_close_stochastic.py +74 -0
  279. backtrader/indicators/contrib/rd_trend_trigger_indicator.py +51 -0
  280. backtrader/indicators/contrib/renko_level.py +85 -0
  281. backtrader/indicators/contrib/renko_line_break.py +91 -0
  282. backtrader/indicators/contrib/rftl_indicator.py +41 -0
  283. backtrader/indicators/contrib/rkd_indicator.py +53 -0
  284. backtrader/indicators/contrib/roc2_vg_indicator.py +68 -0
  285. backtrader/indicators/contrib/rsi_histogram_indicator.py +43 -0
  286. backtrader/indicators/contrib/rsi_slowdown.py +57 -0
  287. backtrader/indicators/contrib/rsioma_v2.py +41 -0
  288. backtrader/indicators/contrib/rvi_histogram_indicator.py +107 -0
  289. backtrader/indicators/contrib/safe_adx.py +89 -0
  290. backtrader/indicators/contrib/shared_strategy_indicators.py +1651 -0
  291. backtrader/indicators/contrib/sidus_indicator.py +105 -0
  292. backtrader/indicators/contrib/silver_trend_indicator.py +79 -0
  293. backtrader/indicators/contrib/sliding_range_color.py +56 -0
  294. backtrader/indicators/contrib/slow_stoch.py +42 -0
  295. backtrader/indicators/contrib/smoothed_adx_indicator.py +86 -0
  296. backtrader/indicators/contrib/smoothed_rsi.py +31 -0
  297. backtrader/indicators/contrib/spearman_rank_correlation_histogram.py +60 -0
  298. backtrader/indicators/contrib/stalin_indicator.py +152 -0
  299. backtrader/indicators/contrib/starter_laguerre_filter.py +62 -0
  300. backtrader/indicators/contrib/step_manrtr_indicator.py +137 -0
  301. backtrader/indicators/contrib/stochastic_histogram_indicator.py +143 -0
  302. backtrader/indicators/contrib/t3_alarm_indicator.py +125 -0
  303. backtrader/indicators/contrib/t3_average.py +76 -0
  304. backtrader/indicators/contrib/t3_indicator.py +40 -0
  305. backtrader/indicators/contrib/the20s_v020_signal.py +93 -0
  306. backtrader/indicators/contrib/three_candles_indicator.py +70 -0
  307. backtrader/indicators/contrib/three_line_break_indicator.py +64 -0
  308. backtrader/indicators/contrib/time_line.py +57 -0
  309. backtrader/indicators/contrib/trading_channel_index_proxy.py +48 -0
  310. backtrader/indicators/contrib/trend_arrows_indicator.py +109 -0
  311. backtrader/indicators/contrib/trend_continuation_indicator.py +127 -0
  312. backtrader/indicators/contrib/trend_intensity_index_proxy.py +51 -0
  313. backtrader/indicators/contrib/trend_manager_indicator.py +39 -0
  314. backtrader/indicators/contrib/tri_x_candle_indicator.py +51 -0
  315. backtrader/indicators/contrib/trigger_line.py +66 -0
  316. backtrader/indicators/contrib/triple_ema_rate.py +34 -0
  317. backtrader/indicators/contrib/trvi_indicator.py +194 -0
  318. backtrader/indicators/contrib/two_pb_ideal_xosma_indicator.py +127 -0
  319. backtrader/indicators/contrib/ultra_absolutely_no_lag_lwma_color.py +92 -0
  320. backtrader/indicators/contrib/ultra_wpr_indicator.py +173 -0
  321. backtrader/indicators/contrib/up_down_candle_strength.py +68 -0
  322. backtrader/indicators/contrib/vinin_i_trend_indicator.py +139 -0
  323. backtrader/indicators/contrib/volume_weighted_ma_indicator.py +78 -0
  324. backtrader/indicators/contrib/volume_weighted_ma_st_dev_indicator.py +111 -0
  325. backtrader/indicators/contrib/vwap_close_indicator.py +65 -0
  326. backtrader/indicators/contrib/vwma_candle.py +57 -0
  327. backtrader/indicators/contrib/vwma_digit_system.py +70 -0
  328. backtrader/indicators/contrib/wami.py +43 -0
  329. backtrader/indicators/contrib/wprsi_signal_indicator.py +105 -0
  330. backtrader/indicators/contrib/x_de_marker_histogram_vol_direct_indicator.py +145 -0
  331. backtrader/indicators/contrib/x_fisher_indicator.py +64 -0
  332. backtrader/indicators/contrib/xcci_histogram_vol_direct_indicator.py +56 -0
  333. backtrader/indicators/contrib/xcci_histogram_vol_indicator.py +85 -0
  334. backtrader/indicators/contrib/xma_ichimoku.py +163 -0
  335. backtrader/indicators/contrib/xma_ishimoku_channel_indicator.py +65 -0
  336. backtrader/indicators/contrib/xma_ishimoku_line.py +68 -0
  337. backtrader/indicators/contrib/xma_range_bands_indicator.py +107 -0
  338. backtrader/indicators/contrib/xmacd_indicator.py +70 -0
  339. backtrader/indicators/contrib/xrsi_de_marker_histogram.py +67 -0
  340. backtrader/indicators/contrib/xrsi_histogram_vol_direct_indicator.py +52 -0
  341. backtrader/indicators/contrib/xrsi_histogram_vol_indicator.py +81 -0
  342. backtrader/indicators/contrib/xrvi_indicator.py +130 -0
  343. backtrader/indicators/contrib/zero_lag_macd.py +36 -0
  344. backtrader/indicators/contrib/zig_zag_recent_pivot_signal.py +90 -0
  345. backtrader/indicators/contrib/zpf_indicator.py +115 -0
  346. backtrader/indicators/crossover.py +337 -0
  347. backtrader/indicators/dema.py +175 -0
  348. backtrader/indicators/demarker.py +270 -0
  349. backtrader/indicators/deviation.py +284 -0
  350. backtrader/indicators/directionalmove.py +1071 -0
  351. backtrader/indicators/dma.py +112 -0
  352. backtrader/indicators/dpo.py +96 -0
  353. backtrader/indicators/dv2.py +56 -0
  354. backtrader/indicators/ema.py +145 -0
  355. backtrader/indicators/envelope.py +475 -0
  356. backtrader/indicators/hadelta.py +198 -0
  357. backtrader/indicators/heikinashi.py +153 -0
  358. backtrader/indicators/hma.py +153 -0
  359. backtrader/indicators/hurst.py +151 -0
  360. backtrader/indicators/ichimoku.py +267 -0
  361. backtrader/indicators/kama.py +181 -0
  362. backtrader/indicators/kst.py +159 -0
  363. backtrader/indicators/lrsi.py +125 -0
  364. backtrader/indicators/mabase.py +147 -0
  365. backtrader/indicators/macd.py +322 -0
  366. backtrader/indicators/momentum.py +267 -0
  367. backtrader/indicators/moneyflow.py +237 -0
  368. backtrader/indicators/mt5atr.py +124 -0
  369. backtrader/indicators/myind.py +179 -0
  370. backtrader/indicators/obv.py +94 -0
  371. backtrader/indicators/ols.py +265 -0
  372. backtrader/indicators/oscillator.py +161 -0
  373. backtrader/indicators/percentchange.py +83 -0
  374. backtrader/indicators/percentrank.py +46 -0
  375. backtrader/indicators/pivotpoint.py +469 -0
  376. backtrader/indicators/prettygoodoscillator.py +113 -0
  377. backtrader/indicators/priceops_ext.py +123 -0
  378. backtrader/indicators/priceoscillator.py +262 -0
  379. backtrader/indicators/psar.py +212 -0
  380. backtrader/indicators/rmi.py +69 -0
  381. backtrader/indicators/rsi.py +440 -0
  382. backtrader/indicators/sma.py +141 -0
  383. backtrader/indicators/smma.py +116 -0
  384. backtrader/indicators/spread.py +54 -0
  385. backtrader/indicators/stochastic.py +263 -0
  386. backtrader/indicators/supertrend.py +436 -0
  387. backtrader/indicators/trend_ext.py +105 -0
  388. backtrader/indicators/trix.py +202 -0
  389. backtrader/indicators/tsi.py +155 -0
  390. backtrader/indicators/ultimateoscillator.py +158 -0
  391. backtrader/indicators/vortex.py +62 -0
  392. backtrader/indicators/williams.py +194 -0
  393. backtrader/indicators/wma.py +103 -0
  394. backtrader/indicators/zlema.py +135 -0
  395. backtrader/indicators/zlind.py +104 -0
  396. backtrader/linebuffer.py +3155 -0
  397. backtrader/lineiterator.py +2911 -0
  398. backtrader/lineroot.py +1106 -0
  399. backtrader/lineseries.py +2559 -0
  400. backtrader/live_trading/__init__.py +31 -0
  401. backtrader/live_trading/interface.py +404 -0
  402. backtrader/mathsupport.py +94 -0
  403. backtrader/metabase.py +1804 -0
  404. backtrader/mixins/__init__.py +21 -0
  405. backtrader/mixins/singleton.py +118 -0
  406. backtrader/observer.py +106 -0
  407. backtrader/observers/__init__.py +45 -0
  408. backtrader/observers/benchmark.py +126 -0
  409. backtrader/observers/broker.py +184 -0
  410. backtrader/observers/buysell.py +144 -0
  411. backtrader/observers/drawdown.py +161 -0
  412. backtrader/observers/logreturns.py +113 -0
  413. backtrader/observers/timereturn.py +86 -0
  414. backtrader/observers/trade_logger.py +2972 -0
  415. backtrader/observers/tradelogger.py +6 -0
  416. backtrader/observers/trades.py +258 -0
  417. backtrader/order.py +1114 -0
  418. backtrader/parameters.py +2345 -0
  419. backtrader/plot/__init__.py +54 -0
  420. backtrader/plot/finance.py +1022 -0
  421. backtrader/plot/formatters.py +200 -0
  422. backtrader/plot/locator.py +353 -0
  423. backtrader/plot/multicursor.py +495 -0
  424. backtrader/plot/plot.py +2500 -0
  425. backtrader/plot/plot_plotly.py +1351 -0
  426. backtrader/plot/scheme.py +253 -0
  427. backtrader/plot/utils.py +104 -0
  428. backtrader/position.py +290 -0
  429. backtrader/position_modes.py +132 -0
  430. backtrader/profiles.py +254 -0
  431. backtrader/reports/__init__.py +39 -0
  432. backtrader/reports/charts.py +371 -0
  433. backtrader/reports/performance.py +620 -0
  434. backtrader/reports/reporter.py +660 -0
  435. backtrader/resamplerfilter.py +1001 -0
  436. backtrader/signal.py +118 -0
  437. backtrader/signals/__init__.py +17 -0
  438. backtrader/sizer.py +114 -0
  439. backtrader/sizers/__init__.py +26 -0
  440. backtrader/sizers/fixedsize.py +161 -0
  441. backtrader/sizers/percents_sizer.py +119 -0
  442. backtrader/store.py +221 -0
  443. backtrader/stores/__init__.py +33 -0
  444. backtrader/stores/btapistore.py +15506 -0
  445. backtrader/stores/livestore.py +137 -0
  446. backtrader/stores/vchartfile.py +96 -0
  447. backtrader/strategy.py +3655 -0
  448. backtrader/talib.py +280 -0
  449. backtrader/test_helpers.py +96 -0
  450. backtrader/timer.py +358 -0
  451. backtrader/trade.py +442 -0
  452. backtrader/tradingcal.py +361 -0
  453. backtrader/utils/__init__.py +68 -0
  454. backtrader/utils/autodict.py +251 -0
  455. backtrader/utils/date.py +71 -0
  456. backtrader/utils/dateintern.py +509 -0
  457. backtrader/utils/flushfile.py +94 -0
  458. backtrader/utils/fractal.py +101 -0
  459. backtrader/utils/get_metrics.py +101 -0
  460. backtrader/utils/load_data.py +209 -0
  461. backtrader/utils/log_message.py +998 -0
  462. backtrader/utils/ordereddefaultdict.py +75 -0
  463. backtrader/utils/py3.py +296 -0
  464. backtrader/version.py +21 -0
  465. backtrader/writer.py +372 -0
@@ -0,0 +1,380 @@
1
+ """Exchange matching models for HFT simulation.
2
+
3
+ Defines :class:`ExchangeModel` and concrete variants that decide how an order
4
+ interacts with an order book / trade stream (maker vs taker fills, rejections),
5
+ plus :class:`FillRole` and :class:`OrderResult`. Used by the tick broker's
6
+ matching core.
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum
11
+
12
+ from backtrader.order import Order
13
+
14
+ from .queue import ProbQueueModel
15
+
16
+
17
+ class FillRole(Enum):
18
+ """Side of liquidity the order provided when it got filled.
19
+
20
+ Attributes:
21
+ MAKER: The order was resting on the book and provided liquidity.
22
+ TAKER: The order aggressed against the book and consumed liquidity.
23
+ """
24
+
25
+ MAKER = "maker"
26
+ TAKER = "taker"
27
+
28
+
29
+ @dataclass
30
+ class OrderResult:
31
+ """Outcome of an exchange model's processing of an order.
32
+
33
+ Attributes:
34
+ action: High-level outcome keyword. One of ``"FILL"`` (the order was
35
+ fully or partially filled), ``"PENDING"`` (the order is resting on
36
+ the book and may fill later) or ``"REJECT"`` (the order was
37
+ rejected, see ``reject_reason`` for the reason code).
38
+ fills: List of fill tuples produced for this order. Each tuple's
39
+ shape is model-specific; for example,
40
+ :class:`SimpleExchangeModel` uses ``(price, quantity, role)``
41
+ while :class:`QueueExchangeModel` prepends the originating
42
+ ``order`` object.
43
+ reject_reason: Short code explaining the rejection, populated when
44
+ ``action == "REJECT"``. Empty otherwise.
45
+ """
46
+
47
+ action: str
48
+ fills: list = field(default_factory=list)
49
+ reject_reason: str = ""
50
+
51
+
52
+ class ExchangeModel:
53
+ """Abstract interface for exchange matching behavior.
54
+
55
+ Subclasses describe how an incoming order interacts with the current
56
+ order book and trade stream, and how subsequent market data updates
57
+ drive fills. All three hook methods receive a snapshot/pending-orders
58
+ view from the matching core.
59
+ """
60
+
61
+ def on_new_order(self, order, ob_snapshot):
62
+ """Handle a newly accepted order against ``ob_snapshot``.
63
+
64
+ Subclasses must implement this and return an :class:`OrderResult`
65
+ describing whether the order was filled, pended, or rejected.
66
+
67
+ Args:
68
+ order: The newly accepted order. Provides ``exectype``,
69
+ ``isbuy()``, ``price`` and ``size`` accessors.
70
+ ob_snapshot: Order book snapshot at the time the order was
71
+ accepted. Provides ``bids`` and ``asks``.
72
+
73
+ Returns:
74
+ OrderResult: Outcome of the matching attempt.
75
+ """
76
+ raise NotImplementedError
77
+
78
+ def on_trade(self, trade_event, pending_orders):
79
+ """Process a trade event for any resting pending orders.
80
+
81
+ Subclasses may consume the trade and emit maker-style fills when
82
+ one of the ``pending_orders`` was at the trade price.
83
+
84
+ Args:
85
+ trade_event: The trade event to consume. Provides ``price`` and
86
+ ``volume``.
87
+ pending_orders: Iterable of resting orders that may be filled by
88
+ this trade.
89
+
90
+ Returns:
91
+ list: Fill tuples produced by the subclass (possibly empty).
92
+ """
93
+ raise NotImplementedError
94
+
95
+ def on_depth_update(self, ob_event, pending_orders):
96
+ """Process a depth update to refresh queue-ahead estimates.
97
+
98
+ The default implementation is a no-op. Models that track per-order
99
+ queue position (e.g. :class:`QueueExchangeModel`) override this to
100
+ reconcile each resting order's queue-ahead against the new depth.
101
+
102
+ Args:
103
+ ob_event: Depth update event. May carry ``previous_bids`` /
104
+ ``previous_asks`` and ``bids`` / ``asks``.
105
+ pending_orders: Iterable of resting orders that may be affected
106
+ by the depth update.
107
+
108
+ Returns:
109
+ list: Fill tuples produced by the subclass (possibly empty).
110
+ """
111
+ _ = (ob_event, pending_orders)
112
+ return []
113
+
114
+
115
+ class SimpleExchangeModel(ExchangeModel):
116
+ """Exchange model that walks the book level-by-level with no queueing.
117
+
118
+ Market orders sweep liquidity against the opposite side of the book.
119
+ Limit orders either fill immediately if they cross the spread or sit
120
+ pending without any queue-position tracking. This model is the right
121
+ choice when queue dynamics are not needed (e.g. fast smoke tests).
122
+ """
123
+
124
+ def on_new_order(self, order, ob_snapshot):
125
+ """Match a new order against the current book with no queueing.
126
+
127
+ Market orders are filled against the opposite side; limit orders
128
+ that cross the spread are matched as takers. Limit orders that do
129
+ not cross the spread are returned as ``PENDING``.
130
+
131
+ Args:
132
+ order: The newly accepted order.
133
+ ob_snapshot: Order book snapshot at acceptance.
134
+
135
+ Returns:
136
+ OrderResult: ``"FILL"`` with taker fills when the order matches
137
+ against depth, otherwise ``"PENDING"``.
138
+ """
139
+ if order.exectype == Order.Market:
140
+ return self._match_against_depth(order, ob_snapshot, FillRole.TAKER)
141
+ if order.exectype == Order.Limit and self._crosses_spread(order, ob_snapshot):
142
+ return self._match_against_depth(order, ob_snapshot, FillRole.TAKER)
143
+ return OrderResult(action="PENDING")
144
+
145
+ def on_trade(self, trade_event, pending_orders):
146
+ """Ignore trade events (queueing is not simulated).
147
+
148
+ Args:
149
+ trade_event: The trade event (unused).
150
+ pending_orders: The resting orders (unused).
151
+
152
+ Returns:
153
+ list: Always empty for this model.
154
+ """
155
+ _ = (trade_event, pending_orders)
156
+ return []
157
+
158
+ def _crosses_spread(self, order, ob_snapshot):
159
+ """Return True when the order's limit price crosses the best quote.
160
+
161
+ A buy limit crosses when its price is greater than or equal to the
162
+ best ask; a sell limit crosses when its price is less than or equal
163
+ to the best bid.
164
+
165
+ Args:
166
+ order: The order being evaluated. Must expose ``isbuy()`` and
167
+ ``price``.
168
+ ob_snapshot: Order book snapshot providing ``bids`` and
169
+ ``asks``.
170
+
171
+ Returns:
172
+ bool: ``True`` if the limit order can be filled immediately.
173
+ """
174
+ if order.isbuy():
175
+ best_ask = ob_snapshot.asks[0][0] if ob_snapshot.asks else None
176
+ return best_ask is not None and order.price >= best_ask
177
+ best_bid = ob_snapshot.bids[0][0] if ob_snapshot.bids else None
178
+ return best_bid is not None and order.price <= best_bid
179
+
180
+ def _match_against_depth(self, order, ob_snapshot, role):
181
+ """Walk the book and fill the order as a taker up to its size.
182
+
183
+ Market orders consume every level until the size is met; limit
184
+ orders stop when the next level's price is on the wrong side of
185
+ ``order.price``.
186
+
187
+ Args:
188
+ order: The aggressive order to fill. Provides ``isbuy()``,
189
+ ``size`` and, for limits, ``price``.
190
+ ob_snapshot: Order book snapshot to consume.
191
+ role: The :class:`FillRole` to attach to each fill produced
192
+ (typically :attr:`FillRole.TAKER`).
193
+
194
+ Returns:
195
+ OrderResult: ``"FILL"`` with the per-level fills when at least
196
+ one level matched, otherwise ``"PENDING"``.
197
+ """
198
+ levels = ob_snapshot.asks if order.isbuy() else ob_snapshot.bids
199
+ remaining = getattr(getattr(order, "executed", None), "remsize", None)
200
+ if remaining is None:
201
+ remaining = getattr(order, "size", 0.0)
202
+ remaining = abs(remaining)
203
+ fills = []
204
+ for price, qty in levels:
205
+ if order.exectype == Order.Limit:
206
+ if order.isbuy() and price > order.price:
207
+ break
208
+ if not order.isbuy() and price < order.price:
209
+ break
210
+ fill_qty = min(remaining, qty)
211
+ if fill_qty <= 0:
212
+ continue
213
+ fills.append((price, fill_qty, role))
214
+ remaining -= fill_qty
215
+ if remaining <= 0:
216
+ break
217
+ if not fills:
218
+ return OrderResult(action="PENDING")
219
+ return OrderResult(action="FILL", fills=fills)
220
+
221
+
222
+ class QueueExchangeModel(SimpleExchangeModel):
223
+ """Exchange model that maintains per-order queue position for maker fills.
224
+
225
+ Limit orders that cross the spread follow :class:`SimpleExchangeModel`'s
226
+ taker semantics (with extra handling for ``GTX``/``FOK`` time-in-force
227
+ flags). Limit orders that do not cross the spread are parked as maker
228
+ orders and their queue-ahead is tracked via the injected queue model
229
+ as trades and depth updates arrive.
230
+ """
231
+
232
+ def __init__(
233
+ self,
234
+ queue_model=None,
235
+ queue_model_power: float = 2.0,
236
+ lot_size: float = 1.0,
237
+ tick_size: float = None,
238
+ ):
239
+ """Initialize the queue-aware exchange model.
240
+
241
+ Args:
242
+ queue_model: Optional pre-built queue model. When ``None``, a
243
+ :class:`backtrader.brokers.hft.queue.ProbQueueModel` is
244
+ constructed from ``queue_model_power`` and ``lot_size``.
245
+ queue_model_power: Power exponent forwarded to the default
246
+ :class:`ProbQueueModel` when ``queue_model`` is ``None``.
247
+ lot_size: Default lot size forwarded to the default
248
+ :class:`ProbQueueModel` when ``queue_model`` is ``None``.
249
+ tick_size: Optional price tick size. When provided, price
250
+ equality is computed after rounding to this granularity so
251
+ floating-point noise does not break maker/trade matching.
252
+ ``None`` disables tick-based matching.
253
+ """
254
+ self._queue_model = queue_model or ProbQueueModel(
255
+ power=queue_model_power, lot_size=lot_size
256
+ )
257
+ self._tick_size = float(tick_size) if tick_size is not None else None
258
+
259
+ def on_new_order(self, order, ob_snapshot):
260
+ """Park the order as a maker or fill it as a taker.
261
+
262
+ Market orders are filled against depth. Crossing limit orders
263
+ follow the taker path and honor ``GTX`` (reject when crossing)
264
+ and ``FOK`` (reject when the full size cannot be filled) flags.
265
+ Non-crossing limit orders are recorded by the queue model and
266
+ returned as ``PENDING`` with the ``_fill_role`` tag set to
267
+ :attr:`FillRole.MAKER`.
268
+
269
+ Args:
270
+ order: The newly accepted order.
271
+ ob_snapshot: Order book snapshot at acceptance.
272
+
273
+ Returns:
274
+ OrderResult: ``"FILL"`` for takers, ``"PENDING"`` for resting
275
+ makers, or ``"REJECT"`` for ``GTX``/``FOK`` violations.
276
+ """
277
+ if order.exectype == Order.Market:
278
+ return self._match_against_depth(order, ob_snapshot, FillRole.TAKER)
279
+
280
+ if order.exectype == Order.Limit:
281
+ if self._crosses_spread(order, ob_snapshot):
282
+ tif = getattr(order, "time_in_force", "GTC")
283
+ taker_result = self._match_against_depth(order, ob_snapshot, FillRole.TAKER)
284
+ filled_qty = sum(fill[1] for fill in taker_result.fills)
285
+ order_qty = abs(getattr(order, "size", 0.0))
286
+ if tif == "GTX":
287
+ return OrderResult(action="REJECT", reject_reason="GTX_CROSSED")
288
+ if tif == "FOK" and filled_qty < order_qty:
289
+ return OrderResult(action="REJECT", reject_reason="FOK_INSUFFICIENT")
290
+ return taker_result
291
+
292
+ self._queue_model.on_new_order(order, ob_snapshot)
293
+ order._fill_role = FillRole.MAKER
294
+ return OrderResult(action="PENDING")
295
+
296
+ return OrderResult(action="PENDING")
297
+
298
+ def on_trade(self, trade_event, pending_orders):
299
+ """Drive maker fills from incoming trade events.
300
+
301
+ For each resting maker order, check whether the trade price
302
+ matches the order's price (within ``_tick_size`` if configured) and,
303
+ if so, delegate to the queue model's ``update_on_trade``. Any
304
+ resulting fill is appended in the form
305
+ ``(order, price, quantity, role)``.
306
+
307
+ Args:
308
+ trade_event: The trade event to consume. Provides ``price`` and
309
+ ``volume``.
310
+ pending_orders: Resting orders that may be filled by this trade.
311
+
312
+ Returns:
313
+ list: ``(order, price, fillable_qty, FillRole.MAKER)`` tuples
314
+ for orders that filled, possibly empty.
315
+ """
316
+ fills = []
317
+ for order in pending_orders:
318
+ if getattr(order, "_fill_role", None) != FillRole.MAKER:
319
+ continue
320
+ trade_price = getattr(trade_event, "price", None)
321
+ order_price = getattr(order, "price", None)
322
+ if trade_price is None or order_price is None:
323
+ continue
324
+ if self._tick_size is not None and self._tick_size > 0:
325
+ if round(float(trade_price) / self._tick_size) != round(
326
+ float(order_price) / self._tick_size
327
+ ):
328
+ continue
329
+ elif float(trade_price) != float(order_price):
330
+ continue
331
+ fillable = self._queue_model.update_on_trade(order, trade_event)
332
+ if fillable > 0:
333
+ fills.append((order, trade_price, fillable, FillRole.MAKER))
334
+ return fills
335
+
336
+ def on_depth_update(self, ob_event, pending_orders):
337
+ """Refresh each maker order's queue-ahead from the new depth.
338
+
339
+ For every resting maker order, compute the previous and current
340
+ depth at the order's price (respecting ``_tick_size``) and forward
341
+ the pair to the queue model's ``update_on_depth``.
342
+
343
+ Args:
344
+ ob_event: Depth update event providing ``previous_bids`` /
345
+ ``previous_asks`` (or empty) and ``bids`` / ``asks``.
346
+ pending_orders: Resting orders to reconcile.
347
+
348
+ Returns:
349
+ list: Always empty in the current implementation; the method
350
+ is called for its side effect of updating queue-ahead state.
351
+ """
352
+ fills: list = []
353
+ prev_bids = getattr(ob_event, "previous_bids", None) or []
354
+ prev_asks = getattr(ob_event, "previous_asks", None) or []
355
+ curr_bids = getattr(ob_event, "bids", None) or []
356
+ curr_asks = getattr(ob_event, "asks", None) or []
357
+
358
+ def level_qty(levels, price):
359
+ for level_price, level_qty_value in levels:
360
+ if self._tick_size is not None and self._tick_size > 0:
361
+ if round(float(level_price) / self._tick_size) == round(
362
+ float(price) / self._tick_size
363
+ ):
364
+ return float(level_qty_value)
365
+ elif float(level_price) == float(price):
366
+ return float(level_qty_value)
367
+ return 0.0
368
+
369
+ for order in pending_orders:
370
+ if getattr(order, "_fill_role", None) != FillRole.MAKER:
371
+ continue
372
+ price = getattr(order, "price", None)
373
+ if price is None:
374
+ continue
375
+ prev_qty = level_qty(prev_bids if order.isbuy() else prev_asks, price)
376
+ new_qty = level_qty(curr_bids if order.isbuy() else curr_asks, price)
377
+ if abs(prev_qty - new_qty) <= 1e-12:
378
+ continue
379
+ self._queue_model.update_on_depth(order, prev_qty, new_qty)
380
+ return fills
@@ -0,0 +1,309 @@
1
+ """Latency models for HFT simulation.
2
+
3
+ Defines :class:`LatencyModel` and implementations (e.g.
4
+ :class:`ConstantLatencyModel`) that model feed, order-entry and order-response
5
+ delays so the tick matching engine can simulate realistic exchange round-trips.
6
+ """
7
+
8
+ import bisect
9
+ import heapq
10
+ import math
11
+
12
+
13
+ class LatencyModel:
14
+ """Abstract interface for exchange round-trip latency models.
15
+
16
+ A latency model returns, in seconds, how much delay a market-data feed,
17
+ an order entry or an order response should incur. The three methods are
18
+ kept separate so models can differentiate feed jitter from order-entry
19
+ latency from exchange ack latency.
20
+ """
21
+
22
+ def feed_latency(self, exch_ts, symbol):
23
+ """Return the feed latency (in seconds) for ``symbol`` at ``exch_ts``.
24
+
25
+ Args:
26
+ exch_ts: Exchange-side timestamp of the market-data event.
27
+ symbol: Symbol the event is for.
28
+
29
+ Returns:
30
+ float: Latency in seconds to be added to ``exch_ts`` to obtain
31
+ the local time at which the event is observed.
32
+ """
33
+ raise NotImplementedError
34
+
35
+ def order_entry_latency(self, local_ts, symbol):
36
+ """Return the order-entry latency (in seconds) for ``symbol``.
37
+
38
+ Args:
39
+ local_ts: Local time at which the order is submitted.
40
+ symbol: Symbol the order is for.
41
+
42
+ Returns:
43
+ float: Latency in seconds between local submission and the
44
+ order being visible to the exchange.
45
+ """
46
+ raise NotImplementedError
47
+
48
+ def order_response_latency(self, exch_ts, symbol):
49
+ """Return the order-response latency (in seconds) for ``symbol``.
50
+
51
+ Args:
52
+ exch_ts: Exchange-side timestamp of the acknowledgement.
53
+ symbol: Symbol the response is for.
54
+
55
+ Returns:
56
+ float: Latency in seconds between the exchange producing the
57
+ response and it being received locally.
58
+ """
59
+ raise NotImplementedError
60
+
61
+
62
+ class ConstantLatencyModel(LatencyModel):
63
+ """Constant-latency model: a single value per stage, regardless of time.
64
+
65
+ All three stages (feed, order entry, order response) use fixed
66
+ per-stage values supplied in milliseconds at construction time. This
67
+ is the simplest model and is typically used for deterministic tests.
68
+ """
69
+
70
+ def __init__(self, feed_latency_ms=0, order_entry_latency_ms=0, order_response_latency_ms=0):
71
+ """Initialize the constant-latency model.
72
+
73
+ Args:
74
+ feed_latency_ms: Feed latency in milliseconds (default ``0``).
75
+ order_entry_latency_ms: Order-entry latency in milliseconds
76
+ (default ``0``).
77
+ order_response_latency_ms: Order-response latency in
78
+ milliseconds (default ``0``).
79
+ """
80
+ self._feed_lat = feed_latency_ms / 1000.0
81
+ self._entry_lat = order_entry_latency_ms / 1000.0
82
+ self._resp_lat = order_response_latency_ms / 1000.0
83
+
84
+ def feed_latency(self, exch_ts, symbol):
85
+ """Return the configured constant feed latency in seconds."""
86
+ _ = (exch_ts, symbol)
87
+ return self._feed_lat
88
+
89
+ def order_entry_latency(self, local_ts, symbol):
90
+ """Return the configured constant order-entry latency in seconds."""
91
+ _ = (local_ts, symbol)
92
+ return self._entry_lat
93
+
94
+ def order_response_latency(self, exch_ts, symbol):
95
+ """Return the configured constant order-response latency in seconds."""
96
+ _ = (exch_ts, symbol)
97
+ return self._resp_lat
98
+
99
+
100
+ class IntpLatencyModel(LatencyModel):
101
+ """Time-varying latency model that linearly interpolates measured samples.
102
+
103
+ Accepts a sequence of ``(timestamp, feed, entry, resp)`` rows. The
104
+ rows are sorted by timestamp at construction time and lookups for an
105
+ arbitrary query time are performed via linear interpolation between
106
+ the two surrounding samples. The optional ``latency_offset`` is added
107
+ to the query timestamp before lookup, so the same data set can be
108
+ time-shifted without rebuilding the model.
109
+ """
110
+
111
+ def __init__(self, latency_data, latency_offset=0.0):
112
+ """Initialize the interpolated latency model.
113
+
114
+ Args:
115
+ latency_data: Iterable of ``(timestamp, feed_latency,
116
+ entry_latency, resp_latency)`` rows. The first column is
117
+ the exchange-side timestamp (in seconds), the other three
118
+ are the corresponding stage latencies in seconds.
119
+ latency_offset: Optional offset (in seconds) added to the
120
+ query timestamp before the binary search; useful for
121
+ time-shifting an existing profile.
122
+ """
123
+ self._offset = float(latency_offset)
124
+ rows = sorted(
125
+ (float(row[0]), float(row[1]), float(row[2]), float(row[3])) for row in latency_data
126
+ )
127
+ self._ts = [row[0] for row in rows]
128
+ self._feed = [row[1] for row in rows]
129
+ self._entry = [row[2] for row in rows]
130
+ self._resp = [row[3] for row in rows]
131
+
132
+ def _interp(self, ts, values):
133
+ """Linearly interpolate ``values`` at the time-shifted query time.
134
+
135
+ Returns the first sample for queries before the first row, the
136
+ last sample for queries past the last row, and a clamped linear
137
+ interpolation in between. Returns ``0.0`` when the model has no
138
+ samples at all.
139
+
140
+ Args:
141
+ ts: Query timestamp (will be offset by ``_offset``).
142
+ values: Per-sample list of latencies (in seconds) sorted in
143
+ the same order as ``_ts``.
144
+
145
+ Returns:
146
+ float: Interpolated latency in seconds.
147
+ """
148
+ if not self._ts:
149
+ return 0.0
150
+ lookup_ts = float(ts) + self._offset
151
+ idx = bisect.bisect_left(self._ts, lookup_ts)
152
+ if idx <= 0:
153
+ return values[0]
154
+ if idx >= len(self._ts):
155
+ return values[-1]
156
+ left_ts = self._ts[idx - 1]
157
+ right_ts = self._ts[idx]
158
+ left_val = values[idx - 1]
159
+ right_val = values[idx]
160
+ if right_ts == left_ts:
161
+ return right_val
162
+ ratio = (lookup_ts - left_ts) / (right_ts - left_ts)
163
+ return left_val + (right_val - left_val) * ratio
164
+
165
+ def feed_latency(self, exch_ts, symbol):
166
+ """Return the interpolated feed latency for ``exch_ts``."""
167
+ _ = symbol
168
+ return self._interp(exch_ts, self._feed)
169
+
170
+ def order_entry_latency(self, local_ts, symbol):
171
+ """Return the interpolated order-entry latency for ``local_ts``."""
172
+ _ = symbol
173
+ return self._interp(local_ts, self._entry)
174
+
175
+ def order_response_latency(self, exch_ts, symbol):
176
+ """Return the interpolated order-response latency for ``exch_ts``."""
177
+ _ = symbol
178
+ return self._interp(exch_ts, self._resp)
179
+
180
+
181
+ class LatencyEngine:
182
+ """Drives the per-stage latencies for orders and events.
183
+
184
+ Holds the priority queue of in-flight orders (sorted by the time at
185
+ which they should become visible to the exchange) and the set of
186
+ order ids that have been cancelled before they became visible. When
187
+ no latency model is supplied, the engine degenerates to a pass-through
188
+ that returns events and orders without applying any delay.
189
+ """
190
+
191
+ def __init__(self, latency_model=None):
192
+ """Initialize the latency engine.
193
+
194
+ Args:
195
+ latency_model: Optional :class:`LatencyModel` used to compute
196
+ per-stage delays. ``None`` disables all delays.
197
+ """
198
+ self._model = latency_model
199
+ self._pending_orders = []
200
+ self._cancelled_order_ids = set()
201
+ self._seq = 0
202
+
203
+ def delay_order(self, order, submit_ts, symbol):
204
+ """Park an order in the visibility queue based on entry latency.
205
+
206
+ Args:
207
+ order: The order that was just submitted locally.
208
+ submit_ts: Local timestamp of the submission.
209
+ symbol: Symbol the order is for.
210
+
211
+ Returns:
212
+ float | None: The exchange-visible timestamp the order will
213
+ become visible at, or ``None`` if latency is disabled
214
+ (``latency_model is None``) or the computed delay is
215
+ non-positive.
216
+ """
217
+ if self._model is None:
218
+ return None
219
+ visible_ts = float(submit_ts) + float(self._model.order_entry_latency(submit_ts, symbol))
220
+ if visible_ts <= float(submit_ts):
221
+ return None
222
+ heapq.heappush(self._pending_orders, (visible_ts, self._seq, order, symbol))
223
+ self._seq += 1
224
+ return visible_ts
225
+
226
+ def cancel_order(self, order):
227
+ """Mark an in-flight order as cancelled.
228
+
229
+ Cancelled orders are removed from the pending queue the next time
230
+ they would have become visible, so the matching engine never sees
231
+ them.
232
+
233
+ Args:
234
+ order: The order to cancel (identified by ``id(order)``).
235
+ """
236
+ self._cancelled_order_ids.add(id(order))
237
+
238
+ def get_visible_orders(self, current_ts):
239
+ """Pop and return all orders whose visibility time has been reached.
240
+
241
+ The order heap is sorted by ``(visible_ts, sequence)``, so equal
242
+ timestamps are popped in submission order. Any order whose id was
243
+ previously passed to :meth:`cancel_order` is discarded and its id
244
+ is removed from the cancelled set.
245
+
246
+ Args:
247
+ current_ts: Current local time used to determine which orders
248
+ have become visible.
249
+
250
+ Returns:
251
+ list[tuple]: ``(order, symbol)`` tuples for the orders that
252
+ should be released to the matching engine.
253
+ """
254
+ visible = []
255
+ now = float(current_ts)
256
+ while self._pending_orders and self._pending_orders[0][0] <= now:
257
+ _, _, order, symbol = heapq.heappop(self._pending_orders)
258
+ if id(order) in self._cancelled_order_ids:
259
+ self._cancelled_order_ids.discard(id(order))
260
+ continue
261
+ visible.append((order, symbol))
262
+ return visible
263
+
264
+ def apply_feed_latency(self, event):
265
+ """Set ``local_time`` on ``event`` after applying feed latency.
266
+
267
+ The event is mutated in place by setting a ``local_time`` attribute
268
+ equal to the exchange timestamp plus the model's feed latency. If no
269
+ model is configured, a valid receive timestamp already supplied by a
270
+ live feed is preserved; otherwise ``local_time`` falls back to the
271
+ event's exchange timestamp.
272
+
273
+ Args:
274
+ event: The market-data event to adjust. Must expose
275
+ ``timestamp`` and (optionally) ``symbol``.
276
+
277
+ Returns:
278
+ object: The same event object, for convenient chaining.
279
+ """
280
+ if self._model is None:
281
+ local_time = getattr(event, "local_time", None)
282
+ if (
283
+ isinstance(local_time, (int, float))
284
+ and not isinstance(local_time, bool)
285
+ and math.isfinite(local_time)
286
+ and local_time > 0
287
+ ):
288
+ return event
289
+ setattr(event, "local_time", getattr(event, "timestamp", 0.0))
290
+ return event
291
+ exch_ts = getattr(event, "timestamp", 0.0)
292
+ symbol = getattr(event, "symbol", "")
293
+ setattr(event, "local_time", exch_ts + float(self._model.feed_latency(exch_ts, symbol)))
294
+ return event
295
+
296
+ def get_response_time(self, exch_ts, symbol):
297
+ """Return the local time at which an exchange ack is received.
298
+
299
+ Args:
300
+ exch_ts: Exchange-side timestamp of the acknowledgement.
301
+ symbol: Symbol the response is for.
302
+
303
+ Returns:
304
+ float: ``exch_ts`` plus the model's order-response latency, or
305
+ ``exch_ts`` itself when no model is configured.
306
+ """
307
+ if self._model is None:
308
+ return float(exch_ts)
309
+ return float(exch_ts) + float(self._model.order_response_latency(exch_ts, symbol))