plotguy 2.2.4__tar.gz → 2.3.1__tar.gz
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.
- {plotguy-2.2.4 → plotguy-2.3.1}/PKG-INFO +1 -1
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy/__init__.py +62 -4
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy/aggregate.py +1 -1
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy/components.py +54 -127
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy/equity_curves.py +27 -22
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy.egg-info/PKG-INFO +1 -1
- {plotguy-2.2.4 → plotguy-2.3.1}/setup.py +1 -1
- {plotguy-2.2.4 → plotguy-2.3.1}/README.md +0 -0
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy/signals.py +0 -0
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy.egg-info/SOURCES.txt +0 -0
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy.egg-info/dependency_links.txt +0 -0
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy.egg-info/requires.txt +0 -0
- {plotguy-2.2.4 → plotguy-2.3.1}/plotguy.egg-info/top_level.txt +0 -0
- {plotguy-2.2.4 → plotguy-2.3.1}/setup.cfg +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
warnings.simplefilter("ignore", UserWarning)
|
|
3
|
+
#warnings.simplefilter("ignore", RuntimeWarning)
|
|
3
4
|
|
|
4
5
|
import datetime
|
|
5
6
|
import os
|
|
@@ -12,6 +13,7 @@ import requests
|
|
|
12
13
|
import pandas as pd
|
|
13
14
|
import numpy as np
|
|
14
15
|
import io
|
|
16
|
+
import re
|
|
15
17
|
|
|
16
18
|
from .equity_curves import *
|
|
17
19
|
from .signals import *
|
|
@@ -22,6 +24,24 @@ from bs4 import BeautifulSoup
|
|
|
22
24
|
|
|
23
25
|
def get_all_para_combination(para_dict, backtest_attribute, df_dict, sec_profile, manager_list, count_down=False):
|
|
24
26
|
|
|
27
|
+
key_to_keep = ['start_date',
|
|
28
|
+
'end_date',
|
|
29
|
+
'freq',
|
|
30
|
+
'market',
|
|
31
|
+
'sectype',
|
|
32
|
+
'initial_capital',
|
|
33
|
+
'data_folder',
|
|
34
|
+
'equity_curve_folder',
|
|
35
|
+
'secondary_data_folder',
|
|
36
|
+
'backtest_name',
|
|
37
|
+
'default_market_price',
|
|
38
|
+
'summary_mode']
|
|
39
|
+
|
|
40
|
+
for k in backtest_attribute.copy().keys():
|
|
41
|
+
if k not in key_to_keep:
|
|
42
|
+
del backtest_attribute[k]
|
|
43
|
+
|
|
44
|
+
|
|
25
45
|
risk_free_rate = get_risk_free_rate(backtest_attribute['start_date'], backtest_attribute['end_date'])
|
|
26
46
|
|
|
27
47
|
freq = backtest_attribute['freq']
|
|
@@ -135,6 +155,8 @@ def save_backtest_result(df, para_combination_item):
|
|
|
135
155
|
### for intraday ###
|
|
136
156
|
if intraday:
|
|
137
157
|
df_daily = df.resample('D').agg({'price': agg_price})
|
|
158
|
+
#df_daily = df.resample('D').agg({'price': agg_price, 'equity_value': agg_price})
|
|
159
|
+
df_daily = df_daily[df_daily['price'].notna()]
|
|
138
160
|
else:
|
|
139
161
|
df_daily = df
|
|
140
162
|
|
|
@@ -196,6 +218,7 @@ def save_backtest_result(df, para_combination_item):
|
|
|
196
218
|
yearly_stats_string += f'{year},year_trade_count:0.00,year_win_rate:00.00,year_return:-00.00|'
|
|
197
219
|
|
|
198
220
|
yearly_stats_string = yearly_stats_string[:-1]
|
|
221
|
+
correlation = 0
|
|
199
222
|
|
|
200
223
|
elif len(df_count) > 0:
|
|
201
224
|
|
|
@@ -277,8 +300,11 @@ def save_backtest_result(df, para_combination_item):
|
|
|
277
300
|
equity_net_profit = df.at[df.index[-1], 'equity_value'] - df.at[df.index[0], 'equity_value']
|
|
278
301
|
|
|
279
302
|
equity_return_on_capital = equity_net_profit / df.at[df.index[0], 'equity_value']
|
|
280
|
-
|
|
281
|
-
|
|
303
|
+
if 1 + equity_return_on_capital > 0:
|
|
304
|
+
equity_annualized_return = (np.sign(1 + equity_return_on_capital) * np.abs(1 + equity_return_on_capital)) ** (365 / holding_period_day) - 1
|
|
305
|
+
else:
|
|
306
|
+
equity_annualized_return = -1
|
|
307
|
+
|
|
282
308
|
equity_annualized_std = equity_pct_series.std() * math.sqrt(365)
|
|
283
309
|
equity_annualized_sr = (
|
|
284
310
|
equity_annualized_return - risk_free_rate / 100) / equity_annualized_std if equity_annualized_std != 0 else 0
|
|
@@ -290,8 +316,14 @@ def save_backtest_result(df, para_combination_item):
|
|
|
290
316
|
equity_annualized_sr = round(equity_annualized_sr, 2)
|
|
291
317
|
equity_net_profit_to_mdd = round(100 * equity_net_profit_to_mdd, 2)
|
|
292
318
|
|
|
319
|
+
df['equity_return'] = df['equity_value'].pct_change() * 100
|
|
320
|
+
df['price_return'] = df['price'].pct_change() * 100
|
|
321
|
+
|
|
322
|
+
correlation = df['equity_return'].corr(df['price_return'])
|
|
323
|
+
|
|
293
324
|
if long_short == 'short':
|
|
294
325
|
return_on_capital_diff = equity_annualized_return - (-1) * price_annualized_return
|
|
326
|
+
correlation *= -1
|
|
295
327
|
else:
|
|
296
328
|
### consider both long and long_short ###
|
|
297
329
|
return_on_capital_diff = equity_annualized_return - price_annualized_return
|
|
@@ -311,6 +343,7 @@ def save_backtest_result(df, para_combination_item):
|
|
|
311
343
|
cov_return = round(cov_return, 2)
|
|
312
344
|
cov_count = round(cov_count, 2)
|
|
313
345
|
total_commission = int(round(total_commission))
|
|
346
|
+
correlation = round(correlation, 3)
|
|
314
347
|
|
|
315
348
|
backtest_result_dict = {
|
|
316
349
|
'ref_code': ref_code,
|
|
@@ -342,6 +375,7 @@ def save_backtest_result(df, para_combination_item):
|
|
|
342
375
|
'yearly_stats_string': yearly_stats_string,
|
|
343
376
|
'cov_return': cov_return,
|
|
344
377
|
'cov_count': cov_count,
|
|
378
|
+
'correlation': correlation,
|
|
345
379
|
'total_commission': total_commission
|
|
346
380
|
}
|
|
347
381
|
|
|
@@ -384,16 +418,40 @@ def get_risk_free_rate(start_date, end_date):
|
|
|
384
418
|
# return app
|
|
385
419
|
|
|
386
420
|
|
|
387
|
-
def plot(mode, backtest_result_df = None, number_of_curves=20):
|
|
421
|
+
def plot(mode, backtest_result_df = None, number_of_curves=20, filter_list= []):
|
|
388
422
|
|
|
389
423
|
if mode == 'equity_curves':
|
|
390
|
-
|
|
424
|
+
if len(filter_list) > 0: filter_list = convert_filter_list(filter_list)
|
|
425
|
+
app = equity_curves.Plot(backtest_result_df, number_of_curves, filter_list)
|
|
391
426
|
|
|
392
427
|
if mode == 'aggregate':
|
|
393
428
|
app = aggregate.Aggregate()
|
|
394
429
|
|
|
395
430
|
return app
|
|
396
431
|
|
|
432
|
+
def convert_filter_list(filter_list):
|
|
433
|
+
'''
|
|
434
|
+
filter string as follows
|
|
435
|
+
equity_return_on_capital eg: 50% --> 50
|
|
436
|
+
equity_annualized_return eg: 50% --> 50
|
|
437
|
+
return_on_capital_diff eg: 50% --> 50
|
|
438
|
+
equity_annualized_sr eg: 0.9
|
|
439
|
+
equity_mdd_pct eg: 50% --> 50
|
|
440
|
+
num_of_trade eg: 10
|
|
441
|
+
cov_count eg: 1
|
|
442
|
+
cov_return eg: 1
|
|
443
|
+
correlation eg: 0.5
|
|
444
|
+
win_rate eg: 50% --> 50
|
|
445
|
+
'''
|
|
446
|
+
|
|
447
|
+
for i in range(len(filter_list)):
|
|
448
|
+
filter = filter_list[i]
|
|
449
|
+
pattern = r'([a-zA-Z_]+)([<>=]+)(-?\d+\.?\d*)'
|
|
450
|
+
match = re.match(pattern, filter)
|
|
451
|
+
|
|
452
|
+
filter_list[i] = [match.group(1), match.group(2), match.group(3)]
|
|
453
|
+
|
|
454
|
+
return filter_list
|
|
397
455
|
|
|
398
456
|
def get_latest_fed_fund_rate():
|
|
399
457
|
url = "https://fred.stlouisfed.org/series/FEDFUNDS"
|
|
@@ -260,7 +260,7 @@ class Aggregate:
|
|
|
260
260
|
|
|
261
261
|
df_current = pd.DataFrame()
|
|
262
262
|
|
|
263
|
-
df_current[f'{row[
|
|
263
|
+
df_current[f'{row["folder"]}_{row["backtest_name"]}_{i}'] = df["equity_value"]
|
|
264
264
|
df_current.index = df.index
|
|
265
265
|
df_list.append(df_current.copy())
|
|
266
266
|
|
|
@@ -35,7 +35,6 @@ class Components:
|
|
|
35
35
|
children=dbc.Select(id='filter_name',
|
|
36
36
|
placeholder="Select Filter",
|
|
37
37
|
options=[
|
|
38
|
-
{'label': 'Exclude Stock', 'value': 'exclude'},
|
|
39
38
|
{'label': 'Return on Capital >', 'value': 'equity_return_on_capital>'},
|
|
40
39
|
{'label': 'Return on Capital <', 'value': 'equity_return_on_capital<'},
|
|
41
40
|
{'label': 'Annualized Return >', 'value': 'equity_annualized_return>'},
|
|
@@ -52,6 +51,8 @@ class Components:
|
|
|
52
51
|
{'label': 'COV (Count) <', 'value': 'cov_count<'},
|
|
53
52
|
{'label': 'COV (Return) >', 'value': 'cov_return>'},
|
|
54
53
|
{'label': 'COV (Return) <', 'value': 'cov_return<'},
|
|
54
|
+
{'label': 'Correlation <', 'value': 'correlation<'},
|
|
55
|
+
{'label': 'Correlation >', 'value': 'correlation>'},
|
|
55
56
|
{'label': 'Win Rate >', 'value': 'win_rate>'},
|
|
56
57
|
{'label': 'Win Rate <', 'value': 'win_rate<'},
|
|
57
58
|
],
|
|
@@ -176,79 +177,6 @@ class Components:
|
|
|
176
177
|
|
|
177
178
|
return checkbox_div, code_list
|
|
178
179
|
|
|
179
|
-
def update_stat_div(self, period, pct_mean, rise_mean, fall_mean):
|
|
180
|
-
div = html.Div([
|
|
181
|
-
|
|
182
|
-
dbc.Row([
|
|
183
|
-
dbc.Col(width=2),
|
|
184
|
-
dbc.Col(str(period[0]) + ' Days', width=2),
|
|
185
|
-
dbc.Col(str(period[1]) + ' Days', width=2),
|
|
186
|
-
dbc.Col(str(period[2]) + ' Days', width=2),
|
|
187
|
-
dbc.Col(str(period[3]) + ' Days', width=2),
|
|
188
|
-
dbc.Col(str(period[4]) + ' Days', width=2),
|
|
189
|
-
]),
|
|
190
|
-
|
|
191
|
-
dbc.Row([
|
|
192
|
-
dbc.Col('pct_change', width=2),
|
|
193
|
-
dbc.Col(f'{pct_mean[0]}%', width=2),
|
|
194
|
-
dbc.Col(f'{pct_mean[1]}%', width=2),
|
|
195
|
-
dbc.Col(f'{pct_mean[2]}%', width=2),
|
|
196
|
-
dbc.Col(f'{pct_mean[3]}%', width=2),
|
|
197
|
-
dbc.Col(f'{pct_mean[4]}%', width=2),
|
|
198
|
-
]),
|
|
199
|
-
|
|
200
|
-
dbc.Row([
|
|
201
|
-
dbc.Col('max_rise', width=2),
|
|
202
|
-
dbc.Col(f'{rise_mean[0]}%', width=2),
|
|
203
|
-
dbc.Col(f'{rise_mean[1]}%', width=2),
|
|
204
|
-
dbc.Col(f'{rise_mean[2]}%', width=2),
|
|
205
|
-
dbc.Col(f'{rise_mean[3]}%', width=2),
|
|
206
|
-
dbc.Col(f'{rise_mean[4]}%', width=2),
|
|
207
|
-
]),
|
|
208
|
-
|
|
209
|
-
dbc.Row([
|
|
210
|
-
dbc.Col('max_fall', width=2),
|
|
211
|
-
dbc.Col(f'{fall_mean[0]}%', width=2),
|
|
212
|
-
dbc.Col(f'{fall_mean[1]}%', width=2),
|
|
213
|
-
dbc.Col(f'{fall_mean[2]}%', width=2),
|
|
214
|
-
dbc.Col(f'{fall_mean[3]}%', width=2),
|
|
215
|
-
dbc.Col(f'{fall_mean[4]}%', width=2),
|
|
216
|
-
]),
|
|
217
|
-
|
|
218
|
-
])
|
|
219
|
-
|
|
220
|
-
return div
|
|
221
|
-
|
|
222
|
-
def generate_radioitems(self, para_dict):
|
|
223
|
-
radioitems_div = []
|
|
224
|
-
|
|
225
|
-
for i, key in enumerate(para_dict):
|
|
226
|
-
options = para_dict[key]
|
|
227
|
-
|
|
228
|
-
radioitems_div.append(html.Div(key,style={'color': 'Yellow','font-size': '14px'}))
|
|
229
|
-
|
|
230
|
-
radioitems_div.append(dcc.RadioItems(
|
|
231
|
-
id={'type': 'para_radioitems', 'index': i},
|
|
232
|
-
options=[{'label': k,
|
|
233
|
-
'value': k} for k in options],
|
|
234
|
-
labelStyle={'font-size': '13px'},
|
|
235
|
-
inputStyle={'margin-left': '10px', 'margin-right': '5px'}
|
|
236
|
-
|
|
237
|
-
), )
|
|
238
|
-
|
|
239
|
-
radioitems_div.append(html.Div(html.Img(),style={'height':'10px'}))
|
|
240
|
-
|
|
241
|
-
return radioitems_div
|
|
242
|
-
|
|
243
|
-
def selection_title(self, para_dict, values):
|
|
244
|
-
title = []
|
|
245
|
-
for i, key in enumerate(para_dict):
|
|
246
|
-
title.append(dbc.Row(
|
|
247
|
-
[dbc.Col(key, width=6),
|
|
248
|
-
dbc.Col(f'{values[i]}', style={'text-align': 'center'}, width=6)]))
|
|
249
|
-
|
|
250
|
-
return title
|
|
251
|
-
|
|
252
180
|
def update_performance_matrix(self, init_chart, para_keys_list, result_df, line_selected = 0):
|
|
253
181
|
|
|
254
182
|
if init_chart:
|
|
@@ -258,8 +186,6 @@ class Components:
|
|
|
258
186
|
ref_code = result_df.loc[line_selected].ref_code
|
|
259
187
|
|
|
260
188
|
yearly_stats_string = result_df.loc[line_selected]['yearly_stats_string']
|
|
261
|
-
print(result_df)
|
|
262
|
-
print(yearly_stats_string)
|
|
263
189
|
|
|
264
190
|
per_col1 =[]
|
|
265
191
|
per_col2 = []
|
|
@@ -270,6 +196,7 @@ class Components:
|
|
|
270
196
|
per_col_2_3 = [html.Div('BaH',style={'text-align': 'center', 'font-weight': 'bold', 'color':'yellow'})]
|
|
271
197
|
|
|
272
198
|
keys = ['num_of_trade',
|
|
199
|
+
'correlation',
|
|
273
200
|
'equity_net_profit',
|
|
274
201
|
'equity_net_profit_to_mdd',
|
|
275
202
|
'return_on_capital_diff',
|
|
@@ -330,39 +257,57 @@ class Components:
|
|
|
330
257
|
|
|
331
258
|
result_df['risk_free_rate'] = result_df['risk_free_rate'].apply(lambda x: f"{x:.2f} %")
|
|
332
259
|
|
|
260
|
+
div_padding = '5px'
|
|
261
|
+
|
|
333
262
|
per_col_1_1.append(html.Div('Number of Trade', style={'background-color': 'darkblue' }))
|
|
334
|
-
per_col_1_1.append(html.Div('
|
|
335
|
-
per_col_1_1.append(html.Div(
|
|
336
|
-
|
|
337
|
-
per_col_1_1.append(html.Div('
|
|
338
|
-
per_col_1_1.append(html.Div('
|
|
339
|
-
per_col_1_1.append(html.Div(
|
|
340
|
-
|
|
341
|
-
per_col_1_1.append(html.Div('
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
263
|
+
per_col_1_1.append(html.Div('Correlation'))
|
|
264
|
+
per_col_1_1.append(html.Div(style={'height': div_padding}))
|
|
265
|
+
|
|
266
|
+
per_col_1_1.append(html.Div('Net Profit', style={'background-color': 'darkblue' }))
|
|
267
|
+
per_col_1_1.append(html.Div('Net Profit/MDD'))
|
|
268
|
+
per_col_1_1.append(html.Div(style={'height': div_padding}))
|
|
269
|
+
|
|
270
|
+
per_col_1_1.append(html.Div('Return-BaH % Diff.', style={'background-color': 'darkblue' }))
|
|
271
|
+
per_col_1_1.append(html.Div('Win Rate'))
|
|
272
|
+
per_col_1_1.append(html.Div(style={'height': div_padding}))
|
|
273
|
+
|
|
274
|
+
per_col_1_1.append(html.Div('COV (Count)', style={'background-color': 'darkblue' }))
|
|
275
|
+
per_col_1_1.append(html.Div('COV (Return)'))
|
|
276
|
+
per_col_1_1.append(html.Div(style={'height': div_padding}))
|
|
277
|
+
|
|
278
|
+
per_col_1_1.append(html.Div('Total Commission', style={'background-color': 'darkblue' }))
|
|
279
|
+
per_col_1_1.append(html.Div('Risk Free Rate'))
|
|
280
|
+
|
|
281
|
+
per_col_2_1.append(html.Div('Return on Capital', style={'background-color': 'darkblue' }))
|
|
282
|
+
per_col_2_1.append(html.Div('Ann. Return'))
|
|
283
|
+
per_col_2_1.append(html.Div(style={'height': div_padding}))
|
|
284
|
+
per_col_2_1.append(html.Div('Ann. Std', style={'background-color': 'darkblue' }))
|
|
285
|
+
per_col_2_1.append(html.Div('Ann. Sharpe Ratio'))
|
|
286
|
+
per_col_2_1.append(html.Div(style={'height': div_padding}))
|
|
287
|
+
per_col_2_1.append(html.Div('MDD Dollar', style={'background-color': 'darkblue' }))
|
|
288
|
+
per_col_2_1.append(html.Div('MDD Percentage'))
|
|
289
|
+
|
|
290
|
+
for i in range(10):
|
|
351
291
|
content = result_df.iloc[0][keys[i]]
|
|
352
292
|
if i%2 == 1:
|
|
353
293
|
per_col_1_2.append(html.Div(content, style={'text-align': 'center', }))
|
|
294
|
+
per_col_1_2.append(html.Div(style={'height': div_padding}))
|
|
354
295
|
else:
|
|
355
|
-
per_col_1_2.append(html.Div(content, style={'text-align': 'center','background-color': 'darkblue'
|
|
356
|
-
|
|
296
|
+
per_col_1_2.append(html.Div(content, style={'text-align': 'center', 'background-color': 'darkblue'}))
|
|
297
|
+
|
|
298
|
+
for i in range(10, 16):
|
|
357
299
|
content = result_df.iloc[0][keys[i]]
|
|
358
300
|
if i%2 == 1:
|
|
359
301
|
per_col_2_2.append(html.Div(content, style={'text-align': 'center'}),)
|
|
302
|
+
per_col_2_2.append(html.Div(style={'height': div_padding}))
|
|
360
303
|
else:
|
|
361
304
|
per_col_2_2.append(html.Div(content, style={'text-align': 'center', 'background-color': 'darkblue'}),)
|
|
362
|
-
|
|
305
|
+
|
|
306
|
+
for i in range(16, 22):
|
|
363
307
|
content = result_df.iloc[0][keys[i]]
|
|
364
308
|
if i%2 == 1:
|
|
365
309
|
per_col_2_3.append(html.Div(content, style={'text-align': 'center'}))
|
|
310
|
+
per_col_2_3.append(html.Div(style={'height': div_padding}))
|
|
366
311
|
else:
|
|
367
312
|
per_col_2_3.append(html.Div(content, style={'text-align': 'center', 'background-color': 'darkblue'}))
|
|
368
313
|
|
|
@@ -443,12 +388,12 @@ class Components:
|
|
|
443
388
|
for idx , key in enumerate(para_keys_list):
|
|
444
389
|
if idx%2 == 0:
|
|
445
390
|
title.append(dbc.Row(
|
|
446
|
-
[dbc.Col(key,width=6),
|
|
447
|
-
dbc.Col(f'{result_df.iloc[0][key]}',style={ 'text-align': 'center'},width=6)]))
|
|
391
|
+
[dbc.Col(key,width=6, style={ 'background-color': 'darkblue', 'marginBottom': '6px'}),
|
|
392
|
+
dbc.Col(f'{result_df.iloc[0][key]}',style={ 'text-align': 'center', 'marginBottom': '6px', 'background-color': 'darkblue'},width=6)]))
|
|
448
393
|
else:
|
|
449
394
|
title.append(dbc.Row(
|
|
450
|
-
[dbc.Col(key,width=6
|
|
451
|
-
dbc.Col(f'{result_df.iloc[0][key]}',style={ 'text-align': 'center'
|
|
395
|
+
[dbc.Col(key,width=6),
|
|
396
|
+
dbc.Col(f'{result_df.iloc[0][key]}',style={ 'text-align': 'center'},width=6)]))
|
|
452
397
|
|
|
453
398
|
matrix_div = html.Div([
|
|
454
399
|
html.Div(style={'height': '8px', }),
|
|
@@ -457,6 +402,8 @@ class Components:
|
|
|
457
402
|
html.Div('Selected Curve', style={'color': 'Cyan',
|
|
458
403
|
'display': 'inline',
|
|
459
404
|
'font-size': '15px'}),
|
|
405
|
+
html.Div(style={'height': '1px', }),
|
|
406
|
+
|
|
460
407
|
html.Div('', id='save_status',
|
|
461
408
|
style={'color': 'Cyan',
|
|
462
409
|
'display': 'inline',
|
|
@@ -469,10 +416,12 @@ class Components:
|
|
|
469
416
|
html.Div(style={'height': '15px', }),
|
|
470
417
|
|
|
471
418
|
html.Div('Performance', style={'color': 'Cyan', 'font-size': '15px'}),
|
|
419
|
+
html.Div(style={'height': '2px', }),
|
|
472
420
|
html.Div(children=per_col1, style={'font-size': '11px','padding-left':'1px'}),
|
|
473
421
|
html.Div(style={'height': '15px', }),
|
|
474
422
|
|
|
475
423
|
html.Div('Comparison', style={'color': 'Cyan', 'font-size': '15px'}),
|
|
424
|
+
html.Div(style={'height': '1px', }),
|
|
476
425
|
html.Div(children=per_col2, style={'font-size': '11px','padding-right': '10px','padding-left':'1px'}),
|
|
477
426
|
html.Div(style={'height': '15px', }),
|
|
478
427
|
|
|
@@ -507,6 +456,7 @@ class Components:
|
|
|
507
456
|
'cov_return': 'COV (Return)',
|
|
508
457
|
'win_rate':'Win Rate',
|
|
509
458
|
'return_on_capital_diff': 'Return/BnH % Diff.',
|
|
459
|
+
'correlation': 'Correlation',
|
|
510
460
|
#'exclude': 'Exclude',
|
|
511
461
|
}
|
|
512
462
|
|
|
@@ -558,12 +508,11 @@ class Components:
|
|
|
558
508
|
return df_checked
|
|
559
509
|
|
|
560
510
|
def assign_colour(self, profile, degree):
|
|
561
|
-
if profile == 0: rgb = (0, int(252 - 252 * degree),
|
|
511
|
+
if profile == 0: rgb = (0, int(252 - 252 * degree), 0)
|
|
562
512
|
elif profile == 1: rgb = (int(252 - 252 * degree), 252, 0)
|
|
563
|
-
elif profile == 2: rgb = (252, 0, int(252 - 252 * degree))
|
|
564
|
-
elif profile == 3: rgb = (0, 252, int(252 * degree))
|
|
565
|
-
elif profile
|
|
566
|
-
elif profile == 5: rgb = (int(252 * degree), 0, 252)
|
|
513
|
+
elif profile == 2: rgb = (252, 0, int(round((252 - 252 * degree)/3)))
|
|
514
|
+
elif profile == 3: rgb = (0, 252, int(round((252 - 252 * degree)/3)))
|
|
515
|
+
elif profile >= 4: rgb = (252, int(252 * degree), 0)
|
|
567
516
|
return 'rgb' + str(rgb)
|
|
568
517
|
|
|
569
518
|
def generate_main_chart(self, graph_df, backtest_name, equity_curve_folder, para_keys_list):
|
|
@@ -767,28 +716,6 @@ class Components:
|
|
|
767
716
|
|
|
768
717
|
return fig_line
|
|
769
718
|
|
|
770
|
-
### for class signal ###
|
|
771
|
-
def generate_df_stat(self, df, period):
|
|
772
|
-
col_pct = 'pct_change_' + str(period)
|
|
773
|
-
col_rise = 'max_rise_' + str(period)
|
|
774
|
-
col_fall = 'max_fall_' + str(period)
|
|
775
|
-
df_his = df.copy()
|
|
776
|
-
df_his[col_pct] = df_his['close'].pct_change(period)
|
|
777
|
-
df_his[col_pct] = df_his[col_pct].shift(-1 * period)
|
|
778
|
-
df_his[col_pct] = df_his[col_pct] * 100
|
|
779
|
-
df_his[col_pct] = df_his[col_pct].map(lambda x: round(x, 2))
|
|
780
|
-
df_his[col_rise] = (df_his['high'].rolling(period).max().shift(-1 * (period)) / df_his['close']) - 1
|
|
781
|
-
df_his[col_fall] = (df_his['low'].rolling(period).min().shift(-1 * (period)) / df_his['close']) - 1
|
|
782
|
-
|
|
783
|
-
df_his = df_his[df_his['logic'] == 'trade_logic']
|
|
784
|
-
|
|
785
|
-
df_his[col_rise] = df_his[col_rise] * 100
|
|
786
|
-
df_his[col_fall] = df_his[col_fall] * 100
|
|
787
|
-
|
|
788
|
-
df_his[col_rise] = df_his[col_rise].map(lambda x: round(x, 2))
|
|
789
|
-
df_his[col_fall] = df_his[col_fall].map(lambda x: round(x, 2))
|
|
790
|
-
|
|
791
|
-
return round(df_his[col_pct].mean(), 2), round(df_his[col_rise].mean(), 2), round(df_his[col_fall].mean(), 2)
|
|
792
719
|
|
|
793
720
|
def aggregate_df(self):
|
|
794
721
|
all_str_dir = []
|
|
@@ -13,7 +13,7 @@ import os
|
|
|
13
13
|
|
|
14
14
|
class Plot:
|
|
15
15
|
|
|
16
|
-
filter_list = []
|
|
16
|
+
#filter_list = []
|
|
17
17
|
add_filter_btn_count = 0
|
|
18
18
|
main_chart_btn_clicks = 0
|
|
19
19
|
stra_alys_chart_btn_clicks = 0
|
|
@@ -24,7 +24,7 @@ class Plot:
|
|
|
24
24
|
select_all_status = False
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def __new__(self, backtest_result_df, number_of_curves):
|
|
27
|
+
def __new__(self, backtest_result_df, number_of_curves, filter_list= []):
|
|
28
28
|
|
|
29
29
|
self.chart_bg = '#1f2c56'
|
|
30
30
|
self.init_chart = True
|
|
@@ -49,6 +49,9 @@ class Plot:
|
|
|
49
49
|
para_keys_str = self.backtest_result_dict[self.backtest_result_key[0]]['para_keys_str']
|
|
50
50
|
self.para_keys_list = para_keys_str.split('|')
|
|
51
51
|
|
|
52
|
+
self.filter_list = filter_list
|
|
53
|
+
self.init_filter = True if len(self.filter_list) >0 else False
|
|
54
|
+
|
|
52
55
|
components = Components(number_of_curves)
|
|
53
56
|
|
|
54
57
|
self.start_year = datetime.datetime.strptime(self.start_date, '%Y-%m-%d').year
|
|
@@ -116,7 +119,7 @@ class Plot:
|
|
|
116
119
|
dbc.Col(html.Div([
|
|
117
120
|
html.Div(style={'height': '15px', }),
|
|
118
121
|
html.Div('Sorting Method', style={'color': 'Cyan', 'margin-left': '15px', 'font-size': '15px'}),
|
|
119
|
-
html.Div(style={'height': '
|
|
122
|
+
html.Div(style={'height': '22px', }),
|
|
120
123
|
sort_method_dropdown,
|
|
121
124
|
html.Div(style={'height': '15px', }),
|
|
122
125
|
html.Div('Parameters', style={'color': 'Cyan', 'margin-left': '15px', 'font-size': '15px'}),
|
|
@@ -247,7 +250,7 @@ class Plot:
|
|
|
247
250
|
if not add_filter_btn_clicks:
|
|
248
251
|
if not main_chart_btn_click:
|
|
249
252
|
#self.sort_method_current = sort_method
|
|
250
|
-
self.filter_list = []
|
|
253
|
+
#self.filter_list = []
|
|
251
254
|
self.add_filter_btn_count = 0
|
|
252
255
|
self.stra_alys_chart_btn_clicks = 0
|
|
253
256
|
self.save_clicks = 0
|
|
@@ -258,25 +261,27 @@ class Plot:
|
|
|
258
261
|
filter_input_div = filter_input
|
|
259
262
|
|
|
260
263
|
######################################################################
|
|
264
|
+
########################### filter reaction ##########################
|
|
261
265
|
|
|
262
|
-
if add_filter_btn_clicks:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
266
|
+
if add_filter_btn_clicks != None and self.add_filter_btn_count!= None:
|
|
267
|
+
add_filter_btn_clicked = add_filter_btn_clicks > self.add_filter_btn_count
|
|
268
|
+
else:
|
|
269
|
+
add_filter_btn_clicked = False
|
|
270
|
+
|
|
271
|
+
if add_filter_btn_clicked or self.init_filter: ## Add btn Pressed
|
|
272
|
+
if not self.init_filter: self.add_filter_btn_count = add_filter_btn_clicks
|
|
273
|
+
if (filter_name != None and filter_input_value != None) or self.init_filter:
|
|
274
|
+
if not self.init_filter: self.filter_list.append([filter_name[0:-1], filter_name[-1], filter_input_value])
|
|
275
|
+
filter_div = components.update_filter_div(self.filter_list)
|
|
276
|
+
if len(self.filter_list) > 11:
|
|
277
|
+
filter_dropdown_div = filter_dropdown_disabled
|
|
278
|
+
filter_input_div = filter_input_disabled
|
|
279
|
+
_add_filter_save_btn_style = add_filter_save_btn_style_disabled
|
|
280
|
+
else:
|
|
281
|
+
filter_dropdown_div = filter_dropdown
|
|
282
|
+
filter_input_div = filter_input
|
|
283
|
+
self.init_chart = False
|
|
284
|
+
self.init_filter = False
|
|
280
285
|
|
|
281
286
|
else:
|
|
282
287
|
self.add_filter_btn_count = 0 # Necessary for initialization
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|