Automate Fund Backtest Reports with Python Jinja2: From Data to HTML
This tutorial shows how to replace cumbersome PDF reports with dynamic HTML reports generated by Python and Jinja2, covering template creation, inserting text, tables, images, and styling to fully automate fund backtesting result publishing.
In a previous article a simple fund‑purchase backtesting system was built with Python, producing many charts and tables.
Saving each result manually is inefficient, so instead of generating a PDF (which is cumbersome due to pagination), the author chooses HTML for its simple layout and cross‑platform nature.
Jinja2 is used as the template engine. A basic template.html file is created with placeholders wrapped in {{ }} for strategy name, dates, initial capital, etc.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>report</title>
</head>
<h1>基金策略回测报告</h1>
<body>
<h2>一、策略详情</h2>
<p>策略描述:{{ strategy_name }}</p>
<p>回测时间段:{{ start_time }} --> {{ end_time }}</p>
<p>初始本金:{{ money }}</p>
</body>
</html>Python code loads the template with Environment(loader=FileSystemLoader('./')), renders it with a dictionary of values, and writes the result to out.html.
import pandas as pd
from jinja2 import Environment, FileSystemLoader
data = {'strategy_name': '第一个策略',
'start_time': '2020-01-01',
'end_time': '2021-06-01',
'money': 20000}
env = Environment(loader=FileSystemLoader('./'))
template = env.get_template('template.html')
with open("out.html", 'w+', encoding='utf-8') as f:
out = template.render(strategy_name=data['strategy_name'],
start_time=data['start_time'],
end_time=data['end_time'],
money=data['money'])
f.write(out)To insert a table, the template defines a <table> with a Jinja2 loop {% for item in items %} that fills rows with values such as fund name, invested capital, total return rate, and maximum drawdown. The Python script reads an Excel file with pandas, formats columns, converts rows to a list of dictionaries via df.to_dict('records'), and passes it as items to the template.
df = pd.read_excel('回测指标汇总.xlsx')
df['消耗本金'] = df['消耗本金'].astype(str) + ' 元'
df['最大回撤率'] = df['最大回撤率'].astype(str) + '%'
df['总收益率'] = df['总收益率'].astype(str) + '%'
data = df.to_dict('records')
results = {'strategy_name': '第一个策略',
'start_time': '2020-01-01',
'end_time': '2021-06-01',
'money': 20000,
'items': data}
# rendering omitted for brevityImages are added by reserving an
<a name="{{ indicator }}"><img src="{{ indicator }}" width="850"></a>placeholder in the template and passing the image path from Python.
indicator = 'images/回测指标.png'
...
out = template.render(..., indicator=indicator)Optional inline CSS can be inserted with a <style> block to improve spacing and visual appearance of headings, tables, and paragraphs.
After running the script, out.html contains the fully populated report with text, tables, and images, which can be viewed in any browser.
The same approach can be extended to generate other report formats or to automate the entire workflow.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Python Crawling & Data Mining
Life's short, I code in Python. This channel shares Python web crawling, data mining, analysis, processing, visualization, automated testing, DevOps, big data, AI, cloud computing, machine learning tools, resources, news, technical articles, tutorial videos and learning materials. Join us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
