mirror of
https://github.com/wassname/btreport.git
synced 2026-09-17 12:10:09 +08:00
First commit
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2019 The Python Packaging Authority
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1 @@
|
||||
include btreport/templates/*
|
||||
@@ -0,0 +1,22 @@
|
||||
cdns_dict = {
|
||||
'cerulean': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/cerulean/bootstrap.min.css" rel="stylesheet" integrity="sha384-C++cugH8+Uf86JbNOnQoBweHHAe/wVKN/mb0lTybu/NZ9sEYbd+BbbYtNpWYAsNP" crossorigin="anonymous">',
|
||||
'cosmo': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/cosmo/bootstrap.min.css" rel="stylesheet" integrity="sha384-uhut8PejFZO8994oEgm/ZfAv0mW1/b83nczZzSwElbeILxwkN491YQXsCFTE6+nx" crossorigin="anonymous">',
|
||||
'cyborg': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/cyborg/bootstrap.min.css" rel="stylesheet" integrity="sha384-mtS696VnV9qeIoC8w/PrPoRzJ5gwydRVn0oQ9b+RJOPxE1Z1jXuuJcyeNxvNZhdx" crossorigin="anonymous">',
|
||||
'darkly': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/darkly/bootstrap.min.css" rel="stylesheet" integrity="sha384-w+8Gqjk9Cuo6XH9HKHG5t5I1VR4YBNdPt/29vwgfZR485eoEJZ8rJRbm3TR32P6k" crossorigin="anonymous">',
|
||||
'flatly': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/flatly/bootstrap.min.css" rel="stylesheet" integrity="sha384-T5jhQKMh96HMkXwqVMSjF3CmLcL1nT9//tCqu9By5XSdj7CwR0r+F3LTzUdfkkQf" crossorigin="anonymous">',
|
||||
'journal': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/journal/bootstrap.min.css" rel="stylesheet" integrity="sha384-ciphE0NCAlD2/N6NUApXAN2dAs/vcSAOTzyE202jJx3oS8n4tAQezRgnlHqcJ59C" crossorigin="anonymous">',
|
||||
'litera': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/litera/bootstrap.min.css" rel="stylesheet" integrity="sha384-D/7uAka7uwterkSxa2LwZR7RJqH2X6jfmhkJ0vFPGUtPyBMF2WMq9S+f9Ik5jJu1" crossorigin="anonymous">',
|
||||
'lumen': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/lumen/bootstrap.min.css" rel="stylesheet" integrity="sha384-iqcNtN3rj6Y1HX/R0a3zu3ngmbdwEa9qQGHdkXwSRoiE+Gj71p0UNDSm99LcXiXV" crossorigin="anonymous">',
|
||||
'lux': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/lux/bootstrap.min.css" rel="stylesheet" integrity="sha384-hVpXlpdRmJ+uXGwD5W6HZMnR9ENcKVRn855pPbuI/mwPIEKAuKgTKgGksVGmlAvt" crossorigin="anonymous">',
|
||||
'materia': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/materia/bootstrap.min.css" rel="stylesheet" integrity="sha384-SYbiks6VdZNAKT8DNoXQZwXAiuUo5/quw6nMKtFlGO/4WwxW86BSTMtgdzzB9JJl" crossorigin="anonymous">',
|
||||
'minty': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/minty/bootstrap.min.css" rel="stylesheet" integrity="sha384-9NlqO4dP5KfioUGS568UFwM3lbWf3Uj3Qb7FBHuIuhLoDp3ZgAqPE1/MYLEBPZYM" crossorigin="anonymous">',
|
||||
'pulse': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/pulse/bootstrap.min.css" rel="stylesheet" integrity="sha384-/uQFqO50IaQu2rNJYKPpV7zwsWJtd6V4DGX4wMw1ATz4KPuZEV96qQ2heVAw2kr2" crossorigin="anonymous">',
|
||||
'sandstone': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/sandstone/bootstrap.min.css" rel="stylesheet" integrity="sha384-G3Fme2BM4boCE9tHx9zHvcxaQoAkksPQa/8oyn1Dzqv7gdcXChereUsXGx6LtbqA" crossorigin="anonymous">',
|
||||
'simplex': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/simplex/bootstrap.min.css" rel="stylesheet" integrity="sha384-1OYccka9EByiS23wvPFiYHBPRAgU91xYVFb8g8sen6vRiBI5Uko6+B87q8zPGUnA" crossorigin="anonymous">',
|
||||
'sketchy': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/sketchy/bootstrap.min.css" rel="stylesheet" integrity="sha384-N8DsABZCqc1XWbg/bAlIDk7AS/yNzT5fcKzg/TwfmTuUqZhGquVmpb5VvfmLcMzp" crossorigin="anonymous">',
|
||||
'slate': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/slate/bootstrap.min.css" rel="stylesheet" integrity="sha384-FBPbZPVh+7ks5JJ70RJmIaqyGnvMbeJ5JQfEbW0Ac6ErfvEg9yG56JQJuMNptWsH" crossorigin="anonymous">',
|
||||
'solar': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/solar/bootstrap.min.css" rel="stylesheet" integrity="sha384-8nq3OiMMgrVFAHyRMMO+DTfMEciSY+c3Awhj/5ljQ1xck1Uv2BUtMjsjLD8GT5Er" crossorigin="anonymous">',
|
||||
'spacelab': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/spacelab/bootstrap.min.css" rel="stylesheet" integrity="sha384-sZG5VVk41YqhJjYXgJFoRVd3d2AdDgy4oyIytQJMGx/Mizz1N+5bgKQBSCGfKQnP" crossorigin="anonymous">',
|
||||
'superhero': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/superhero/bootstrap.min.css" rel="stylesheet" integrity="sha384-LS4/wo5Z/8SLpOLHs0IbuPAGOWTx30XSoZJ8o7WKH0UJhRpjXXTpODOjfVnNjeHu" crossorigin="anonymous">',
|
||||
'united': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/united/bootstrap.min.css" rel="stylesheet" integrity="sha384-WTtvlZJeRyCiKUtbQ88X1x9uHmKi0eHCbQ8irbzqSLkE0DpAZuixT5yFvgX0CjIu" crossorigin="anonymous">',
|
||||
'yeti': '<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/yeti/bootstrap.min.css" rel="stylesheet" integrity="sha384-w6tc0TXjTUnYHwVwGgnYyV12wbRoJQo9iMlC2KdkdmVvntGgzT9jvqNEF/uKaF4m" crossorigin="anonymous">'}
|
||||
@@ -0,0 +1,502 @@
|
||||
"""
|
||||
Contains reporting logic and objects.
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
import math
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import bt
|
||||
import ffn
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from statsmodels.tsa.stattools import acf
|
||||
|
||||
import matplotlib
|
||||
import plotly
|
||||
import plotly.figure_factory as ff
|
||||
import plotly.graph_objs as go
|
||||
import plotly.plotly as py
|
||||
|
||||
from bt.backtest import Result
|
||||
from .cdns import cdns_dict
|
||||
from ffn import GroupStats
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from matplotlib import pyplot as plt
|
||||
from plotly import tools
|
||||
from plotly.offline import plot
|
||||
|
||||
|
||||
class Report(Result):
|
||||
|
||||
"""
|
||||
NEED TO FILL IN
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, result):
|
||||
self.result = result
|
||||
self.result = result
|
||||
self.backtest_list = result.backtest_list
|
||||
self.backtests = result.backtests
|
||||
|
||||
def get_years(self):
|
||||
years = len(self.backtest_list[0].strategy.prices.resample("Y"))
|
||||
return years
|
||||
|
||||
def get_nominal_volumes(self, backtest=0):
|
||||
temp_transactions = self.get_transactions().reset_index()
|
||||
nominal_volumes = temp_transactions.groupby("Security")["quantity"].apply(
|
||||
lambda x: x.abs().sum()
|
||||
)
|
||||
return nominal_volumes
|
||||
|
||||
def get_value_volumes(self, backtest=0):
|
||||
strategy_name = self.backtest_list[0].name
|
||||
temp_transactions = self.get_transactions(
|
||||
strategy_name=strategy_name
|
||||
).reset_index()
|
||||
temp_transactions["value_vol"] = (
|
||||
temp_transactions["quantity"] * temp_transactions["price"]
|
||||
)
|
||||
value_volumes = temp_transactions.groupby("Security")["value_vol"].apply(
|
||||
lambda x: x.abs().sum()
|
||||
)
|
||||
return value_volumes
|
||||
|
||||
def get_individual_equity_curves(self, backtest=0):
|
||||
weights = self.get_weights()[self.get_weights().columns[1:]]
|
||||
weights.columns = [x.split(">")[1] for x in self.get_weights().columns[1:]]
|
||||
weights.replace(0, np.NaN, inplace=True)
|
||||
equity_curves = (
|
||||
(self.backtest_list[backtest].data.pct_change() * weights).dropna(how="all")
|
||||
+ 1
|
||||
).cumprod().ffill() * 100
|
||||
return equity_curves
|
||||
|
||||
def get_monthly_return_table(self, backtest=0):
|
||||
table = (self.result[backtest].return_table * 100).round(2)
|
||||
return table.to_html(
|
||||
classes="table table-hover table-bordered table-striped dt dataTable"
|
||||
)
|
||||
|
||||
def get_stats_table_strat(self, backtest=0):
|
||||
table = self.result[backtest].stats.to_frame()
|
||||
for col in table:
|
||||
table[col].iloc[2:] = table[col].iloc[2:].apply(round, args=(3,))
|
||||
table.columns = ["Strategy"]
|
||||
return table.to_html(
|
||||
classes="table strat-stats table-hover table-bordered table-striped dataTable",
|
||||
header=True,
|
||||
)
|
||||
|
||||
def get_stats_table_ind(self, equity_curves):
|
||||
table = GroupStats(equity_curves).stats
|
||||
for col in table:
|
||||
table[col].iloc[2:] = table[col].iloc[2:].apply(round, args=(3,))
|
||||
return table.to_html(
|
||||
classes="table ind-stats table-hover table-bordered table-striped dataTable"
|
||||
)
|
||||
|
||||
def get_trade_numbers(self):
|
||||
return (
|
||||
self.get_transactions()
|
||||
.reset_index()
|
||||
.groupby("Security")
|
||||
.agg("count")["quantity"]
|
||||
)
|
||||
|
||||
def get_acf(self, series):
|
||||
acf_strat = acf(
|
||||
series.loc[series[series != 0.0].first_valid_index() :], alpha=0.05
|
||||
)
|
||||
acf_strat_df = pd.DataFrame(
|
||||
{
|
||||
"acf_res": acf_strat[0],
|
||||
"acf_lower": [x[0] for x in acf_strat[1]],
|
||||
"acf__higher": [x[1] for x in acf_strat[1]],
|
||||
}
|
||||
)
|
||||
return acf_strat_df
|
||||
|
||||
def plot_eq_chart(self, equity_curves, kind="Equity", size="auto"):
|
||||
|
||||
title = kind
|
||||
|
||||
if size == "half":
|
||||
width = 840
|
||||
elif size == "full":
|
||||
width = 1200
|
||||
elif size == "auto":
|
||||
width = None
|
||||
|
||||
layout = go.Layout(
|
||||
title=title + " Chart",
|
||||
yaxis=dict(title=kind),
|
||||
height=600,
|
||||
width=width,
|
||||
autosize=True,
|
||||
showlegend=True,
|
||||
legend=dict(orientation="h"),
|
||||
template=self.theme,
|
||||
)
|
||||
|
||||
# print(equity_curves)
|
||||
trace_list = []
|
||||
|
||||
if kind == "Equity" or kind == "Weights":
|
||||
|
||||
if isinstance(equity_curves, pd.Series):
|
||||
x = equity_curves.index
|
||||
y = equity_curves.values
|
||||
|
||||
trace_eq = go.Scatter(
|
||||
x=x,
|
||||
y=y,
|
||||
name=self.backtest_list[0].name,
|
||||
marker=dict(line=dict(width=0.5)),
|
||||
)
|
||||
|
||||
data = [trace_eq]
|
||||
|
||||
elif isinstance(equity_curves, pd.DataFrame):
|
||||
for curve in equity_curves:
|
||||
if ">" in curve:
|
||||
name = curve.split(">")[1]
|
||||
else:
|
||||
name = curve
|
||||
|
||||
trace_eq = go.Scatter(
|
||||
x=equity_curves[curve].index,
|
||||
y=equity_curves[curve].values,
|
||||
name=name,
|
||||
marker=dict(line=dict(width=0.5)),
|
||||
)
|
||||
|
||||
trace_list.append(trace_eq)
|
||||
data = trace_list
|
||||
|
||||
elif kind == "Drawdown":
|
||||
|
||||
if isinstance(equity_curves, pd.Series):
|
||||
x = equity_curves.index
|
||||
y = equity_curves.to_drawdown_series().values
|
||||
|
||||
trace_dd = go.Scatter(
|
||||
x=x,
|
||||
y=y,
|
||||
name=self.backtest_list[0].name,
|
||||
marker=dict(line=dict(width=0.5)),
|
||||
)
|
||||
# line = dict(color = ('rgb(205, 12, 24)')))
|
||||
|
||||
data = [trace_dd]
|
||||
|
||||
elif isinstance(equity_curves, pd.DataFrame):
|
||||
eq_trace_list = []
|
||||
for curve in equity_curves:
|
||||
trace_dd = go.Scatter(
|
||||
x=equity_curves[curve].index,
|
||||
y=equity_curves[curve].to_drawdown_series().values,
|
||||
name=curve,
|
||||
marker=dict(line=dict(width=0.5)),
|
||||
)
|
||||
|
||||
trace_list.append(trace_dd)
|
||||
data = trace_list
|
||||
|
||||
fig = go.Figure(data=data, layout=layout)
|
||||
|
||||
# fig.layout.template = self.theme
|
||||
|
||||
chart_div = plot(fig, output_type="div", include_plotlyjs=False)
|
||||
|
||||
return chart_div
|
||||
|
||||
def acf_plot(self, acf_df, size="auto"):
|
||||
|
||||
if size == "half":
|
||||
width = 840
|
||||
elif size == "full":
|
||||
width = 1200
|
||||
elif size == "auto":
|
||||
width = None
|
||||
|
||||
layout = go.Layout(
|
||||
title="ACF Chart",
|
||||
yaxis=dict(title=None),
|
||||
height=600,
|
||||
width=width,
|
||||
autosize=True,
|
||||
showlegend=True,
|
||||
legend=dict(orientation="h"),
|
||||
template=self.theme,
|
||||
)
|
||||
|
||||
trace_list = []
|
||||
|
||||
for series in acf_df:
|
||||
trace = go.Scatter(
|
||||
x=acf_df[series].index,
|
||||
y=acf_df[series].values,
|
||||
name=series,
|
||||
marker=dict(line=dict(width=0.5)),
|
||||
)
|
||||
|
||||
trace_list.append(trace)
|
||||
data = trace_list
|
||||
|
||||
fig = go.Figure(data=data, layout=layout)
|
||||
|
||||
chart_div = plot(fig, output_type="div",include_plotlyjs=False)
|
||||
|
||||
return chart_div
|
||||
|
||||
def pie_plot(self, volumes, kind, title):
|
||||
labels = volumes.index.values
|
||||
values = volumes.round(0).values
|
||||
|
||||
pie = go.Pie(
|
||||
labels=labels,
|
||||
values=values,
|
||||
sort=False,
|
||||
hoverinfo="label+percent",
|
||||
textinfo="value",
|
||||
textfont=dict(size=20),
|
||||
marker=dict(line=dict(color=("rgb(22, 96, 167)"), width=2)),
|
||||
)
|
||||
|
||||
data = [pie]
|
||||
|
||||
layout = go.Layout(
|
||||
title=title + " " + kind,
|
||||
legend=dict(orientation="h"),
|
||||
showlegend=True,
|
||||
margin=go.layout.Margin(l=10, r=10, b=10, t=50, pad=4),
|
||||
)
|
||||
|
||||
fig = go.Figure(data=data, layout=layout)
|
||||
|
||||
fig.layout.template = self.theme
|
||||
|
||||
chart_div = plot(fig, output_type="div", include_plotlyjs =False)
|
||||
|
||||
return chart_div
|
||||
|
||||
def dist_plot(self, equity_curves):
|
||||
hist_data = []
|
||||
|
||||
if isinstance(equity_curves, pd.Series):
|
||||
hist_data.append(equity_curves.pct_change().dropna().values)
|
||||
group_labels = ["strategy"]
|
||||
|
||||
else:
|
||||
for symbol in equity_curves:
|
||||
data = equity_curves[symbol].pct_change().dropna().values
|
||||
hist_data.append(data)
|
||||
|
||||
group_labels = equity_curves.columns
|
||||
|
||||
fig = ff.create_distplot(
|
||||
hist_data, group_labels, bin_size=0.001, show_rug=False, show_hist=False
|
||||
)
|
||||
|
||||
# Add title
|
||||
fig["layout"].update(title="Density Plot of Returns", height=565)
|
||||
|
||||
fig.layout.template = self.theme
|
||||
|
||||
chart_div = plot(fig, output_type="div", include_plotlyjs =False)
|
||||
|
||||
return chart_div
|
||||
|
||||
def scatter_matrix(self, dataframe):
|
||||
data = [
|
||||
dict(label=col, values=round(dataframe[col] * 100, 2)) for col in dataframe
|
||||
]
|
||||
|
||||
color_vals = list(range(dataframe.shape[0]))
|
||||
|
||||
text = [x.strftime("%d %b, %Y") for x in dataframe.index]
|
||||
|
||||
trace1 = go.Splom(
|
||||
dimensions=data,
|
||||
marker=dict(
|
||||
color=color_vals,
|
||||
# colorbar=dict(tickvals= color_vals),
|
||||
size=3,
|
||||
# colorscale='Viridis',
|
||||
line=dict(width=0.5, color="rgb(230,230,230)"),
|
||||
),
|
||||
text=text,
|
||||
diagonal=dict(),
|
||||
)
|
||||
|
||||
axis = dict(showline=True, zeroline=False, gridcolor="#fff", ticklen=4)
|
||||
|
||||
layout = go.Layout(
|
||||
title="",
|
||||
dragmode="select",
|
||||
# width=100%,
|
||||
height=800,
|
||||
autosize=True,
|
||||
hovermode="closest",
|
||||
template=self.theme,
|
||||
)
|
||||
|
||||
fig = dict(data=[trace1], layout=layout)
|
||||
|
||||
# fig.layout.template = self.theme
|
||||
|
||||
chart_div = plot(fig, output_type="div", include_plotlyjs =False)
|
||||
|
||||
return chart_div
|
||||
|
||||
def corr_heatmap(self, returns):
|
||||
|
||||
z = returns.corr().iloc[::-1]
|
||||
x = z.columns
|
||||
y = z.columns[::-1]
|
||||
|
||||
layout = go.Layout(
|
||||
title="",
|
||||
dragmode="select",
|
||||
width=500,
|
||||
height=500,
|
||||
# autosize=True,
|
||||
hovermode="closest",
|
||||
template=self.theme,
|
||||
)
|
||||
|
||||
trace = go.Heatmap(
|
||||
z=z,
|
||||
x=x,
|
||||
y=y,
|
||||
colorscale=[
|
||||
[0.0, "rgb(165,0,38)"],
|
||||
[0.111111111111, "rgb(215,48,39)"],
|
||||
[0.222222222222, "rgb(244,109,67)"],
|
||||
[0.333333333333, "rgb(253,174,97)"],
|
||||
[0.444444444444, "rgb(254,224,144)"],
|
||||
[0.555555555556, "rgb(224,243,248)"],
|
||||
[0.666666666667, "rgb(171,217,233)"],
|
||||
[0.888888888889, "rgb(69,117,180)"],
|
||||
[1.0, "rgb(49,54,149)"],
|
||||
],
|
||||
)
|
||||
data = [trace]
|
||||
|
||||
fig = dict(data=data, layout=layout)
|
||||
|
||||
chart_div = plot(fig, output_type="div", include_plotlyjs =False)
|
||||
|
||||
return chart_div
|
||||
|
||||
def generate_html(self):
|
||||
""" Returns parsed HTML text string for report
|
||||
"""
|
||||
|
||||
goal_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "templates"))
|
||||
|
||||
# with open(os.path.join(goal_dir,'template_v2.0.html'),'w') as f:
|
||||
|
||||
# f.write(HTML_STRING)
|
||||
|
||||
env = Environment(loader=FileSystemLoader(goal_dir), autoescape=True)
|
||||
|
||||
template = env.get_template("template.html")
|
||||
|
||||
years = self.get_years()
|
||||
# header = self.get_header_data()
|
||||
# kpis = self.get_performance_stats()
|
||||
eq_chart = self.plot_eq_chart(
|
||||
self.backtest_list[0].strategy.prices, kind="Equity"
|
||||
)
|
||||
returns_table = self.get_monthly_return_table()
|
||||
|
||||
stats_table = self.get_stats_table_strat()
|
||||
|
||||
nominal_volumes = self.pie_plot(
|
||||
self.get_nominal_volumes(), "(Shares)", "Volume"
|
||||
)
|
||||
value_volumes = self.pie_plot(self.get_value_volumes(), "(Value)", "Volume")
|
||||
|
||||
trade_numbers = self.pie_plot(self.get_trade_numbers(), "", "Trades")
|
||||
|
||||
ind_equity_curves = self.get_individual_equity_curves()
|
||||
ind_returns = self.pie_plot(ind_equity_curves.iloc[-1], "Individual", "Returns")
|
||||
|
||||
dd_chart = self.plot_eq_chart(
|
||||
self.backtest_list[0].strategy.prices, kind="Drawdown"
|
||||
)
|
||||
|
||||
eq_ind_chart = self.plot_eq_chart(ind_equity_curves, kind="Equity", size="half")
|
||||
dd_ind_chart = self.plot_eq_chart(
|
||||
ind_equity_curves, kind="Drawdown", size="half"
|
||||
)
|
||||
|
||||
returns_dist = self.dist_plot(self.backtest_list[0].strategy.prices)
|
||||
ind_returns_dist = self.dist_plot(ind_equity_curves)
|
||||
|
||||
weights_chart = self.plot_eq_chart(
|
||||
self.get_weights().iloc[:, 1:], kind="Weights"
|
||||
)
|
||||
|
||||
stats_table_ind = self.get_stats_table_ind(ind_equity_curves)
|
||||
|
||||
ind_returns_df = self.get_individual_equity_curves().pct_change().fillna(0)
|
||||
ind_scatter_matrix = self.scatter_matrix(ind_returns_df)
|
||||
|
||||
# scatter_matrix_test = self.scatter_matrix2()
|
||||
strat_returns = self.result.prices.pct_change()
|
||||
all_returns = pd.concat([strat_returns, ind_returns_df], axis=1)
|
||||
|
||||
heatmap_corr = self.corr_heatmap(all_returns)
|
||||
|
||||
acf_strat = self.get_acf(self.backtest_list[0].strategy.prices.pct_change())
|
||||
|
||||
acf_chart = self.acf_plot(acf_strat, size="auto")
|
||||
|
||||
# all_numbers = {**header, **kpis}
|
||||
# all_numbers = {eq_chart}
|
||||
html_out = template.render(
|
||||
cdn=self.cdn,
|
||||
years=years,
|
||||
eq_chart=eq_chart,
|
||||
returns_table=returns_table,
|
||||
stats_table=stats_table,
|
||||
heatmap_corr=heatmap_corr,
|
||||
nominal_volumes=nominal_volumes,
|
||||
value_volumes=value_volumes,
|
||||
trade_numbers=trade_numbers,
|
||||
ind_returns=ind_returns,
|
||||
dd_chart=dd_chart,
|
||||
eq_ind_chart=eq_ind_chart,
|
||||
dd_ind_chart=dd_ind_chart,
|
||||
returns_dist=returns_dist,
|
||||
ind_returns_dist=ind_returns_dist,
|
||||
weights_chart=weights_chart,
|
||||
stats_table_ind=stats_table_ind,
|
||||
ind_scatter_matrix=ind_scatter_matrix, # scatter_matrix_test=scatter_matrix_test),
|
||||
acf_chart=acf_chart,
|
||||
)
|
||||
return html_out
|
||||
|
||||
def generate_html_report(
|
||||
self, theme="plotly_dark", cdn="cyborg", output_file="report"
|
||||
):
|
||||
""" Returns HTML report with backtest results
|
||||
"""
|
||||
|
||||
self.cdn = cdns_dict[cdn]
|
||||
self.theme = theme
|
||||
html = self.generate_html()
|
||||
#goal_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "reports"))
|
||||
outfile = os.path.join(os.getcwd(), output_file + ".html")
|
||||
|
||||
file = open(outfile, "w")
|
||||
file.write(html)
|
||||
file.close()
|
||||
msg = "See {} for report with backtest results."
|
||||
print(msg.format(outfile))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
import setuptools
|
||||
|
||||
with open("README.md", "r") as fh:
|
||||
long_description = fh.read()
|
||||
|
||||
setuptools.setup(
|
||||
name="btreport",
|
||||
version="0.0.17",
|
||||
author="Stuart Jamieson",
|
||||
author_email="stuj79@hotmail.com",
|
||||
description="A module to help visualise and analyse the results of a bt module backtest",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/Stuj79/btreport",
|
||||
packages=setuptools.find_packages(),
|
||||
include_package_data=True,
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
)
|
||||
Reference in New Issue
Block a user