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,660 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Main report generator.
4
+
5
+ Generates backtest reports in HTML, PDF, and JSON formats.
6
+ """
7
+
8
+ import json
9
+ import math
10
+ import os
11
+ from datetime import datetime
12
+
13
+ from .charts import ReportChart
14
+ from .performance import PerformanceCalculator
15
+
16
+ # Try to import Jinja2
17
+ try:
18
+ from jinja2 import BaseLoader, Environment, FileSystemLoader
19
+
20
+ JINJA2_AVAILABLE = True
21
+ except ImportError:
22
+ JINJA2_AVAILABLE = False
23
+
24
+ # Try to import weasyprint (PDF generation)
25
+ try:
26
+ from weasyprint import HTML as WeasyHTML
27
+
28
+ WEASYPRINT_AVAILABLE = True
29
+ except ImportError:
30
+ WEASYPRINT_AVAILABLE = False
31
+
32
+
33
+ # Default HTML template
34
+ DEFAULT_TEMPLATE = """<!DOCTYPE html>
35
+ <html>
36
+ <head>
37
+ <meta charset="UTF-8">
38
+ <title>Backtrader Report - {{ strategy_name }}</title>
39
+ <style>
40
+ @page {
41
+ size: A4;
42
+ margin: 12mm 15mm;
43
+ }
44
+ * { box-sizing: border-box; margin: 0; padding: 0; }
45
+ body {
46
+ font-family: 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
47
+ font-size: 10pt;
48
+ line-height: 1.4;
49
+ color: #2c3e50;
50
+ background: white;
51
+ }
52
+
53
+ /* Header - Compact */
54
+ .header {
55
+ background: #1a365d;
56
+ color: white;
57
+ padding: 15px 20px;
58
+ margin-bottom: 12px;
59
+ border-bottom: 3px solid #3182ce;
60
+ }
61
+ .header h1 {
62
+ font-size: 18pt;
63
+ font-weight: 600;
64
+ margin-bottom: 3px;
65
+ }
66
+ .header .subtitle {
67
+ font-size: 11pt;
68
+ font-weight: 400;
69
+ margin-bottom: 10px;
70
+ opacity: 0.9;
71
+ }
72
+ .header-info {
73
+ font-size: 9pt;
74
+ line-height: 1.6;
75
+ }
76
+ .header-info span {
77
+ margin-right: 20px;
78
+ }
79
+ .header-info b { color: #90cdf4; }
80
+
81
+ /* Sections - Minimal spacing */
82
+ .section {
83
+ margin-bottom: 8px;
84
+ }
85
+ .section h2 {
86
+ padding: 6px 12px;
87
+ margin-bottom: 8px;
88
+ background: #edf2f7;
89
+ border-left: 3px solid #3182ce;
90
+ font-size: 12pt;
91
+ font-weight: 600;
92
+ color: #1a365d;
93
+ }
94
+
95
+ /* Notes */
96
+ .notes {
97
+ background: #fffbeb;
98
+ border: 1px solid #f6e05e;
99
+ padding: 8px 12px;
100
+ margin: 0 12px 10px 12px;
101
+ font-size: 9pt;
102
+ color: #744210;
103
+ }
104
+
105
+ /* Charts - New Page */
106
+ .section.charts-page {
107
+ page-break-before: always;
108
+ }
109
+ .charts {
110
+ padding: 0 12px;
111
+ }
112
+ .charts img {
113
+ width: 100%;
114
+ height: auto;
115
+ margin-bottom: 12px;
116
+ border: 1px solid #e2e8f0;
117
+ }
118
+
119
+ /* Params - New Page */
120
+ .section.params-page {
121
+ page-break-before: always;
122
+ }
123
+
124
+ /* Metrics Table - Compact */
125
+ .metrics-container {
126
+ padding: 0 12px;
127
+ }
128
+ .metrics-table {
129
+ width: 100%;
130
+ border-collapse: collapse;
131
+ margin-bottom: 10px;
132
+ font-size: 9pt;
133
+ }
134
+ .metrics-table td {
135
+ padding: 5px 8px;
136
+ border-bottom: 1px solid #e2e8f0;
137
+ }
138
+ .metrics-table .group-header td {
139
+ background: #3182ce;
140
+ color: white;
141
+ font-weight: 600;
142
+ font-size: 9pt;
143
+ text-transform: uppercase;
144
+ letter-spacing: 0.5px;
145
+ padding: 6px 8px;
146
+ }
147
+ .metrics-table .label {
148
+ color: #4a5568;
149
+ width: 22%;
150
+ }
151
+ .metrics-table .value {
152
+ font-weight: 600;
153
+ color: #2d3748;
154
+ text-align: right;
155
+ width: 28%;
156
+ }
157
+ .metrics-table .value.positive { color: #276749; }
158
+ .metrics-table .value.negative { color: #c53030; }
159
+
160
+ /* Parameters - Compact */
161
+ .params-table {
162
+ width: 60%;
163
+ border-collapse: collapse;
164
+ font-size: 9pt;
165
+ margin: 0 12px;
166
+ }
167
+ .params-table td {
168
+ padding: 4px 8px;
169
+ border: 1px solid #e2e8f0;
170
+ }
171
+ .params-table .param-name {
172
+ background: #f7fafc;
173
+ font-weight: 500;
174
+ width: 40%;
175
+ }
176
+
177
+ /* Footer - Minimal */
178
+ .footer {
179
+ text-align: center;
180
+ color: #718096;
181
+ font-size: 8pt;
182
+ padding: 10px;
183
+ border-top: 1px solid #e2e8f0;
184
+ margin-top: 15px;
185
+ }
186
+ .footer p { margin: 2px 0; }
187
+ </style>
188
+ </head>
189
+ <body>
190
+
191
+ <div class="header">
192
+ <h1>{{ strategy_name }}</h1>
193
+ <div class="subtitle">Backtest Performance Report</div>
194
+ <div class="header-info">
195
+ <span><b>Data:</b> {{ data_name }}</span>
196
+ <span><b>Period:</b> {{ start_date }} ~ {{ end_date }}</span>
197
+ <span><b>Bars:</b> {{ bars }}</span>
198
+ {% if user %}<span><b>Analyst:</b> {{ user }}</span>{% endif %}
199
+ <span><b>Generated:</b> {{ report_date }}</span>
200
+ </div>
201
+ </div>
202
+
203
+ {% if memo %}
204
+ <div class="section">
205
+ <h2>Notes</h2>
206
+ <div class="notes">{{ memo }}</div>
207
+ </div>
208
+ {% endif %}
209
+
210
+ <div class="section">
211
+ <h2>Performance Summary</h2>
212
+ <div class="metrics-container">
213
+ <table class="metrics-table">
214
+ <tr class="group-header"><td colspan="4">Profit & Loss</td></tr>
215
+ <tr>
216
+ <td class="label">Start Capital</td>
217
+ <td class="value">{{ "${:,.2f}".format(start_cash) if start_cash is not none else 'N/A' }}</td>
218
+ <td class="label">End Value</td>
219
+ <td class="value">{{ "${:,.2f}".format(end_value) if end_value is not none else 'N/A' }}</td>
220
+ </tr>
221
+ <tr>
222
+ <td class="label">Net Profit</td>
223
+ <td class="value {{ 'positive' if rpl is not none and rpl > 0 else 'negative' if rpl is not none and rpl < 0 else '' }}">{{ "${:,.2f}".format(rpl) if rpl is not none else 'N/A' }}</td>
224
+ <td class="label">Total Return</td>
225
+ <td class="value {{ 'positive' if total_return is not none and total_return > 0 else 'negative' if total_return is not none and total_return < 0 else '' }}">{{ "{:.2f}%".format(total_return) if total_return is not none else 'N/A' }}</td>
226
+ </tr>
227
+ <tr>
228
+ <td class="label">Annual Return</td>
229
+ <td class="value {{ 'positive' if annual_return is not none and annual_return > 0 else 'negative' if annual_return is not none and annual_return < 0 else '' }}">{{ "{:.2f}%".format(annual_return) if annual_return is not none else 'N/A' }}</td>
230
+ <td class="label">Profit Factor</td>
231
+ <td class="value">{{ "{:.2f}".format(profit_factor) if profit_factor is not none else 'N/A' }}</td>
232
+ </tr>
233
+
234
+ <tr class="group-header"><td colspan="4">Risk Metrics</td></tr>
235
+ <tr>
236
+ <td class="label">Max Drawdown ($)</td>
237
+ <td class="value negative">{{ "${:,.2f}".format(max_money_drawdown) if max_money_drawdown is not none else 'N/A' }}</td>
238
+ <td class="label">Max Drawdown (%)</td>
239
+ <td class="value negative">{{ "{:.2f}%".format(max_pct_drawdown) if max_pct_drawdown is not none else 'N/A' }}</td>
240
+ </tr>
241
+ <tr>
242
+ <td class="label">Sharpe Ratio</td>
243
+ <td class="value">{{ "{:.2f}".format(sharpe_ratio) if sharpe_ratio is not none else 'N/A' }}</td>
244
+ <td class="label">Calmar Ratio</td>
245
+ <td class="value">{{ "{:.2f}".format(calmar_ratio) if calmar_ratio is not none else 'N/A' }}</td>
246
+ </tr>
247
+ <tr>
248
+ <td class="label">SQN Score</td>
249
+ <td class="value">{{ "{:.2f}".format(sqn_score) if sqn_score is not none else 'N/A' }}</td>
250
+ <td class="label">SQN Rating</td>
251
+ <td class="value">{{ sqn_human if sqn_human is not none else 'N/A' }}</td>
252
+ </tr>
253
+
254
+ <tr class="group-header"><td colspan="4">Trade Statistics</td></tr>
255
+ <tr>
256
+ <td class="label">Total Trades</td>
257
+ <td class="value">{{ total_number_trades }}</td>
258
+ <td class="label">Closed Trades</td>
259
+ <td class="value">{{ trades_closed }}</td>
260
+ </tr>
261
+ <tr>
262
+ <td class="label">Win Rate</td>
263
+ <td class="value">{{ "{:.2f}%".format(pct_winning) if pct_winning is not none else 'N/A' }}</td>
264
+ <td class="label">Avg Win</td>
265
+ <td class="value positive">{{ "${:,.2f}".format(avg_money_winning) if avg_money_winning is not none else 'N/A' }}</td>
266
+ </tr>
267
+ <tr>
268
+ <td class="label">Avg Loss</td>
269
+ <td class="value negative">{{ "${:,.2f}".format(avg_money_losing) if avg_money_losing is not none else 'N/A' }}</td>
270
+ <td class="label">Best Trade</td>
271
+ <td class="value positive">{{ "${:,.2f}".format(best_winning_trade) if best_winning_trade is not none else 'N/A' }}</td>
272
+ </tr>
273
+ <tr>
274
+ <td class="label">Worst Trade</td>
275
+ <td class="value negative">{{ "${:,.2f}".format(worst_losing_trade) if worst_losing_trade is not none else 'N/A' }}</td>
276
+ <td class="label"></td>
277
+ <td class="value"></td>
278
+ </tr>
279
+ </table>
280
+ </div>
281
+ </div>
282
+
283
+ <div class="section charts-page">
284
+ <h2>Performance Charts</h2>
285
+ <div class="charts">
286
+ {% if equity_curve_img %}<img src="{{ equity_curve_img|safe }}" alt="Equity Curve">{% endif %}
287
+ {% if return_bars_img %}<img src="{{ return_bars_img|safe }}" alt="Return Bars">{% endif %}
288
+ {% if drawdown_img %}<img src="{{ drawdown_img|safe }}" alt="Drawdown">{% endif %}
289
+ </div>
290
+ </div>
291
+
292
+ {% if params %}
293
+ <div class="section params-page">
294
+ <h2>Strategy Parameters</h2>
295
+ <div class="metrics-container">
296
+ <table class="params-table">
297
+ {% for key, value in params.items() %}
298
+ <tr>
299
+ <td class="param-name">{{ key }}</td>
300
+ <td>{{ value }}</td>
301
+ </tr>
302
+ {% endfor %}
303
+ </table>
304
+ </div>
305
+ </div>
306
+ {% endif %}
307
+
308
+ <div class="footer">
309
+ <p>Generated by <strong>Backtrader Reports Module</strong></p>
310
+ <p>{{ report_date }}</p>
311
+ </div>
312
+
313
+ </body>
314
+ </html>
315
+ """
316
+
317
+
318
+ class ReportGenerator:
319
+ """Main report generator.
320
+
321
+ Generates backtest reports in HTML, PDF, and JSON formats.
322
+
323
+ Attributes:
324
+ strategy: Strategy instance
325
+ calculator: Performance calculator
326
+ charts: Chart generator
327
+
328
+ Usage example:
329
+ report = ReportGenerator(strategy)
330
+ report.generate_html('report.html')
331
+ report.generate_pdf('report.pdf')
332
+ report.generate_json('report.json')
333
+ """
334
+
335
+ def __init__(self, strategy, template="default"):
336
+ """Initialize the report generator.
337
+
338
+ Args:
339
+ strategy: backtrader strategy instance
340
+ template: Template name or template string
341
+ """
342
+ self.strategy = strategy
343
+ self.calculator = PerformanceCalculator(strategy)
344
+ self.charts = ReportChart()
345
+ self.template = template
346
+
347
+ def generate_html(self, output_path, user=None, memo=None, **kwargs):
348
+ """Generate HTML report.
349
+
350
+ Args:
351
+ output_path: Output file path
352
+ user: Username
353
+ memo: Notes
354
+ **kwargs: Additional template variables
355
+
356
+ Returns:
357
+ str: Output file path
358
+ """
359
+ if not JINJA2_AVAILABLE:
360
+ raise ImportError(
361
+ "jinja2 is required for HTML report generation. Install it with: pip install jinja2"
362
+ )
363
+
364
+ # Collect all data
365
+ context = self._build_context(user=user, memo=memo, **kwargs)
366
+
367
+ # Render template
368
+ html_content = self._render_template(context)
369
+
370
+ # Write to file
371
+ with open(output_path, "w", encoding="utf-8") as f:
372
+ f.write(html_content)
373
+
374
+ # Clean up charts
375
+ self.charts.close_all()
376
+
377
+ return output_path
378
+
379
+ def generate_pdf(self, output_path, user=None, memo=None, **kwargs):
380
+ """Generate PDF report.
381
+
382
+ Args:
383
+ output_path: Output file path
384
+ user: Username
385
+ memo: Notes
386
+ **kwargs: Additional template variables
387
+
388
+ Returns:
389
+ str: Output file path
390
+ """
391
+ if not WEASYPRINT_AVAILABLE:
392
+ raise ImportError(
393
+ "weasyprint is required for PDF report generation. Install it with: pip install weasyprint"
394
+ )
395
+
396
+ # Collect all data
397
+ context = self._build_context(user=user, memo=memo, **kwargs)
398
+
399
+ # Render template
400
+ html_content = self._render_template(context)
401
+
402
+ # Convert to PDF
403
+ WeasyHTML(string=html_content).write_pdf(output_path)
404
+
405
+ # Clean up charts
406
+ self.charts.close_all()
407
+
408
+ return output_path
409
+
410
+ def generate_json(self, output_path, indent=2, **kwargs):
411
+ """Generate JSON report.
412
+
413
+ Args:
414
+ output_path: Output file path
415
+ indent: JSON indentation
416
+ **kwargs: Additional data
417
+
418
+ Returns:
419
+ str: Output file path
420
+ """
421
+ # Get all metrics
422
+ metrics = self.calculator.get_all_metrics()
423
+ strategy_info = self.calculator.get_strategy_info()
424
+ data_info = self.calculator.get_data_info()
425
+
426
+ # Build JSON structure
427
+ report_data = {
428
+ "generated_at": datetime.now().isoformat(),
429
+ "strategy": strategy_info,
430
+ "data": {
431
+ "name": data_info.get("data_name"),
432
+ "start_date": (
433
+ str(data_info.get("start_date")) if data_info.get("start_date") else None
434
+ ),
435
+ "end_date": str(data_info.get("end_date")) if data_info.get("end_date") else None,
436
+ "bars": data_info.get("bars"),
437
+ },
438
+ "metrics": {
439
+ "pnl": {
440
+ "start_cash": metrics.get("start_cash"),
441
+ "end_value": metrics.get("end_value"),
442
+ "net_profit": metrics.get("rpl"),
443
+ "total_return": metrics.get("total_return"),
444
+ "annual_return": metrics.get("annual_return"),
445
+ "profit_factor": metrics.get("profit_factor"),
446
+ },
447
+ "risk": {
448
+ "max_drawdown_money": metrics.get("max_money_drawdown"),
449
+ "max_drawdown_pct": metrics.get("max_pct_drawdown"),
450
+ "sharpe_ratio": metrics.get("sharpe_ratio"),
451
+ "calmar_ratio": metrics.get("calmar_ratio"),
452
+ "sqn_score": metrics.get("sqn_score"),
453
+ "sqn_rating": metrics.get("sqn_human"),
454
+ },
455
+ "trades": {
456
+ "total": metrics.get("total_number_trades"),
457
+ "closed": metrics.get("trades_closed"),
458
+ "won": metrics.get("trades_won"),
459
+ "lost": metrics.get("trades_lost"),
460
+ "win_rate": metrics.get("pct_winning"),
461
+ "avg_win": metrics.get("avg_money_winning"),
462
+ "avg_loss": metrics.get("avg_money_losing"),
463
+ "best_trade": metrics.get("best_winning_trade"),
464
+ "worst_trade": metrics.get("worst_losing_trade"),
465
+ },
466
+ },
467
+ **kwargs,
468
+ }
469
+
470
+ # Handle non-serializable values
471
+ report_data = self._make_json_serializable(report_data)
472
+
473
+ # Write to file
474
+ with open(output_path, "w", encoding="utf-8") as f:
475
+ json.dump(report_data, f, indent=indent, ensure_ascii=False)
476
+
477
+ return output_path
478
+
479
+ def _build_context(self, user=None, memo=None, **kwargs):
480
+ """Build template context.
481
+
482
+ Args:
483
+ user: Username for report metadata
484
+ memo: Notes for report metadata
485
+ **kwargs: Additional template variables
486
+
487
+ Returns:
488
+ dict: Template variables dictionary
489
+ """
490
+ # Get metrics
491
+ metrics = self.calculator.get_all_metrics()
492
+ strategy_info = self.calculator.get_strategy_info()
493
+ data_info = self.calculator.get_data_info()
494
+
495
+ # Generate charts
496
+ dates, values = self.calculator.get_equity_curve()
497
+ benchmark_dates, benchmark_values = self.calculator.get_buynhold_curve()
498
+
499
+ equity_curve_img = ""
500
+ return_bars_img = ""
501
+ drawdown_img = ""
502
+
503
+ if dates and values:
504
+ # Equity curve
505
+ fig_equity = self.charts.plot_equity_curve(
506
+ dates, values, benchmark_dates, benchmark_values
507
+ )
508
+ if fig_equity:
509
+ equity_curve_img = self.charts.to_base64(fig_equity)
510
+
511
+ # Return bars chart
512
+ fig_returns = self.charts.plot_return_bars(dates, values)
513
+ if fig_returns:
514
+ return_bars_img = self.charts.to_base64(fig_returns)
515
+
516
+ # Drawdown chart
517
+ fig_drawdown = self.charts.plot_drawdown(dates, values)
518
+ if fig_drawdown:
519
+ drawdown_img = self.charts.to_base64(fig_drawdown)
520
+
521
+ # Build context
522
+ serializable_metrics = self._make_json_serializable(metrics)
523
+ serializable_kwargs = self._make_json_serializable(kwargs)
524
+ context = {
525
+ # Strategy information
526
+ "strategy_name": strategy_info.get("strategy_name", "Strategy"),
527
+ "params": self._make_json_serializable(strategy_info.get("params", {})),
528
+ # Data information
529
+ "data_name": data_info.get("data_name", "Data"),
530
+ "start_date": (
531
+ str(data_info.get("start_date", ""))[:10] if data_info.get("start_date") else "N/A"
532
+ ),
533
+ "end_date": (
534
+ str(data_info.get("end_date", ""))[:10] if data_info.get("end_date") else "N/A"
535
+ ),
536
+ "bars": data_info.get("bars", 0),
537
+ # User information
538
+ "user": user,
539
+ "memo": memo,
540
+ "report_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
541
+ # Charts
542
+ "equity_curve_img": equity_curve_img,
543
+ "return_bars_img": return_bars_img,
544
+ "drawdown_img": drawdown_img,
545
+ # Metrics
546
+ **serializable_metrics,
547
+ **serializable_kwargs,
548
+ }
549
+
550
+ return context
551
+
552
+ def _render_template(self, context):
553
+ """Render template.
554
+
555
+ Args:
556
+ context: Template variables dictionary
557
+
558
+ Returns:
559
+ str: Rendered HTML
560
+ """
561
+ if self.template == "default":
562
+ # Use default template
563
+ env = Environment(loader=BaseLoader(), autoescape=True)
564
+ template = env.from_string(DEFAULT_TEMPLATE)
565
+ else:
566
+ # Try to load as file path
567
+ if os.path.isfile(self.template):
568
+ template_dir = os.path.dirname(self.template)
569
+ template_name = os.path.basename(self.template)
570
+ env = Environment(loader=FileSystemLoader(template_dir), autoescape=True)
571
+ template = env.get_template(template_name)
572
+ else:
573
+ # Handle as template string
574
+ env = Environment(loader=BaseLoader(), autoescape=True)
575
+ template = env.from_string(self.template)
576
+
577
+ return template.render(**context)
578
+
579
+ def _make_json_serializable(self, obj):
580
+ """Make object JSON serializable.
581
+
582
+ Args:
583
+ obj: Object to process
584
+
585
+ Returns:
586
+ Serializable object
587
+ """
588
+ import math
589
+
590
+ if isinstance(obj, dict):
591
+ return {k: self._make_json_serializable(v) for k, v in obj.items()}
592
+ if isinstance(obj, (list, tuple)):
593
+ return [self._make_json_serializable(v) for v in obj]
594
+ if isinstance(obj, float):
595
+ if math.isnan(obj) or math.isinf(obj):
596
+ return None
597
+ return obj
598
+ if hasattr(obj, "isoformat") and callable(getattr(obj, "isoformat", None)):
599
+ return obj.isoformat()
600
+ if hasattr(obj, "__dict__"):
601
+ return str(obj)
602
+ return obj
603
+
604
+ def get_metrics(self):
605
+ """Get all performance metrics.
606
+
607
+ Returns:
608
+ dict: Performance metrics dictionary
609
+ """
610
+ return self.calculator.get_all_metrics()
611
+
612
+ @staticmethod
613
+ def _fmt_metric(value, fmt=",.2f", suffix=""):
614
+ """Format a metric value for console display.
615
+
616
+ Args:
617
+ value: Metric value (may be None)
618
+ fmt: Format string for numeric values
619
+ suffix: Suffix to append (e.g. '%')
620
+
621
+ Returns:
622
+ str: Formatted value or 'N/A'
623
+ """
624
+ if value is None:
625
+ return "N/A"
626
+ if isinstance(value, (int, float)) and not math.isfinite(value):
627
+ return "N/A"
628
+ try:
629
+ return f"{value:{fmt}}{suffix}"
630
+ except (ValueError, TypeError):
631
+ return str(value)
632
+
633
+ def print_summary(self):
634
+ """Print performance summary to console."""
635
+ metrics = self.calculator.get_all_metrics()
636
+ strategy_info = self.calculator.get_strategy_info()
637
+ fmt = self._fmt_metric
638
+
639
+ print("\n" + "=" * 60)
640
+ print(f"Strategy: {strategy_info.get('strategy_name', 'Strategy')}")
641
+ print("=" * 60)
642
+
643
+ print("\n*** PnL ***")
644
+ print(f"Start Capital : {fmt(metrics.get('start_cash'))}")
645
+ print(f"Net Profit : {fmt(metrics.get('rpl'))}")
646
+ print(f"Total Return : {fmt(metrics.get('total_return'), '.2f', '%')}")
647
+ print(f"Annual Return : {fmt(metrics.get('annual_return'), '.2f', '%')}")
648
+
649
+ print("\n*** Risk ***")
650
+ print(f"Max Drawdown ($) : {fmt(metrics.get('max_money_drawdown'))}")
651
+ print(f"Max Drawdown (%) : {fmt(metrics.get('max_pct_drawdown'), '.2f', '%')}")
652
+ print(f"Sharpe Ratio : {fmt(metrics.get('sharpe_ratio'), '.2f')}")
653
+
654
+ print("\n*** Trades ***")
655
+ print(f"Total Trades : {metrics.get('total_number_trades', 0)}")
656
+ print(f"Win Rate : {fmt(metrics.get('pct_winning'), '.2f', '%')}")
657
+ print(f"SQN Score : {fmt(metrics.get('sqn_score'), '.2f')}")
658
+ print(f"SQN Rating : {metrics.get('sqn_human', 'N/A')}")
659
+
660
+ print("\n" + "=" * 60)