plotguy 2.2.2__tar.gz → 2.3.0__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.2 → plotguy-2.3.0}/PKG-INFO +1 -1
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy/__init__.py +44 -6
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy/aggregate.py +1 -1
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy/components.py +54 -127
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy/equity_curves.py +27 -22
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy.egg-info/PKG-INFO +1 -1
- {plotguy-2.2.2 → plotguy-2.3.0}/setup.py +1 -1
- {plotguy-2.2.2 → plotguy-2.3.0}/README.md +0 -0
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy/signals.py +0 -0
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy.egg-info/SOURCES.txt +0 -0
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy.egg-info/dependency_links.txt +0 -0
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy.egg-info/requires.txt +0 -0
- {plotguy-2.2.2 → plotguy-2.3.0}/plotguy.egg-info/top_level.txt +0 -0
- {plotguy-2.2.2 → plotguy-2.3.0}/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 *
|
|
@@ -135,6 +137,8 @@ def save_backtest_result(df, para_combination_item):
|
|
|
135
137
|
### for intraday ###
|
|
136
138
|
if intraday:
|
|
137
139
|
df_daily = df.resample('D').agg({'price': agg_price})
|
|
140
|
+
#df_daily = df.resample('D').agg({'price': agg_price, 'equity_value': agg_price})
|
|
141
|
+
df_daily = df_daily[df_daily['price'].notna()]
|
|
138
142
|
else:
|
|
139
143
|
df_daily = df
|
|
140
144
|
|
|
@@ -196,6 +200,7 @@ def save_backtest_result(df, para_combination_item):
|
|
|
196
200
|
yearly_stats_string += f'{year},year_trade_count:0.00,year_win_rate:00.00,year_return:-00.00|'
|
|
197
201
|
|
|
198
202
|
yearly_stats_string = yearly_stats_string[:-1]
|
|
203
|
+
correlation = 0
|
|
199
204
|
|
|
200
205
|
elif len(df_count) > 0:
|
|
201
206
|
|
|
@@ -246,8 +251,6 @@ def save_backtest_result(df, para_combination_item):
|
|
|
246
251
|
axis=1)
|
|
247
252
|
|
|
248
253
|
yearly_stats_string = "|".join(formatted_rows)
|
|
249
|
-
print(yearly_stats_string)
|
|
250
|
-
print(yearly_stats)
|
|
251
254
|
|
|
252
255
|
######## resample to daily #############
|
|
253
256
|
if 'summary_mode' in para_combination:
|
|
@@ -279,8 +282,11 @@ def save_backtest_result(df, para_combination_item):
|
|
|
279
282
|
equity_net_profit = df.at[df.index[-1], 'equity_value'] - df.at[df.index[0], 'equity_value']
|
|
280
283
|
|
|
281
284
|
equity_return_on_capital = equity_net_profit / df.at[df.index[0], 'equity_value']
|
|
282
|
-
|
|
283
|
-
|
|
285
|
+
if 1 + equity_return_on_capital > 0:
|
|
286
|
+
equity_annualized_return = (np.sign(1 + equity_return_on_capital) * np.abs(1 + equity_return_on_capital)) ** (365 / holding_period_day) - 1
|
|
287
|
+
else:
|
|
288
|
+
equity_annualized_return = -1
|
|
289
|
+
|
|
284
290
|
equity_annualized_std = equity_pct_series.std() * math.sqrt(365)
|
|
285
291
|
equity_annualized_sr = (
|
|
286
292
|
equity_annualized_return - risk_free_rate / 100) / equity_annualized_std if equity_annualized_std != 0 else 0
|
|
@@ -292,8 +298,14 @@ def save_backtest_result(df, para_combination_item):
|
|
|
292
298
|
equity_annualized_sr = round(equity_annualized_sr, 2)
|
|
293
299
|
equity_net_profit_to_mdd = round(100 * equity_net_profit_to_mdd, 2)
|
|
294
300
|
|
|
301
|
+
df['equity_return'] = df['equity_value'].pct_change() * 100
|
|
302
|
+
df['price_return'] = df['price'].pct_change() * 100
|
|
303
|
+
|
|
304
|
+
correlation = df['equity_return'].corr(df['price_return'])
|
|
305
|
+
|
|
295
306
|
if long_short == 'short':
|
|
296
307
|
return_on_capital_diff = equity_annualized_return - (-1) * price_annualized_return
|
|
308
|
+
correlation *= -1
|
|
297
309
|
else:
|
|
298
310
|
### consider both long and long_short ###
|
|
299
311
|
return_on_capital_diff = equity_annualized_return - price_annualized_return
|
|
@@ -313,6 +325,7 @@ def save_backtest_result(df, para_combination_item):
|
|
|
313
325
|
cov_return = round(cov_return, 2)
|
|
314
326
|
cov_count = round(cov_count, 2)
|
|
315
327
|
total_commission = int(round(total_commission))
|
|
328
|
+
correlation = round(correlation, 3)
|
|
316
329
|
|
|
317
330
|
backtest_result_dict = {
|
|
318
331
|
'ref_code': ref_code,
|
|
@@ -344,6 +357,7 @@ def save_backtest_result(df, para_combination_item):
|
|
|
344
357
|
'yearly_stats_string': yearly_stats_string,
|
|
345
358
|
'cov_return': cov_return,
|
|
346
359
|
'cov_count': cov_count,
|
|
360
|
+
'correlation': correlation,
|
|
347
361
|
'total_commission': total_commission
|
|
348
362
|
}
|
|
349
363
|
|
|
@@ -386,16 +400,40 @@ def get_risk_free_rate(start_date, end_date):
|
|
|
386
400
|
# return app
|
|
387
401
|
|
|
388
402
|
|
|
389
|
-
def plot(mode, backtest_result_df = None, number_of_curves=20):
|
|
403
|
+
def plot(mode, backtest_result_df = None, number_of_curves=20, filter_list= []):
|
|
390
404
|
|
|
391
405
|
if mode == 'equity_curves':
|
|
392
|
-
|
|
406
|
+
if len(filter_list) > 0: filter_list = convert_filter_list(filter_list)
|
|
407
|
+
app = equity_curves.Plot(backtest_result_df, number_of_curves, filter_list)
|
|
393
408
|
|
|
394
409
|
if mode == 'aggregate':
|
|
395
410
|
app = aggregate.Aggregate()
|
|
396
411
|
|
|
397
412
|
return app
|
|
398
413
|
|
|
414
|
+
def convert_filter_list(filter_list):
|
|
415
|
+
'''
|
|
416
|
+
filter string as follows
|
|
417
|
+
equity_return_on_capital eg: 50% --> 50
|
|
418
|
+
equity_annualized_return eg: 50% --> 50
|
|
419
|
+
return_on_capital_diff eg: 50% --> 50
|
|
420
|
+
equity_annualized_sr eg: 0.9
|
|
421
|
+
equity_mdd_pct eg: 50% --> 50
|
|
422
|
+
num_of_trade eg: 10
|
|
423
|
+
cov_count eg: 1
|
|
424
|
+
cov_return eg: 1
|
|
425
|
+
correlation eg: 0.5
|
|
426
|
+
win_rate eg: 50% --> 50
|
|
427
|
+
'''
|
|
428
|
+
|
|
429
|
+
for i in range(len(filter_list)):
|
|
430
|
+
filter = filter_list[i]
|
|
431
|
+
pattern = r'([a-zA-Z_]+)([<>=]+)(-?\d+\.?\d*)'
|
|
432
|
+
match = re.match(pattern, filter)
|
|
433
|
+
|
|
434
|
+
filter_list[i] = [match.group(1), match.group(2), match.group(3)]
|
|
435
|
+
|
|
436
|
+
return filter_list
|
|
399
437
|
|
|
400
438
|
def get_latest_fed_fund_rate():
|
|
401
439
|
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
|