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.
Files changed (54) hide show
  1. azapyGUI/AppSettingsPage.py +213 -0
  2. azapyGUI/AppSettingsPageMisc.py +101 -0
  3. azapyGUI/AppSettingsWindow.py +52 -0
  4. azapyGUI/BacktestComputation.py +166 -0
  5. azapyGUI/BacktestEntryWindow.py +307 -0
  6. azapyGUI/BacktestMenuPortfolioWindow.py +20 -0
  7. azapyGUI/CloneMenuPortfolioWindow.py +93 -0
  8. azapyGUI/CrossHairBCursor.py +80 -0
  9. azapyGUI/DF_Window.py +63 -0
  10. azapyGUI/DF_table.py +282 -0
  11. azapyGUI/EditMenuPortfolioWindow.py +16 -0
  12. azapyGUI/EditPortfolioWindow.py +475 -0
  13. azapyGUI/EntryClonePortfolioWindow.py +35 -0
  14. azapyGUI/EntryNameWindow.py +55 -0
  15. azapyGUI/EntryRenamePortfolioWindow.py +33 -0
  16. azapyGUI/GetMktData.py +85 -0
  17. azapyGUI/MenuApp.py +194 -0
  18. azapyGUI/MktDataFrame.py +129 -0
  19. azapyGUI/MktDataNode.py +34 -0
  20. azapyGUI/ModelParamEditWindow.py +143 -0
  21. azapyGUI/NrShares_table.py +54 -0
  22. azapyGUI/PortAnalyseWindow.py +179 -0
  23. azapyGUI/PortDataNode.py +180 -0
  24. azapyGUI/PortfolioFrame.py +197 -0
  25. azapyGUI/RebalanceMenuPortfolioWindow.py +21 -0
  26. azapyGUI/RemoveMenuPortfolioWindow.py +33 -0
  27. azapyGUI/SaveMenuPortfolioWindow.py +36 -0
  28. azapyGUI/Scrollable.py +60 -0
  29. azapyGUI/SelectOneWindow.py +65 -0
  30. azapyGUI/SymbAnalyseWindow.py +21 -0
  31. azapyGUI/SymbExtractWindow.py +129 -0
  32. azapyGUI/SymbTableEntry.py +109 -0
  33. azapyGUI/TimeSeriesViewWindow.py +480 -0
  34. azapyGUI/ViewTip.py +72 -0
  35. azapyGUI/WeightsWindow.py +352 -0
  36. azapyGUI/__init__.py +6 -0
  37. azapyGUI/azHelper.py +27 -0
  38. azapyGUI/azapyApp.py +89 -0
  39. azapyGUI/config.py +35 -0
  40. azapyGUI/configHelps.py +84 -0
  41. azapyGUI/configMSG.py +194 -0
  42. azapyGUI/configModels.py +519 -0
  43. azapyGUI/configPlot.py +70 -0
  44. azapyGUI/configSettings.py +138 -0
  45. azapyGUI/configTips.py +240 -0
  46. azapyGUI/mktDataValidation.py +42 -0
  47. azapyGUI/modelParametersValidation.py +442 -0
  48. azapyGUI/serviceMasterUserConfig.py +28 -0
  49. azapyGUI/tkHelper.py +18 -0
  50. azapyGUI-0.0.1.dist-info/LICENSE +674 -0
  51. azapyGUI-0.0.1.dist-info/METADATA +126 -0
  52. azapyGUI-0.0.1.dist-info/RECORD +54 -0
  53. azapyGUI-0.0.1.dist-info/WHEEL +5 -0
  54. 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)
@@ -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)
@@ -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'))