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
@@ -0,0 +1,352 @@
1
+ import tkinter as tk
2
+ from tkinter import ttk
3
+ import pandas as pd
4
+ import numpy as np
5
+ from copy import deepcopy
6
+ import webbrowser
7
+ import azapy as az
8
+
9
+ import azapyGUI.config as config
10
+ import azapyGUI.configSettings as configSettings
11
+ import azapyGUI.configModels as configModels
12
+ import azapyGUI.configTips as configTips
13
+ import azapyGUI.configHelps as configHelps
14
+ from azapyGUI.NrShares_table import NrShares_table
15
+ from azapyGUI.modelParametersValidation import _validDateMMDDYYYY, _validFloat
16
+ from azapyGUI.GetMktData import GetMktData
17
+ from azapyGUI.mktDataValidation import mkt_today
18
+ from azapyGUI.DF_Window import DF_Window
19
+ import azapyGUI.azHelper as azHelper
20
+
21
+
22
+ class WeightsWindow():
23
+ def __init__(self, master, pname):
24
+ self._master = master
25
+ self._pname = pname
26
+
27
+ self._rep_show = []
28
+ self._dfw = None
29
+ self._dfts = None
30
+ self._old_param = None
31
+
32
+ self._window = tk.Toplevel()
33
+ self._window.geometry('320x340')
34
+ title = f'Rebalance - {self._pname}'
35
+ self._window.title(title)
36
+ self._window.focus_set()
37
+ self._window.protocol("WM_DELETE_WINDOW", self._btn_cancel)
38
+ self._window.columnconfigure(0, weight=1)
39
+
40
+ menubar = tk.Menu(self._window)
41
+ filemenu = tk.Menu(menubar, tearoff=0)
42
+ filemenu.add_command(label='Help', command=self._menu_help_func)
43
+ menubar.add_cascade(label='Help', menu=filemenu)
44
+ self._window.config(menu=menubar)
45
+
46
+ wwi = 300
47
+ row = 0
48
+ self._window.rowconfigure(row, weight=0)
49
+ frm_port = tk.Frame(master=self._window, width=wwi)
50
+ frm_port.grid(row=row, column=0, sticky=tk.EW, padx=10, pady=10)
51
+
52
+ row += 1
53
+ self._window.rowconfigure(row, weight=1)
54
+ frm_data = tk.Frame(master=self._window, width=wwi)
55
+ frm_data.grid(row=row, column=0, sticky=tk.NSEW, padx=5, pady=5)
56
+
57
+ row += 1
58
+ self._window.rowconfigure(row, weight=0)
59
+ frm_btn = tk.Frame(master=self._window, width=wwi)
60
+ frm_btn.grid(row=row, column=0, sticky=tk.EW, padx=5, pady=5)
61
+
62
+ # on frm_port
63
+ lbl = tk.Label(master=frm_port, text='Portfolio', anchor=tk.W, font=("Forte", 10), width=8)
64
+ lbl.pack(side=tk.LEFT)
65
+
66
+ lbl = tk.Label(master=frm_port, text=self._pname, anchor=tk.W, width=10)
67
+ lbl.pack(side=tk.LEFT)
68
+
69
+ self._ent_asof = tk.StringVar(master=frm_port, value='today')
70
+ ent = tk.Entry(master=frm_port, textvariable=self._ent_asof, validate='key', width=10)
71
+ ent.pack(side=tk.RIGHT)
72
+ ent['validatecommand'] = (ent.register(_validDateMMDDYYYY),'%S','%d','%P')
73
+ config.tiptil.bind(ent, configTips._reb_as_of_tip)
74
+
75
+ lbl = tk.Label(master=frm_port, text='As of', anchor=tk.W, font=("Forte", 10), width=5)
76
+ lbl.pack(side=tk.RIGHT)
77
+
78
+ # on frm_data
79
+ frm_data.rowconfigure(0, weight=1)
80
+ frm_data.columnconfigure(0, weight=1)
81
+ frm_data.columnconfigure(1, weight=0)
82
+
83
+ frm_left = tk.LabelFrame(master=frm_data, text='Reinv. Capital', font=("Forte", 10))
84
+ frm_left.grid(row=0, column=0, sticky=tk.NSEW)
85
+
86
+ frm_right = tk.LabelFrame(master=frm_data, text='Settings', font=("Forte", 10))
87
+ frm_right.grid(row=0, column=1, sticky=tk.NSEW)
88
+
89
+ # on frm_left
90
+ frm_left.columnconfigure(0, weight=1)
91
+ frm_left.columnconfigure(1, weight=1)
92
+
93
+ row = 0
94
+ frm_left.rowconfigure(row, weight=0)
95
+ lbl = tk.Label(master=frm_left, text='Cash', width=5, anchor=tk.W)
96
+ lbl.grid(row=row, column=0, padx=0, pady=5, sticky=tk.W)
97
+
98
+ self._ent_cash = tk.DoubleVar(master=frm_left,
99
+ value=configSettings.MasterApplicationSettings['capital'])
100
+ ent = tk.Entry(master=frm_left, textvariable=self._ent_cash, validate='key', width=8)
101
+ ent.grid(row=row, column=1, padx=0, pady=5, sticky=tk.W, ipadx=3)
102
+ ent['validatecommand'] = (ent.register(_validFloat),'%S','%d','%P')
103
+ config.tiptil.bind(ent, configTips._reb_cash_tip)
104
+
105
+ row += 1
106
+ frm_left.rowconfigure(row, weight=0)
107
+ lbl = tk.Label(master=frm_left, text='Symbol', width=6, anchor=tk.W)
108
+ lbl.grid(row=row, column=0, padx=0, pady=5, sticky=tk.W)
109
+
110
+ lbl = tk.Label(master=frm_left, text='Nr. Shares', width=8, anchor=tk.W)
111
+ lbl.grid(row=row, column=1, padx=0, pady=5, sticky=tk.W)
112
+
113
+ row += 1
114
+ frm_left.rowconfigure(row, weight=1)
115
+ self._symbols = config.PortDataDict[self._pname].symbols
116
+ data = pd.Series(0, index=self._symbols)
117
+ frm_sls = tk.Frame(master=frm_left)
118
+ frm_sls.grid(row=row, column=0, columnspan=2, padx=0, pady=5, sticky=tk.NSEW)
119
+ frm_sls.rowconfigure(0, weight=1)
120
+ frm_sls.columnconfigure(0, weight=1)
121
+ self._frm_symb = NrShares_table(master=frm_sls, data=data)
122
+ self._frm_symb.grid(row=0, column=0, sticky=tk.NSEW)
123
+ frm_sls.update()
124
+
125
+ row += 1
126
+ self._ent_nsh_round = tk.BooleanVar(master=frm_left, value=True)
127
+ cbx = tk.Checkbutton(frm_left, text='NSh Int', variable=self._ent_nsh_round,
128
+ onvalue=True, offvalue=False, width=5, anchor=tk.W)
129
+ cbx.grid(row=row, column=0, padx=0, pady=5, sticky=tk.W)
130
+ config.tiptil.bind(cbx, configTips._reb_nsh_round_tip)
131
+
132
+ btn = tk.Button(master=frm_left, text='Refresh', width=8, command=self._btn_refresh_func)
133
+ btn.grid(row=row, column=1, padx=2, pady=5, sticky=tk.E)
134
+
135
+ # on frm_right
136
+ frm_right.columnconfigure(0, weight=0)
137
+ frm_right.columnconfigure(1, weight=0)
138
+
139
+ row = 0
140
+ lbl = tk.Label(master=frm_right, text="Provider", anchor=tk.W, width=6)
141
+ lbl.grid(row=row, column=0, padx=0, pady=5, sticky=tk.W)
142
+
143
+ pval = list(configSettings.MasterApplicationSettings["Provider"].keys())
144
+ self._ent_provider = ttk.Combobox(master=frm_right, width=12)
145
+ self._ent_provider["values"] = pval
146
+ self._ent_provider.current(0)
147
+ self._ent_provider.grid(row=row, column=1, pady=5, padx=5, sticky=tk.EW)
148
+ config.tiptil.bind(self._ent_provider, configTips._reb_provider_tip)
149
+
150
+ row += 1
151
+ lbl = tk.Label(master=frm_right, text="Force", anchor=tk.W, width=6)
152
+ lbl.grid(row=row, column=0, padx=0, pady=5, sticky=tk.W)
153
+
154
+ self._ent_force = tk.BooleanVar(value=configSettings.MasterApplicationSettings['force'])
155
+ chk_force = tk.Checkbutton(master=frm_right, onvalue=True, offvalue=False, variable=self._ent_force)
156
+ chk_force.grid(row=row, column=1, padx=5, pady=5, sticky=tk.W)
157
+ config.tiptil.bind(chk_force, configTips._reb_force_tip)
158
+
159
+ row += 1
160
+ btn = tk.Button(master=frm_right, text='Weights', width=10, command=self._btn_weights_func)
161
+ btn.grid(row=row, column=1, padx=5, pady=5, sticky=tk.W)
162
+
163
+ # on frm_btn
164
+ frm_btn.rowconfigure(0, weight=1)
165
+ frm_btn.columnconfigure(0, weight=1)
166
+ frm_btn.columnconfigure(1, weight=1)
167
+
168
+ btn = tk.Button(master=frm_btn, text='Close', width=10, command=self._btn_cancel)
169
+ btn.grid(row=0, column=0, sticky=tk.W)
170
+
171
+ btn = tk.Button(master=frm_btn, text="Trading Sheet", width=14, command=self._btn_traiding_sheet_func)
172
+ btn.grid(row=0, column=1, sticky=tk.E)
173
+
174
+
175
+ def _mktdata_info(self):
176
+ # edate
177
+ asof = self._ent_asof.get()
178
+ edate = pd.Timestamp(asof).normalize().tz_localize(None)
179
+ edate = config._bday.rollback(edate).normalize().tz_localize(None)
180
+ mkttoday = mkt_today()
181
+ self._edate = edate if edate < mkttoday else mkttoday
182
+
183
+ # sdate
184
+ model = config.PortDataDict[self._pname]
185
+ hlength = 0
186
+ for mo in model.selectors.values():
187
+ if 'hlength' in mo['param'].keys():
188
+ hlength = max(hlength, mo['param']['hlength'])
189
+ for mo in model.optimizer.values():
190
+ if 'hlength' in mo['param'].keys():
191
+ hlength = max(hlength, mo['param']['hlength'])
192
+ sdate = self._edate - pd.DateOffset(days=np.ceil(hlength * 365.25) + 10)
193
+ self._sdate = config._bday.rollback(sdate).normalize().tz_localize(None)
194
+
195
+ # provider, force
196
+ self._provider = self._ent_provider.get()
197
+ self._force = self._ent_force.get()
198
+
199
+ if self._old_param is not None:
200
+ if [self._edate, self._provider, self._force] == self._old_param:
201
+ return False
202
+ if (self._dfw is not None) and self._dfw.winfo_exists():
203
+ self._dfw.destroy()
204
+ self._old_param = [self._edate, self._provider, self._force]
205
+ return True
206
+
207
+
208
+ def _collectMktData(self):
209
+ gmd = GetMktData(self._provider, self._force)
210
+ gmd.getMkTDataSymb(self._symbols, self._sdate, self._edate)
211
+
212
+ if len(gmd.errorsymb) == 0: return True
213
+
214
+ msg = (f"The following symbols, {gmd.errorsymb},\n"
215
+ f"cannot be retrieved from the provider {self._provider}\n"
216
+ f"Abort the computation.")
217
+ tk.messagebox.showwarning("Warning", message=msg, parent=self._window)
218
+ return False
219
+
220
+
221
+ def _computeWeights(self):
222
+ portdata = config.PortDataDict[self._pname]
223
+ mktdata = {symb: config.MktDataDict[symb].get_mktdata(self._edate).copy() for symb in portdata.symbols}
224
+
225
+ optname = list(portdata.optimizer.keys())[0]
226
+ if configModels.get_comptype(optname) == 'standalone':
227
+ fmname = configModels.get_model_family(optname)
228
+ mname = configModels.portfolio_model_family[fmname][optname]['azapy'][0]
229
+ eff_param = deepcopy(portdata.optimizer[optname]['param'])
230
+ eff_param['sdate'] = self._sdate
231
+ eff_param['edate'] = self._edate
232
+ exec(f"self._pipe = {mname}(**eff_param)")
233
+ else:
234
+ model = []
235
+ sel_list = [None] * len(portdata.selectors.keys())
236
+ for vv in portdata.selectors.values():
237
+ sel_list[vv['index']] = vv
238
+ for vv in sel_list:
239
+ mname = configModels.selector_models[vv['name']]['azapy']
240
+ oo = getattr(az, mname)
241
+ moo = oo(**vv['param'])
242
+ model.append(moo)
243
+
244
+ for vv in portdata.optimizer.values():
245
+ fmname = configModels.get_model_family(vv['name'])
246
+ mname = configModels.portfolio_model_family[fmname][vv['name']]['azapy']
247
+ if mname == 'EWPEngine':
248
+ model.append('EWP')
249
+ else:
250
+ oo = getattr(az, mname)
251
+ moo = oo(**vv['param'])
252
+ model.append(moo)
253
+
254
+ self._pipe = az.ModelPipeline(model)
255
+
256
+ self._weights = self._pipe.getWeights(mktdata, verbose=False)
257
+ if self._pipe.status == 0: return True
258
+
259
+ msg = 'Numerical errors in weights computation. Abort!'
260
+ tk.messagebox.showwarning("Warning", message=msg, parent=self._window)
261
+ return False
262
+
263
+
264
+ def _btn_refresh_func(self):
265
+ self._frm_symb.sort()
266
+
267
+
268
+ def _btn_weights_func(self):
269
+ if self._mktdata_info():
270
+ if not self._collectMktData():
271
+ return
272
+ if not self._computeWeights():
273
+ return
274
+
275
+ df = (pd.DataFrame(self._weights) * 100).round(2)
276
+ df.columns = ['weight']
277
+ df.index.name = 'symbol'
278
+ df.sort_values('weight', ascending=False, inplace=True)
279
+ if '_CASH_' in df.index:
280
+ ix = list(df.index)
281
+ ix.remove('_CASH_')
282
+ df = df.reindex(['_CASH_'] + ix)
283
+ title = self._pname + ' weights as of ' + self._edate.strftime('%Y%m%d')
284
+ geometry = '100x200'
285
+ fname = self._pname + '_ww_' + self._edate.strftime('%Y%m%d')
286
+
287
+ if (self._dfw is not None) and self._dfw.winfo_exists():
288
+ self._dfw.destroy()
289
+ self._dfw = DF_Window(master=self._window,
290
+ df=df,
291
+ title=title,
292
+ geometry=geometry,
293
+ fname=fname)
294
+ self._rep_show.append(self._dfw)
295
+
296
+
297
+ def _ww_destroy(self):
298
+ for ww in self._rep_show:
299
+ if ww.winfo_exists(): ww.destroy()
300
+
301
+
302
+ def _btn_cancel(self):
303
+ self._ww_destroy()
304
+ config.PortDataDict[self._pname].setActive(False)
305
+ config.appPortfolioFrame.refresh()
306
+ self._window.destroy()
307
+
308
+
309
+ def _capital_info(self):
310
+ self._cash = self._ent_cash.get()
311
+ self._nshares = self._frm_symb.get_data()
312
+ self._nsh_round = self._ent_nsh_round.get()
313
+
314
+
315
+ def _btn_traiding_sheet_func(self):
316
+ if self._mktdata_info():
317
+ if not self._collectMktData():
318
+ return
319
+ if not self._computeWeights():
320
+ return
321
+
322
+ self._capital_info()
323
+ tsheet = self._pipe.getPositions(nshares=self._nshares, cash=self._cash,
324
+ nsh_round=self._nsh_round, verbose=False)
325
+ cap = (tsheet['old_nsh'] * tsheet['prices']).sum()
326
+ if cap <= 0:
327
+ msg = "Total calipta, i.e. cash + cash values of shares, must be >0."
328
+ tk.messagebox.showwarning("Woarning", message=msg, parent=self._window)
329
+ return
330
+ tsheet['Allocation'] = (tsheet['new_nsh'] * tsheet['prices']).round(2)
331
+ tsheet.rename(columns={'old_nsh': 'Initial', 'new_nsh': 'Final',
332
+ 'diff_nsh': 'Delta', 'weights': 'Weights',
333
+ 'prices': 'Prices'}, inplace=True)
334
+ tsheet.index.name = 'Symbols'
335
+ title = 'Trading Sheet'
336
+ geometry = '400x200'
337
+ fname = self._pname + '_TS_' + self._edate.strftime('%Y%m%d')
338
+
339
+
340
+ if (self._dfts is not None) and self._dfts.winfo_exists():
341
+ self._dfts.destroy()
342
+ self._dfts = DF_Window(master=self._window,
343
+ df=tsheet,
344
+ title=title,
345
+ geometry=geometry,
346
+ fname=fname)
347
+ self._rep_show.append(self._dfts)
348
+
349
+
350
+ def _menu_help_func(self):
351
+ webbrowser.open_new_tab(configHelps._Rebalance_panel_help)
352
+
azapyGUI/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ from .azapyApp import start
2
+ from .config import __version__
3
+
4
+ def version():
5
+ """Returns **azapyGUI** package version"""
6
+ return __version__
azapyGUI/azHelper.py ADDED
@@ -0,0 +1,27 @@
1
+ import pandas as pd
2
+ import azapy as az
3
+
4
+ import azapyGUI.config as config
5
+
6
+ # need to mode this to azapy
7
+ def schedule_date(sdate, edate, freq='Q', fixoffset=-1, **kwargs):
8
+ qdate = pd.Timestamp(edate).to_period(freq).end_time.normalize().tz_localize(None)
9
+ noffset = -pd.bdate_range(edate, qdate, freq=config._bday).size + 1 - fixoffset
10
+ return az.schedule_simple(sdate=sdate, edate=edate, freq=freq,
11
+ noffset=noffset, fixoffset=fixoffset,
12
+ calendar=config.calendar)
13
+
14
+ # need to incorporate into azapy
15
+ def UniversalEngineWrap(mktdata=None, colname='adjusted', freq='M', name='Universal',
16
+ schedule=None, sdate=None, edate='today', noffset=0,
17
+ fixoffset=-1, hlength=12, dirichlet_alpha=None,
18
+ variance_reduction=True, nr_batches=16, mc_paths=100,
19
+ mc_seed=42, verbose=False):
20
+
21
+ fixing_schedule = schedule_date(sdate, edate, freq=freq, fixoffset=fixoffset)
22
+ return az.UniversalEngine(mktdata=mktdata, colname=colname, freq=freq,
23
+ schedule=fixing_schedule,
24
+ sdate=sdate, edate=edate, noffset=noffset, fixoffset=fixoffset, hlength=hlength,
25
+ dirichlet_alpha=dirichlet_alpha, variance_reduction=variance_reduction,
26
+ nr_batches=nr_batches, mc_paths=mc_paths, mc_seed=mc_seed,
27
+ verbose=verbose)
azapyGUI/azapyApp.py ADDED
@@ -0,0 +1,89 @@
1
+ import tkinter as tk
2
+ import pandas as pd
3
+ import base64
4
+ import azapy as az
5
+
6
+ import azapyGUI.config as config
7
+ import azapyGUI.configSettings as configSettings
8
+ from azapyGUI.serviceMasterUserConfig import _readMasterUserConfig
9
+ from azapyGUI.ViewTip import ViewTip
10
+ from azapyGUI.PortfolioFrame import PortfolioFrame
11
+ from azapyGUI.MktDataFrame import MktDataFrame
12
+ from azapyGUI.MenuApp import MenuApp
13
+
14
+ class app:
15
+ def __init__(self):
16
+ self._root = tk.Tk()
17
+ self._on_start()
18
+ self._root.iconphoto(True, config.photo)
19
+ self._root.title("azapy")
20
+ self._root.rowconfigure(0, minsize=300, weight=1)
21
+ self._root.columnconfigure(0, minsize=100, weight=1)
22
+ self._root.protocol("WM_DELETE_WINDOW", self._on_exit)
23
+
24
+ MenuApp(self._root)
25
+ config.appPortfolioFrame = PortfolioFrame(self._root, text="Active Projects", font=("Forte", 10))
26
+ config.appPortfolioFrame.grid(row=0, column=0, padx=5, pady=5, sticky=tk.NSEW)
27
+ config.appMktDataFrame = MktDataFrame(self._root, text="Active Market Data", font=("Forte", 10))
28
+ config.appMktDataFrame.grid(row=0, column=1, padx=5, pady=5, sticky=tk.NSEW)
29
+
30
+
31
+ def run(self):
32
+ self._root.mainloop()
33
+
34
+
35
+ def _on_start(self):
36
+ configSettings.MasterApplicationSettings = _readMasterUserConfig()
37
+ config.MktDataDict = {}
38
+ config.PortDataDict = {}
39
+ config.count_SymbTableEntry = -1
40
+ config.calendar = az.NYSEgen()
41
+ config._bday = pd.offsets.CustomBusinessDay(calendar=config.calendar)
42
+ img = base64.b64decode(config.iconimgdata)
43
+ config.photo = tk.PhotoImage(data=img, master=self._root)
44
+ config.tiptil = ViewTip(self._root)
45
+ config.tiptil.turned(on=configSettings.MasterApplicationSettings["ShowTips"])
46
+
47
+
48
+ def _on_exit(self):
49
+ port_path = configSettings.MasterApplicationSettings["UserPortfolioDirectory"]
50
+ for kk in config.PortDataDict.keys():
51
+ if not config.PortDataDict[kk].saved:
52
+ msg = "There are unsaved portfolios.\nDo you want to save them?"
53
+ res = tk.messagebox.askquestion("Quit", message=msg, parent=self._root)
54
+ if res == 'no':
55
+ self._root.destroy()
56
+ return
57
+ break
58
+
59
+ for kk, port in config.PortDataDict.items():
60
+ if port.saved: continue
61
+ filepath = tk.filedialog.asksaveasfilename(
62
+ defaultextension=".json",
63
+ filetypes=[("Json Files", "*.json")],
64
+ initialdir=port_path,
65
+ initialfile=port.name,
66
+ parent=self._root,
67
+ )
68
+ if filepath:
69
+ port.saved = True
70
+ config.PortDataDict[port.name].writeFile(filepath)
71
+
72
+ self._destroy_all_windows()
73
+ self._root.destroy()
74
+
75
+
76
+ def _destroy_all_windows(self):
77
+ for widget in self._root.winfo_children():
78
+ if isinstance(widget, tk.Toplevel):
79
+ widget.destroy()
80
+
81
+
82
+ def start():
83
+ """Starts azapyGUI application"""
84
+ app().run()
85
+
86
+
87
+ #==============================================================================
88
+ # if __name__ == "__main__":
89
+ # start()
azapyGUI/config.py ADDED
@@ -0,0 +1,35 @@
1
+ __version__ = '0.0.1'
2
+
3
+ MktDataDict = {}
4
+ PortDataDict = {}
5
+
6
+ appPortfolioFrame = None
7
+ appMktDataFrame = None
8
+
9
+ count_SymbTableEntry = -1
10
+
11
+ calendar = None
12
+ _bday = None
13
+
14
+ _months_name = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
15
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16
+
17
+ iconimgdata = b'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAIGNIUk0AAHomA' \
18
+ b'ACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAwUExURQEAAAABAQ' \
19
+ b'ABAAEAAQEBAAAAAAAAAQEBAf8BAP4AAP4BAP8AAP8AAf8BAf4AAf///4idQz8' \
20
+ b'AAAAIdFJOUwAAAAAAAAAAt+dSoQAAAAFiS0dEDxi6ANkAAAAHdElNRQfoAhAW' \
21
+ b'LDbyOhZFAAAAAW9yTlQBz6J3mgAAAJtJREFUCNcBkABv/wABIgMjQAA1VQBYm' \
22
+ b'pq7y5zLsAAlVmVSVhUJlQBVIVUgVVWSgwBlUlUlBlhVkgAFRUVRZsVihgBVVV' \
23
+ b'VlKGABhQBlVFIA1VVVlQAyVlUJJWUltQBVVWLry5y7xgBWNWklViUlxgAlUcV' \
24
+ b'lICcilQAmC1VlVQYWsgBlxlRSUgJWswBcy7iIm4vN5QBgYFYgMmJSIK3tLxlm' \
25
+ b'qSAlAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI0LTAyLTE2VDIyOjQ0OjUzKzAwO' \
26
+ b'jAwsbbnQwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNC0wMi0xNlQyMjo0NDo1My' \
27
+ b'swMDowMMDrX/8AAAAodEVYdGRhdGU6dGltZXN0YW1wADIwMjQtMDItMTZUMjI' \
28
+ b'6NDQ6NTQrMDA6MDBSWUCuAAAAAElFTkSuQmCC'
29
+
30
+ iconphoto = None
31
+
32
+ tiptil = None
33
+
34
+
35
+
@@ -0,0 +1,84 @@
1
+ _Dual_Momentum_model_help = \
2
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#dual-momentum-selector'
3
+
4
+ _Correlation_Clustering_model_help = \
5
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#correlation-clustering-selector'
6
+
7
+ _mCVaR_model_help = \
8
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#mcvar-based-optimizers'
9
+
10
+ _mSMCR_model_help = \
11
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#msmcr-based-optimizers'
12
+
13
+ _mEVaR_model_help = \
14
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#mevar-based-optimizers'
15
+
16
+ _mMAD_model_help = \
17
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#mmad-based-optimizers'
18
+
19
+ _mLSD_model_help = \
20
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#mlsd-based-optimizers'
21
+
22
+ _mBTAD_model_help = \
23
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#mbtad-based-optimizers'
24
+
25
+ _mBTSD_model_help = \
26
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#mbtsd-based-optimizers'
27
+
28
+ _GINI_model_help = \
29
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#gini-based-optimizers'
30
+
31
+ _SD_model_help = \
32
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#standard-deviation-based-optimizers'
33
+
34
+ _MV_model_help = \
35
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#mean-variance-based-optimizers'
36
+
37
+ _Equal_Weighted_model_help = \
38
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#equal-weighted-portfolios'
39
+
40
+ _Inverse_Volatility_model_help = \
41
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#inverse-volatility-portfolios'
42
+
43
+ _Inverse_Variance_model_help = \
44
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#inverse-variance-portfolios'
45
+
46
+ _Inverse_Drawdown_model_help = \
47
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#inverse-drawdown-portfolios'
48
+
49
+ _Kelly_model_help = \
50
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#kellys-optimal-portfolios'
51
+
52
+ _Universal_model_help = \
53
+ 'https://azapygui.readthedocs.io/en/latest/Model_Panels.html#universal-portfolios'
54
+
55
+ _Portfolio_Edit_help = \
56
+ 'https://azapygui.readthedocs.io/en/latest/Portfolio_Edit_Panel.html'
57
+
58
+ _Settings_panel_help = \
59
+ 'https://azapygui.readthedocs.io/en/latest/Settings_Panel.html'
60
+
61
+ _Backtest_panel_help = \
62
+ 'https://azapygui.readthedocs.io/en/latest/Backtest_Panel.html'
63
+
64
+ _Rebalance_panel_help = \
65
+ 'https://azapygui.readthedocs.io/en/latest/Rebalance_Panel.html'
66
+
67
+ _Statistics_panel_help = \
68
+ 'https://azapygui.readthedocs.io/en/latest/Statistics_Panel.html'
69
+
70
+ _About_help = \
71
+ """
72
+ azapyGUI is a graphical interface for
73
+ azapy portfolio optimizations library\n
74
+ Distributed under GNU General Public License v3 (GPLv3)\n
75
+ Author: Mircea Marinescu\n
76
+ azapyGUI version: {vgui}
77
+ azapy version: {v}
78
+ """
79
+
80
+ _Quick_Start_help = \
81
+ 'https://azapygui.readthedocs.io/en/latest/Quick_Start.html'
82
+
83
+ _index_help = \
84
+ 'https://azapygui.readthedocs.io/en/latest/index.html#'