azapyGUI 0.0.1__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.
- azapyGUI/AppSettingsPage.py +213 -0
- azapyGUI/AppSettingsPageMisc.py +101 -0
- azapyGUI/AppSettingsWindow.py +52 -0
- azapyGUI/BacktestComputation.py +166 -0
- azapyGUI/BacktestEntryWindow.py +307 -0
- azapyGUI/BacktestMenuPortfolioWindow.py +20 -0
- azapyGUI/CloneMenuPortfolioWindow.py +93 -0
- azapyGUI/CrossHairBCursor.py +80 -0
- azapyGUI/DF_Window.py +63 -0
- azapyGUI/DF_table.py +282 -0
- azapyGUI/EditMenuPortfolioWindow.py +16 -0
- azapyGUI/EditPortfolioWindow.py +475 -0
- azapyGUI/EntryClonePortfolioWindow.py +35 -0
- azapyGUI/EntryNameWindow.py +55 -0
- azapyGUI/EntryRenamePortfolioWindow.py +33 -0
- azapyGUI/GetMktData.py +85 -0
- azapyGUI/MenuApp.py +194 -0
- azapyGUI/MktDataFrame.py +129 -0
- azapyGUI/MktDataNode.py +34 -0
- azapyGUI/ModelParamEditWindow.py +143 -0
- azapyGUI/NrShares_table.py +54 -0
- azapyGUI/PortAnalyseWindow.py +179 -0
- azapyGUI/PortDataNode.py +180 -0
- azapyGUI/PortfolioFrame.py +197 -0
- azapyGUI/RebalanceMenuPortfolioWindow.py +21 -0
- azapyGUI/RemoveMenuPortfolioWindow.py +33 -0
- azapyGUI/SaveMenuPortfolioWindow.py +36 -0
- azapyGUI/Scrollable.py +60 -0
- azapyGUI/SelectOneWindow.py +65 -0
- azapyGUI/SymbAnalyseWindow.py +21 -0
- azapyGUI/SymbExtractWindow.py +129 -0
- azapyGUI/SymbTableEntry.py +109 -0
- azapyGUI/TimeSeriesViewWindow.py +480 -0
- azapyGUI/ViewTip.py +72 -0
- azapyGUI/WeightsWindow.py +352 -0
- azapyGUI/__init__.py +6 -0
- azapyGUI/azHelper.py +27 -0
- azapyGUI/azapyApp.py +89 -0
- azapyGUI/config.py +35 -0
- azapyGUI/configHelps.py +84 -0
- azapyGUI/configMSG.py +194 -0
- azapyGUI/configModels.py +519 -0
- azapyGUI/configPlot.py +70 -0
- azapyGUI/configSettings.py +138 -0
- azapyGUI/configTips.py +240 -0
- azapyGUI/mktDataValidation.py +42 -0
- azapyGUI/modelParametersValidation.py +442 -0
- azapyGUI/serviceMasterUserConfig.py +28 -0
- azapyGUI/tkHelper.py +18 -0
- azapyGUI-0.0.1.dist-info/LICENSE +674 -0
- azapyGUI-0.0.1.dist-info/METADATA +126 -0
- azapyGUI-0.0.1.dist-info/RECORD +54 -0
- azapyGUI-0.0.1.dist-info/WHEEL +5 -0
- azapyGUI-0.0.1.dist-info/top_level.txt +1 -0
azapyGUI/MenuApp.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import filedialog
|
|
3
|
+
import glob
|
|
4
|
+
import os
|
|
5
|
+
import webbrowser
|
|
6
|
+
import azapy as az
|
|
7
|
+
|
|
8
|
+
import azapyGUI.config as config
|
|
9
|
+
import azapyGUI.configSettings as configSettings
|
|
10
|
+
import azapyGUI.configHelps as configHelps
|
|
11
|
+
from azapyGUI.PortDataNode import PortDataNode
|
|
12
|
+
from azapyGUI.EditPortfolioWindow import EditPortfolioWindow
|
|
13
|
+
from azapyGUI.SaveMenuPortfolioWindow import SaveMenuPortfolioWindow
|
|
14
|
+
from azapyGUI.CloneMenuPortfolioWindow import CloneMenuPortfolioWindow
|
|
15
|
+
from azapyGUI.RemoveMenuPortfolioWindow import RemoveMenuPortfolioWindow
|
|
16
|
+
from azapyGUI.EditMenuPortfolioWindow import EditMenuPortfolioWindow
|
|
17
|
+
from azapyGUI.AppSettingsWindow import AppSettingsWindow
|
|
18
|
+
from azapyGUI.SymbExtractWindow import SymbExtractWindow
|
|
19
|
+
from azapyGUI.BacktestMenuPortfolioWindow import BacktestMenuPortfolioWindow
|
|
20
|
+
from azapyGUI.BacktestEntryWindow import BacktestEntryWindow
|
|
21
|
+
from azapyGUI.RebalanceMenuPortfolioWindow import RebalanceMenuPortfolioWindow
|
|
22
|
+
from azapyGUI.WeightsWindow import WeightsWindow
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MenuApp:
|
|
26
|
+
def __init__(self, window):
|
|
27
|
+
self._window = window
|
|
28
|
+
|
|
29
|
+
menubar = tk.Menu(self._window)
|
|
30
|
+
|
|
31
|
+
filemenu = tk.Menu(menubar, tearoff=0)
|
|
32
|
+
filemenu.add_command(label="New", command=self._new_port)
|
|
33
|
+
filemenu.add_command(label="Open", command=self._load_port)
|
|
34
|
+
filemenu.add_command(label="Clone", command=self._clone_port)
|
|
35
|
+
filemenu.add_command(label="Edit", command=self._edit_port)
|
|
36
|
+
filemenu.add_separator()
|
|
37
|
+
filemenu.add_command(label="Backtest", command=self._backtest_port)
|
|
38
|
+
filemenu.add_command(label="Rebalance", command=self._rebalance_port)
|
|
39
|
+
filemenu.add_separator()
|
|
40
|
+
filemenu.add_command(label="Save", command=self._save_port)
|
|
41
|
+
filemenu.add_command(label="Save all", command=self._save_all_port)
|
|
42
|
+
filemenu.add_command(label="Remove", command=self._remove_port)
|
|
43
|
+
filemenu.add_separator()
|
|
44
|
+
filemenu.add_command(label="Exit", command=self._quit)
|
|
45
|
+
|
|
46
|
+
menubar.add_cascade(label="Portfolio", menu=filemenu)
|
|
47
|
+
|
|
48
|
+
editmenu = tk.Menu(menubar, tearoff=0)
|
|
49
|
+
editmenu.add_command(label="Load", command=self._load_symbols)
|
|
50
|
+
editmenu.add_separator()
|
|
51
|
+
editmenu.add_command(label="Clear", command=self._clear_symbols)
|
|
52
|
+
|
|
53
|
+
menubar.add_cascade(label="Mkt Data", menu=editmenu)
|
|
54
|
+
|
|
55
|
+
setingsmenu = tk.Menu(menubar, tearoff=0)
|
|
56
|
+
setingsmenu.add_command(label="Application Settings", command=self._application_settings)
|
|
57
|
+
|
|
58
|
+
menubar.add_cascade(label="Settings", menu=setingsmenu)
|
|
59
|
+
|
|
60
|
+
helpmenu = tk.Menu(menubar, tearoff=0)
|
|
61
|
+
helpmenu.add_command(label="Help Index", command=self._help_index)
|
|
62
|
+
helpmenu.add_command(label="Quick Start", command=self._help_quick_start)
|
|
63
|
+
helpmenu.add_separator()
|
|
64
|
+
helpmenu.add_command(label="About...", command=self._help_about)
|
|
65
|
+
|
|
66
|
+
menubar.add_cascade(label="Help", menu=helpmenu)
|
|
67
|
+
|
|
68
|
+
self._window.config(menu=menubar)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _new_port(self):
|
|
72
|
+
npw = EditPortfolioWindow(master=self._window)
|
|
73
|
+
portfolio = npw.portfolio
|
|
74
|
+
if portfolio is None: return
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _save_port(self):
|
|
78
|
+
SaveMenuPortfolioWindow(master=self._window)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _save_all_port(self):
|
|
82
|
+
port_path = configSettings.MasterApplicationSettings["UserPortfolioDirectory"]
|
|
83
|
+
for kk, port in config.PortDataDict.items():
|
|
84
|
+
if port.saved: continue
|
|
85
|
+
filepath = filedialog.asksaveasfilename(
|
|
86
|
+
defaultextension=".json",
|
|
87
|
+
filetypes=[("Json Files", "*.json")],
|
|
88
|
+
initialdir=port_path,
|
|
89
|
+
initialfile=port.name,
|
|
90
|
+
)
|
|
91
|
+
if not filepath: return
|
|
92
|
+
|
|
93
|
+
port.saved = True
|
|
94
|
+
config.PortDataDict[port.name].writeFile(filepath)
|
|
95
|
+
|
|
96
|
+
config.appPortfolioFrame.refresh()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _load_port(self):
|
|
100
|
+
port_path = configSettings.MasterApplicationSettings["UserPortfolioDirectory"]
|
|
101
|
+
filepaths = tk.filedialog.askopenfilename(
|
|
102
|
+
filetypes=[("Json Files", "*.json")],
|
|
103
|
+
initialdir=port_path,
|
|
104
|
+
title='Portfolio loading',
|
|
105
|
+
multiple=True,
|
|
106
|
+
parent=self._window
|
|
107
|
+
)
|
|
108
|
+
for filepath in filepaths:
|
|
109
|
+
sport = PortDataNode().loadFile(filepath)
|
|
110
|
+
if sport.name in config.PortDataDict.keys():
|
|
111
|
+
tk.messagebox.showwarning(
|
|
112
|
+
"Warning",
|
|
113
|
+
f"Name {sport.name} already in use!\nAbort opening.")
|
|
114
|
+
continue
|
|
115
|
+
sport.status = 'Set'
|
|
116
|
+
config.PortDataDict[sport.name] = sport
|
|
117
|
+
config.appPortfolioFrame.refresh()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _clone_port(self):
|
|
121
|
+
CloneMenuPortfolioWindow(self._window)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _remove_port(self):
|
|
125
|
+
RemoveMenuPortfolioWindow(self._window)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _edit_port(self):
|
|
129
|
+
empw = EditMenuPortfolioWindow(master=self._window)
|
|
130
|
+
if empw.selection is None: return
|
|
131
|
+
EditPortfolioWindow(self._window, portfolio=config.PortDataDict[empw.selection])
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _quit(self):
|
|
135
|
+
for kk in config.PortDataDict.keys():
|
|
136
|
+
if not config.PortDataDict[kk].saved:
|
|
137
|
+
res = tk.messagebox.askquestion(
|
|
138
|
+
"Save portfolio",
|
|
139
|
+
"There are unsaved portfolios.\nDo you want to save them?",
|
|
140
|
+
parent=self._window)
|
|
141
|
+
if res == 'yes': self._save_all_port()
|
|
142
|
+
break
|
|
143
|
+
|
|
144
|
+
self._window.quit()
|
|
145
|
+
self._window.destroy()
|
|
146
|
+
|
|
147
|
+
def _application_settings(self):
|
|
148
|
+
AppSettingsWindow(master=self._window)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _load_symbols(self):
|
|
152
|
+
SymbExtractWindow(master=self._window, title="Download Market Data", entry=True)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _clear_symbols(self):
|
|
156
|
+
path_mkt = configSettings.MasterApplicationSettings["UserMktDataDirectory"]
|
|
157
|
+
if path_mkt is None: return
|
|
158
|
+
title = "Clear Market Data directory?"
|
|
159
|
+
msg = f"Delete all market data files from\n{path_mkt}"
|
|
160
|
+
res = tk.messagebox.askyesno(title, message=msg, parent=self._window)
|
|
161
|
+
if not res: return
|
|
162
|
+
for ff in glob.iglob(path_mkt + '/*'):
|
|
163
|
+
os.remove(ff)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _backtest_port(self):
|
|
167
|
+
bmpw = BacktestMenuPortfolioWindow(master=self._window)
|
|
168
|
+
if bmpw.selection is None:
|
|
169
|
+
return
|
|
170
|
+
config.PortDataDict[bmpw.selection].setActive(True)
|
|
171
|
+
config.appPortfolioFrame.refresh()
|
|
172
|
+
BacktestEntryWindow(master=self._window, pnames=[bmpw.selection])
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _rebalance_port(self):
|
|
176
|
+
rmpw = RebalanceMenuPortfolioWindow(master=self._window)
|
|
177
|
+
if rmpw.selection is None:
|
|
178
|
+
return
|
|
179
|
+
config.PortDataDict[rmpw.selection].setActive(True)
|
|
180
|
+
config.appPortfolioFrame.refresh()
|
|
181
|
+
WeightsWindow(master=self._window, pname=rmpw.selection)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _help_about(self):
|
|
185
|
+
msg = configHelps._About_help.format(vgui=config.__version__, v=az.__version__)
|
|
186
|
+
tk.messagebox.showinfo('About', msg)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _help_index(self):
|
|
190
|
+
webbrowser.open_new_tab(configHelps._index_help)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _help_quick_start(self):
|
|
194
|
+
webbrowser.open_new_tab(configHelps._Quick_Start_help)
|
azapyGUI/MktDataFrame.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
import azapyGUI.config as config
|
|
7
|
+
import azapyGUI.configSettings as configSettings
|
|
8
|
+
from azapyGUI.SymbExtractWindow import SymbExtractWindow
|
|
9
|
+
from azapyGUI.SymbAnalyseWindow import SymbAnalyseWindow
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MktDataFrame(tk.LabelFrame):
|
|
13
|
+
def __init__(self, master, **kwargs):
|
|
14
|
+
self._master = master
|
|
15
|
+
super().__init__(master=self._master, **kwargs)
|
|
16
|
+
self.grid_rowconfigure(0, weight=1)
|
|
17
|
+
|
|
18
|
+
self._trv = ttk.Treeview(master=self, columns=("sdate", "edate", "nrow", "iserror", "status"))
|
|
19
|
+
self._trv.grid(row=0, column=0, sticky=tk.NSEW)
|
|
20
|
+
|
|
21
|
+
self._trv.heading("#0", text="Symbol", command=self._load_symbols)
|
|
22
|
+
self._trv.heading("#1", text="Start Date")
|
|
23
|
+
self._trv.heading("#2", text="End Date")
|
|
24
|
+
self._trv.heading("#3", text="Nr records")
|
|
25
|
+
self._trv.heading("#4", text="Errors")
|
|
26
|
+
self._trv.heading("#5", text="Status")
|
|
27
|
+
|
|
28
|
+
self._trv.column("#0", minwidth=10, width=60, stretch=False)
|
|
29
|
+
self._trv.column("#1", minwidth=10, width=80, stretch=False)
|
|
30
|
+
self._trv.column("#2", minwidth=10, width=80, stretch=False)
|
|
31
|
+
self._trv.column("#3", minwidth=10, width=65, stretch=False)
|
|
32
|
+
self._trv.column("#4", minwidth=10, width=40, stretch=False)
|
|
33
|
+
self._trv.column("#5", minwidth=10, width=40, stretch=False)
|
|
34
|
+
|
|
35
|
+
vscrlb = ttk.Scrollbar(master=self,
|
|
36
|
+
orient ="vertical",
|
|
37
|
+
command = self._trv.yview)
|
|
38
|
+
vscrlb.grid(row=0, column=1, sticky=tk.NS)
|
|
39
|
+
self._trv.configure(yscrollcommand = vscrlb.set)
|
|
40
|
+
|
|
41
|
+
self._trv.tag_bind("trv_tag", "<<TreeviewSelect>>", self._trv_model_item_selector_func)
|
|
42
|
+
|
|
43
|
+
self._set_trv_menu_selector()
|
|
44
|
+
self.refresh()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def refresh(self):
|
|
48
|
+
for item in self._trv.get_children():
|
|
49
|
+
self._trv.delete(item)
|
|
50
|
+
|
|
51
|
+
for kk, val in sorted(config.MktDataDict.items()):
|
|
52
|
+
self._trv.insert(
|
|
53
|
+
"",
|
|
54
|
+
tk.END,
|
|
55
|
+
text=val.name,
|
|
56
|
+
values=(val.sdate().date(), val.edate().date(), val.nrow(),
|
|
57
|
+
"Yes" if val.iserror() else "No",
|
|
58
|
+
"Busy" if val.status else "Free",),
|
|
59
|
+
tags=("trv_tag",),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _set_trv_menu_selector(self):
|
|
64
|
+
self._trv_menu_selector = tk.Menu(self._trv, tearoff = 0)
|
|
65
|
+
self._trv_menu_selector.add_command(label ="Update", command=self._update_func)
|
|
66
|
+
#self._trv_menu_selector.add_command(label ="Stats", command=self._donothing)
|
|
67
|
+
self._trv_menu_selector.add_command(label ="View", command=self._view_fun)
|
|
68
|
+
self._trv_menu_selector.add_command(label ="To Excel", command=self._to_excel_func)
|
|
69
|
+
self._trv_menu_selector.add_separator()
|
|
70
|
+
self._trv_menu_selector.add_command(label ="Remove", command=self._delete_func)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _trv_model_item_selector_func(self, event):
|
|
74
|
+
try:
|
|
75
|
+
self._trv_menu_selector.tk_popup(self._trv_menu_selector.winfo_pointerx(),
|
|
76
|
+
self._trv_menu_selector.winfo_pointery())
|
|
77
|
+
finally:
|
|
78
|
+
self._trv_menu_selector.grab_release()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _delete_func(self):
|
|
82
|
+
items = self._trv.selection()
|
|
83
|
+
for item in items:
|
|
84
|
+
symb = self._trv.item(item, "text")
|
|
85
|
+
del config.MktDataDict[symb]
|
|
86
|
+
self.refresh()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _update_func(self):
|
|
90
|
+
items = self._trv.selection()
|
|
91
|
+
symbols = [self._trv.item(item, "text") for item in items]
|
|
92
|
+
title = "Update Mkt Data"
|
|
93
|
+
btn_text = "Update"
|
|
94
|
+
SymbExtractWindow(master=self._master, title=title,
|
|
95
|
+
symbols=symbols, btn_text=btn_text)
|
|
96
|
+
self.refresh()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _view_fun(self):
|
|
100
|
+
items = self._trv.selection()
|
|
101
|
+
symbols = [self._trv.item(item, "text") for item in items]
|
|
102
|
+
SymbAnalyseWindow(master=self._master, symbols=symbols)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _to_excel_func(self):
|
|
106
|
+
items = self._trv.selection()
|
|
107
|
+
port_path = configSettings.MasterApplicationSettings["UserOutputDirectory"]
|
|
108
|
+
path = tk.filedialog.asksaveasfilename(
|
|
109
|
+
defaultextension=".xlsx",
|
|
110
|
+
filetypes=[("Excel Files", "*.xlsx")],
|
|
111
|
+
initialdir=port_path,
|
|
112
|
+
initialfile="MktDataBook.xlsx",
|
|
113
|
+
parent=self._master,
|
|
114
|
+
)
|
|
115
|
+
if path:
|
|
116
|
+
with pd.ExcelWriter(path, mode='w', engine='xlsxwriter',
|
|
117
|
+
date_format="YYYY-MM-DD",
|
|
118
|
+
datetime_format="YYYY-MM-DD") as writer:
|
|
119
|
+
for item in items:
|
|
120
|
+
symb = self._trv.item(item, "text")
|
|
121
|
+
config.MktDataDict[symb].mktdata.to_excel(writer, sheet_name=symb)
|
|
122
|
+
if configSettings.MasterApplicationSettings["OpenExcel"]:
|
|
123
|
+
try:
|
|
124
|
+
os.system('start EXCEL.EXE ' + path)
|
|
125
|
+
except:
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
def _load_symbols(self):
|
|
129
|
+
SymbExtractWindow(master=self._master, title="Download Market Data", entry=True)
|
azapyGUI/MktDataNode.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import azapy as az
|
|
2
|
+
|
|
3
|
+
class MktDataNode:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self.name = None
|
|
6
|
+
self.mktdata = None
|
|
7
|
+
self.stats = None
|
|
8
|
+
self.status = False
|
|
9
|
+
self._summary = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def sdate(self):
|
|
13
|
+
if self._summary is None: self._summary = az.summary_MkTdata(self.mktdata)
|
|
14
|
+
return self._summary.begin[0]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def edate(self):
|
|
18
|
+
if self._summary is None: self._summary = az.summary_MkTdata(self.mktdata)
|
|
19
|
+
return self._summary.end[0]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def nrow(self):
|
|
23
|
+
if self._summary is None: self._summary = az.summary_MkTdata(self.mktdata)
|
|
24
|
+
return self._summary.length[0]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def iserror(self):
|
|
28
|
+
if self._summary is None: self._summary = az.summary_MkTdata(self.mktdata)
|
|
29
|
+
return (self._summary.na_total[0] + self._summary.cont[0]) > 0
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_mktdata(self, edate=None):
|
|
33
|
+
return self.mktdata if edate is None else self.mktdata[self.mktdata.index <= edate]
|
|
34
|
+
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
import webbrowser
|
|
4
|
+
|
|
5
|
+
import azapyGUI.configModels as configModels
|
|
6
|
+
import azapyGUI.config as config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ModelParamEditWindow:
|
|
10
|
+
def __init__(self, model_name, param=None, model_family=None, master=None):
|
|
11
|
+
self._master = master
|
|
12
|
+
self._model_name = model_name
|
|
13
|
+
self._model_family = model_family if model_family is not None else configModels.get_model_family(self._model_name)
|
|
14
|
+
|
|
15
|
+
pdefault = configModels.param_default(model_name)
|
|
16
|
+
self.param = pdefault if param is None else (pdefault.update(param) or pdefault)
|
|
17
|
+
self._param_invisible = configModels.param_default(model_name, visible=False)
|
|
18
|
+
|
|
19
|
+
self._window = tk.Toplevel()
|
|
20
|
+
str_title = self._model_name + " - Parameters"
|
|
21
|
+
self._window.title(str_title)
|
|
22
|
+
self._window.focus_set()
|
|
23
|
+
self._window.grab_set()
|
|
24
|
+
|
|
25
|
+
menubar = tk.Menu(self._window)
|
|
26
|
+
filemenu = tk.Menu(menubar, tearoff=0)
|
|
27
|
+
filemenu.add_command(label='Help', command=self._menu_help_func)
|
|
28
|
+
menubar.add_cascade(label='Help', menu=filemenu)
|
|
29
|
+
self._window.config(menu=menubar)
|
|
30
|
+
|
|
31
|
+
frm_model = tk.LabelFrame(master=self._window, text=str_title, font=("Forte", 10))
|
|
32
|
+
frm_model.pack(fill=tk.BOTH, padx=5, pady=5, expand=True)
|
|
33
|
+
|
|
34
|
+
self._frm = tk.Frame(master=frm_model)
|
|
35
|
+
self._frm.pack(fill=tk.BOTH, padx=5, pady=5, expand=True)
|
|
36
|
+
self._frm.columnconfigure(1, weight=1)
|
|
37
|
+
|
|
38
|
+
config_param = configModels.portfolio_model_family[self._model_family][self._model_name]['param']
|
|
39
|
+
row = 0
|
|
40
|
+
self._ent_dict = {}
|
|
41
|
+
for kk, vv in config_param.items():
|
|
42
|
+
row = self._insert_item(kk, vv, row)
|
|
43
|
+
|
|
44
|
+
frm_btn = tk.Frame(master=frm_model)
|
|
45
|
+
frm_btn.pack(fill=tk.BOTH, padx=5, pady=5)
|
|
46
|
+
|
|
47
|
+
btn_cancel = tk.Button(master=frm_btn, text="Cancel", width=10, command=self._btn_cancel)
|
|
48
|
+
btn_cancel.pack(side="left", padx=5, pady=5)
|
|
49
|
+
|
|
50
|
+
btn_save = tk.Button(master=frm_btn, text="Save", width=10, command=self._btn_save)
|
|
51
|
+
btn_save.pack(side="right", padx=5, pady=5)
|
|
52
|
+
|
|
53
|
+
self._window.update()
|
|
54
|
+
self._master.wait_window(self._window)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _insert_item(self, kk, vv, row):
|
|
58
|
+
if not vv['visible']: return row
|
|
59
|
+
|
|
60
|
+
lbl = tk.Label(master=self._frm, text=kk, width=10, anchor='w')
|
|
61
|
+
lbl.grid(row=row, column=0, padx=2, pady=0, sticky=tk.W )
|
|
62
|
+
|
|
63
|
+
if 'values' not in vv.keys():
|
|
64
|
+
ent = tk.Entry(master=self._frm, width=15)
|
|
65
|
+
ent.grid(row=row, column=1, padx=2, pady=0, sticky=tk.EW)
|
|
66
|
+
ent.insert(0, str(self.param[kk]))
|
|
67
|
+
config.tiptil.bind(ent, vv['tip'])
|
|
68
|
+
self._ent_dict[kk] = ent
|
|
69
|
+
|
|
70
|
+
self._frm.rowconfigure(row, weight=1)
|
|
71
|
+
else:
|
|
72
|
+
ent = ttk.Combobox(master=self._frm, width=12, state='readonly')
|
|
73
|
+
ent['values'] = vv['values']
|
|
74
|
+
ent.set(str(self.param[kk]))
|
|
75
|
+
ent.grid(row=row, column=1, padx=2, pady=0, sticky=tk.EW )
|
|
76
|
+
config.tiptil.bind(ent, vv['tip'])
|
|
77
|
+
self._ent_dict[kk] = ent
|
|
78
|
+
|
|
79
|
+
self._frm.rowconfigure(row, weight=1)
|
|
80
|
+
|
|
81
|
+
if 'param' in vv.keys():
|
|
82
|
+
row += 1
|
|
83
|
+
ent.bind("<<ComboboxSelected>>", lambda event: self._switch_item(kk=kk, vv=vv, row=row))
|
|
84
|
+
for kkk, vvv in vv['param'].items():
|
|
85
|
+
self._insert_item(kkk, vvv, row)
|
|
86
|
+
self._switch_item(kk, vv, row)
|
|
87
|
+
|
|
88
|
+
return row + 1
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _switch_item(self, kk, vv, row):
|
|
92
|
+
sol = self._ent_dict[kk].get()
|
|
93
|
+
|
|
94
|
+
lbl = tk.Label(master=self._frm, text="", width=10, anchor='w')
|
|
95
|
+
lbl.grid(row=row, column=0, padx=2, pady=0, sticky=tk.W )
|
|
96
|
+
for kkk, vvv in vv['param'].items():
|
|
97
|
+
self._ent_dict[kkk].grid_forget()
|
|
98
|
+
if sol in vvv['conditional']:
|
|
99
|
+
lbl.config(text=kkk)
|
|
100
|
+
self._ent_dict[kkk].grid(row=row, column=1, padx=2, pady=0, sticky=tk.EW)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _btn_cancel(self):
|
|
104
|
+
self._window.grab_release()
|
|
105
|
+
if (self._master is not None) and self._master.winfo_exists():
|
|
106
|
+
self._master.focus_set()
|
|
107
|
+
self._window.destroy()
|
|
108
|
+
self.param = None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _btn_save(self):
|
|
112
|
+
wparam = {}
|
|
113
|
+
for kk in self._ent_dict.keys():
|
|
114
|
+
if self._ent_dict[kk].winfo_ismapped():
|
|
115
|
+
wparam[kk] = self._ent_dict[kk].get()
|
|
116
|
+
|
|
117
|
+
status = self._validate(wparam)
|
|
118
|
+
if not status: return
|
|
119
|
+
|
|
120
|
+
self._param_invisible.update(wparam)
|
|
121
|
+
self.param = self._param_invisible
|
|
122
|
+
self._window.grab_release()
|
|
123
|
+
if (self._master is not None) and self._master.winfo_exists():
|
|
124
|
+
self._master.focus_set()
|
|
125
|
+
self._window.destroy()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _validate(self, param):
|
|
129
|
+
dparam = configModels.flat_param_default(self._model_family, self._model_name)
|
|
130
|
+
lmodel = configModels.portfolio_model_family[self._model_family][self._model_name]['val_priority']
|
|
131
|
+
lmodel += [kk for kk in param.keys() if kk not in lmodel]
|
|
132
|
+
rout = {}
|
|
133
|
+
for kk in lmodel:
|
|
134
|
+
status, rout = dparam[kk]['validate'](param)
|
|
135
|
+
if not status:
|
|
136
|
+
tk.messagebox.showwarning("Warning", rout, parent=self._window)
|
|
137
|
+
return False
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _menu_help_func(self):
|
|
142
|
+
webbrowser.open_new_tab(configModels.portfolio_model_family[self._model_family][self._model_name]['help'])
|
|
143
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from azapyGUI.DF_table import DF_table
|
|
6
|
+
from azapyGUI.modelParametersValidation import _validIntPositive
|
|
7
|
+
|
|
8
|
+
class NrShares_table(DF_table):
|
|
9
|
+
def __init__(self, master, data):
|
|
10
|
+
self._df = pd.DataFrame(data)
|
|
11
|
+
super().__init__(master=master, df=pd.DataFrame(data))
|
|
12
|
+
|
|
13
|
+
def _draw(self, df):
|
|
14
|
+
self._sent = [ [tk.StringVar(self._frm, value=symb),
|
|
15
|
+
tk.IntVar(self._frm, value=df.loc[symb][0])] for symb in df.index]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
self._frm.columnconfigure(0, weight=1)
|
|
19
|
+
self._frm.columnconfigure(1, weight=1)
|
|
20
|
+
for row, ll in enumerate(self._sent):
|
|
21
|
+
self._frm.rowconfigure(row, weight=1)
|
|
22
|
+
lbl = tk.Label(self._frm, textvariable=ll[0], width=5, bg='white', relief=tk.RIDGE, anchor=tk.W)
|
|
23
|
+
lbl.grid(row=row, column=0, sticky=tk.NSEW)
|
|
24
|
+
|
|
25
|
+
ent = tk.Entry(self._frm, textvariable=ll[1], width=5, validate='key')
|
|
26
|
+
ent['validatecommand'] = (ent.register(_validIntPositive),'%S','%d','%P')
|
|
27
|
+
ent.grid(row=row, column=1, sticky=tk.NSEW)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_data(self):
|
|
31
|
+
index = pd.Index([self._sent[kk][0].get() for kk in range(len(self._sent))], dtype=str)
|
|
32
|
+
data = pd.Series([self._sent[kk][1].get() for kk in range(len(self._sent))],
|
|
33
|
+
index=index, dtype=np.dtype(float))
|
|
34
|
+
return data
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def set_data(self, data):
|
|
38
|
+
df = pd.DataFrame(data)
|
|
39
|
+
for row, symb in enumerate(df.index):
|
|
40
|
+
self._sent[row][0].set(symb)
|
|
41
|
+
self._sent[row][1].set(df.loc[symb][0])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def sort(self, axis=1, ascending=False):
|
|
45
|
+
data = self.get_data()
|
|
46
|
+
|
|
47
|
+
if axis == 0:
|
|
48
|
+
self.set_data(data.sort_index(ascending=ascending))
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
self.set_data(pd.DataFrame(data)
|
|
52
|
+
.reset_index()
|
|
53
|
+
.sort_values(by=[0, 'index'], ascending=[ascending, True])
|
|
54
|
+
.set_index('index'))
|