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,475 @@
1
+ import tkinter as tk
2
+ from tkinter import ttk
3
+ from copy import deepcopy
4
+ import webbrowser
5
+
6
+ import azapyGUI.config as config
7
+ import azapyGUI.configMSG as configMSG
8
+ import azapyGUI.configModels as configModels
9
+ import azapyGUI.configSettings as configSettings
10
+ import azapyGUI.configTips as configTips
11
+ import azapyGUI.configHelps as configHelps
12
+ import azapyGUI.modelParametersValidation as mpv
13
+ from azapyGUI.SymbTableEntry import SymbTableEntry
14
+ from azapyGUI.ModelParamEditWindow import ModelParamEditWindow
15
+ from azapyGUI.PortDataNode import PortDataNode
16
+ from azapyGUI.SymbExtractWindow import SymbExtractWindow
17
+ from azapyGUI.mktDataValidation import sdate_edate_validate
18
+ from azapyGUI.GetMktData import GetMktData
19
+
20
+
21
+ class EditPortfolioWindow:
22
+ def __init__(self, master=None, portfolio=None):
23
+ self._master = master
24
+ if portfolio is not None:
25
+ portfolio.status = 'Edit'
26
+ config.appPortfolioFrame.refresh()
27
+ self._initial_portfolio_name = None if portfolio is None else portfolio.name
28
+ self.portfolio = deepcopy(portfolio) if portfolio is not None else PortDataNode()
29
+
30
+ self._window = tk.Toplevel()
31
+ self._window.geometry("800x400")
32
+ self._window.focus_set()
33
+ self._window.title("Portfolio Edit")
34
+ self._window.protocol("WM_DELETE_WINDOW", self._btn_cancel)
35
+
36
+ menubar = tk.Menu(self._window)
37
+ filemenu = tk.Menu(menubar, tearoff=0)
38
+ filemenu.add_command(label='Help', command=self._menu_help_func)
39
+ menubar.add_cascade(label='Help', menu=filemenu)
40
+ self._window.config(menu=menubar)
41
+
42
+ self._build()
43
+
44
+ self._window.update()
45
+ self._master.wait_window(self._window)
46
+
47
+
48
+ def _build(self):
49
+ frm_name = tk.Frame(master=self._window)
50
+ frm_name.grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
51
+
52
+ lbl_text = "Edit Portfolio: "
53
+ lbl_port = tk.Label(master=frm_name, text=lbl_text, justify='left', font=("Harlow Solid Italic", 12))
54
+ lbl_port.pack(side='left', pady=5, padx=2)
55
+ self._ent_name = tk.Entry(master=frm_name, width=30)
56
+ self._ent_name.pack(side='right', pady=5, padx=2)
57
+ self._ent_name.insert(0, self.portfolio.name)
58
+ config.tiptil.bind(self._ent_name, configTips._epw_portfolio_name_tip)
59
+
60
+ self._build_symbPanel()
61
+ self._build_modelPanel()
62
+
63
+ frm_btn_done = tk.Frame(master=self._window)
64
+ frm_btn_done.grid(row=2, columnspan=2, padx=5, pady=5, sticky=tk.EW)
65
+
66
+ btn_set = tk.Button(master=frm_btn_done, text=" Set ", width=10, command=self._btn_set)
67
+ btn_set.pack(side=tk.RIGHT, padx=10, pady=5)
68
+ btn_cancel = tk.Button(master=frm_btn_done, text="Cancel", width=10, command=self._btn_cancel)
69
+ btn_cancel.pack(side=tk.LEFT, padx=10, pady=5)
70
+
71
+
72
+ def _build_symbPanel(self):
73
+ # -- symb panel
74
+ symbPanel = tk.LabelFrame(master=self._window, width=300, text="Portfolio components", font=("Forte", 10))
75
+ symbPanel.grid(row=1, column=0, padx=5, pady=5, sticky=tk.NSEW)
76
+
77
+ self._window.rowconfigure(1, weight=1)
78
+ self._window.columnconfigure(0, weight=1)
79
+
80
+ frm_symb_btn = tk.Frame(master=symbPanel)
81
+ frm_symb_btn.grid(row=0, column=0, sticky=tk.EW)
82
+
83
+ btn_refresh = tk.Button(master=frm_symb_btn, text="Refresh", width=8, command=self._btn_refresh)
84
+ btn_refresh.pack(side=tk.LEFT, padx=2, pady=5)
85
+
86
+ btn_validate = tk.Button(master=frm_symb_btn, text="Validate", width=8, command=self._btn_symb_validate)
87
+ btn_validate.pack(side=tk.RIGHT, padx=2, pady=5)
88
+
89
+ self._symb_table = SymbTableEntry(master=symbPanel, )
90
+ self._symb_table.grid(row=1, column=0, sticky=tk.NSEW)
91
+ symbPanel.rowconfigure(1, weight=1)
92
+ symbPanel.columnconfigure(0, weight=1)
93
+ self._symb_table.write_order(self.portfolio.symbols)
94
+
95
+ self._window.bind('<KeyPress>', self._symb_table.key_press)
96
+
97
+
98
+ def _build_modelPanel(self):
99
+ # -- model panel
100
+ modelPanel = tk.LabelFrame(master=self._window, text="Optimization model", width=400, font=("Forte", 10))
101
+ modelPanel.grid(row=1, column=1, padx=5, pady=5, sticky=tk.NSEW)
102
+ self._window.columnconfigure(1, weight=1)
103
+
104
+ frm_choice = tk.Frame(master=modelPanel)
105
+ frm_choice.pack(side='left', pady=2, expand=True, fill=tk.Y)
106
+
107
+ tk.Label(master=frm_choice, text="Selector:").pack(padx=2, pady=2, side='top', anchor="w")
108
+ self._cbx_selector = ttk.Combobox(master=frm_choice, width=20, state='readonly')
109
+ self._cbx_selector['values'] = tuple(configModels.selector_models.keys())
110
+ self._cbx_selector.current(0)
111
+ self._cbx_selector.pack(pady=5, padx=5, side='top',)
112
+ self._cbx_selector.bind("<<ComboboxSelected>>", self._cbx_selector_func)
113
+
114
+ tk.Label(master=frm_choice, text="Optimization type:").pack(padx=2, pady=2, side='top', anchor="w")
115
+ self._cbx_optim_type = ttk.Combobox(master=frm_choice, width=20, state='readonly')
116
+ self._cbx_optim_type['values'] = tuple(configModels.optim_model_types)
117
+ self._cbx_optim_type.current(0)
118
+ self._cbx_optim_type.pack(pady=5, padx=5, side='top',)
119
+ self._cbx_optim_type.bind("<<ComboboxSelected>>", self._cbx_optim_type_func)
120
+
121
+ tk.Label(master=frm_choice, text="Optimization model:").pack(padx=2, pady=2, side='top', anchor="w")
122
+ self._cbx_optim_model = ttk.Combobox(master=frm_choice, width=20, state='readonly')
123
+ self._cbx_optim_model['values'] = tuple(configModels.risk_based_models.keys())
124
+ self._cbx_optim_model.current(0)
125
+ self._cbx_optim_model.pack(pady=5, padx=5, side='top',)
126
+ self._cbx_optim_model.bind("<<ComboboxSelected>>", self._cbx_optim_model_func)
127
+
128
+ frm_view = tk.Frame(master=modelPanel, width=150)
129
+ frm_view.pack(side='right', expand=True, pady=2, fill=tk.BOTH)
130
+
131
+ self._tree_model = ttk.Treeview(master=frm_view, selectmode="browse", show="tree")
132
+ self._tree_model.pack(padx=0, pady=0, expand=True, side=tk.LEFT, fill=tk.BOTH)
133
+ self._tree_model.tag_bind("m_selector", "<<TreeviewSelect>>", self._tree_model_item_selector_func)
134
+ self._tree_model.tag_bind("m_optimizer", "<<TreeviewSelect>>", self._tree_model_item_optimizer_func)
135
+
136
+ vscrlb = ttk.Scrollbar(master=frm_view,
137
+ orient ="vertical",
138
+ command = self._tree_model.yview)
139
+ vscrlb.pack(padx=0, pady=2, side=tk.RIGHT, fill=tk.Y)
140
+ self._tree_model.configure(yscrollcommand = vscrlb.set)
141
+ self._tree_model_write()
142
+
143
+ self._set_tree_menu_selector()
144
+ self._set_tree_menu_optimizer()
145
+
146
+
147
+ def _tree_model_write(self):
148
+ for name, selector in self.portfolio.selectors.items():
149
+ item = self._tree_model.insert("", selector['index'], text=name, tags=("m_selector",))
150
+ for kk, vv in selector['param'].items():
151
+ self._tree_model.insert(item, tk.END, text=kk +" =" + str(vv))
152
+
153
+ for name, optimizer in self.portfolio.optimizer.items():
154
+ item = self._tree_model.insert("", tk.END, text=name, tags=("m_optimizer",))
155
+ for kk, vv in optimizer['param'].items():
156
+ self._tree_model.insert(item, tk.END, text=kk + " = " + str(vv))
157
+
158
+
159
+ def _set_tree_menu_selector(self):
160
+ self._tree_menu_selector = tk.Menu(self._tree_model, tearoff = 0)
161
+ self._tree_menu_selector.add_command(label ="Edit", command=self._tree_menu_selector_edit_func)
162
+ self._tree_menu_selector.add_command(label ="Move Up", command=self._tree_menu_selector_moveup_func)
163
+ self._tree_menu_selector.add_command(label ="Move Down", command=self._tree_menu_selector_movedown_func)
164
+ self._tree_menu_selector.add_separator()
165
+ self._tree_menu_selector.add_command(label ="Delete", command=self._tree_menu_selector_delete_func)
166
+
167
+
168
+ def _tree_menu_selector_edit_func(self):
169
+ iid = self._tree_model.selection()[0]
170
+ model = self._tree_model.item(iid)['text']
171
+
172
+ selw = ModelParamEditWindow(model_name=model,
173
+ param=self.portfolio.get_selector(model)['param'],
174
+ master=self._window)
175
+ param = selw.param
176
+ if param is None: return
177
+
178
+ self.portfolio.update_selector(model, param)
179
+
180
+ for child in self._tree_model.get_children(iid):
181
+ self._tree_model.delete(child)
182
+
183
+ invisible = configModels.param_default(model, visible=False)
184
+ for kk, vv in param.items():
185
+ if kk in invisible: continue
186
+ self._tree_model.insert(iid, tk.END, text=kk +" = "+ str(vv))
187
+
188
+
189
+ def _tree_menu_selector_moveup_func(self):
190
+ item = self._tree_model.selection()[0]
191
+ loc = self._tree_model.index(item)
192
+ item_name = self._tree_model.item(item)['text']
193
+
194
+ self.portfolio.moveUp_selector(item_name)
195
+ self._tree_model.move(item, self._tree_model.parent(item), loc - 1)
196
+
197
+
198
+ def _tree_menu_selector_movedown_func(self):
199
+ item = self._tree_model.selection()[0]
200
+ loc = self._tree_model.index(item)
201
+ new_loc = loc + 1
202
+
203
+ if new_loc >= self.portfolio.number_selector(): return
204
+ item_name = self._tree_model.item(item)['text']
205
+ self.portfolio.moveDown_selector(item_name)
206
+ self._tree_model.move(item, self._tree_model.parent(item), new_loc)
207
+
208
+
209
+ def _tree_menu_selector_delete_func(self):
210
+ item = self._tree_model.selection()[0]
211
+ item_name = self._tree_model.item(item)['text']
212
+ self.portfolio.remove_selector(item_name)
213
+ self._tree_model.delete(item)
214
+
215
+
216
+ def _set_tree_menu_optimizer(self):
217
+ self._tree_menu_optimizer = tk.Menu(self._tree_model, tearoff = 0)
218
+ self._tree_menu_optimizer.add_command(label ="Edit", command=self._tree_menu_optimizer_edit_func)
219
+ self._tree_menu_optimizer.add_separator()
220
+ self._tree_menu_optimizer.add_command(label ="Delete", command=self._tree_menu_optimizer_delete_func)
221
+
222
+
223
+ def _tree_menu_optimizer_edit_func(self):
224
+ iid = self._tree_model.selection()[0]
225
+ model = self._tree_model.item(iid)['text']
226
+
227
+ selw = ModelParamEditWindow(model_name=model,
228
+ param=self.portfolio.get_optimizer()['param'],
229
+ master=self._window)
230
+ param = selw.param
231
+ if param is None: return
232
+
233
+ self.portfolio.update_optimizer(param)
234
+
235
+ for child in self._tree_model.get_children(iid):
236
+ self._tree_model.delete(child)
237
+
238
+ invisible = configModels.param_default(model, visible=False)
239
+ for kk, vv in param.items():
240
+ if kk in invisible: continue
241
+ self._tree_model.insert(iid, tk.END, text=kk +" = "+ str(vv))
242
+
243
+
244
+ def _tree_menu_optimizer_delete_func(self):
245
+ item = self._tree_model.selection()[0]
246
+ self.portfolio.remove_optimizer()
247
+ self._tree_model.delete(item)
248
+
249
+
250
+ def _tree_model_item_selector_func(self, event):
251
+ try:
252
+ self._tree_menu_selector.tk_popup(self._tree_menu_selector.winfo_pointerx(),
253
+ self._tree_menu_selector.winfo_pointery())
254
+ finally:
255
+ self._tree_menu_selector.grab_release()
256
+
257
+
258
+ def _tree_model_item_optimizer_func(self, event):
259
+ try:
260
+ self._tree_menu_optimizer.tk_popup(self._tree_menu_optimizer.winfo_pointerx(),
261
+ self._tree_menu_optimizer.winfo_pointery())
262
+ finally:
263
+ self._tree_menu_optimizer.grab_release()
264
+
265
+
266
+ def _cbx_selector_func(self, event):
267
+ selo = self._cbx_selector.get()
268
+ if selo == 'Null': return
269
+
270
+ for kk in self.portfolio.optimizer.keys():
271
+ if configModels.get_comptype(kk) == 'standalone':
272
+ msg = f"The optimizer {kk} doesn't accept selectors. "
273
+ tk.messagebox.showwarning("Warning", message=msg, parent=self._window)
274
+ return
275
+
276
+ if self.portfolio.is_set_selector(selo):
277
+ tk.messagebox.showwarning("warning", "Already selected", parent=self._window)
278
+ return
279
+
280
+ selw = ModelParamEditWindow(master=self._window, model_name=selo)
281
+ param = selw.param
282
+ if param is None: return
283
+
284
+ self.portfolio.add_selector(name=selo, param=param)
285
+
286
+ loc = self.portfolio.number_selector() - 1
287
+ item = self._tree_model.insert("", loc, text=selo, tags=("m_selector",))
288
+ invisible = configModels.param_default(selo, visible=False)
289
+ for kk, vv in param.items():
290
+ if kk in invisible: continue
291
+ self._tree_model.insert(item, tk.END, text=kk +" = "+ str(vv))
292
+
293
+
294
+ def _cbx_optim_type_func(self, event):
295
+ selo = self._cbx_optim_type.get()
296
+
297
+ if selo == 'Risk based':
298
+ self._cbx_optim_model['values'] = list(configModels.risk_based_models.keys())
299
+ self._cbx_optim_model.current(0)
300
+ elif selo == 'Naive':
301
+ self._cbx_optim_model['values'] = list(configModels.naive_models.keys())
302
+ self._cbx_optim_model.current(0)
303
+ elif selo == 'Greedy':
304
+ self._cbx_optim_model['values'] = list(configModels.greedy_models.keys())
305
+ self._cbx_optim_model.current(0)
306
+
307
+
308
+ def _cbx_optim_model_func(self, event):
309
+ selo = self._cbx_optim_model.get()
310
+
311
+ if self.portfolio.is_set_optimizer():
312
+ msg = "The optimizer is already set. Only one optimizer is allowed."
313
+ tk.messagebox.showwarning("warning", message=msg, parent=self._window)
314
+ return
315
+
316
+ if len(self.portfolio.selectors.keys()) > 0:
317
+ if configModels.get_comptype(selo) == 'standalone':
318
+ msg = (f"The optimizer {selo} doesn’t accept selectors.\n"
319
+ "Do you want to remove the selectors?")
320
+ ans = tk.messagebox.askyesno("Warning", message=msg, parent=self._window)
321
+ if not ans: return
322
+ self._tree_model.delete(*self._tree_model.get_children())
323
+ self.portfolio.selectors = {}
324
+
325
+ selw = ModelParamEditWindow(master=self._window, model_name=selo)
326
+ param = selw.param
327
+ if param is None: return
328
+
329
+ self.portfolio.add_optimizer(name=selo, param=param)
330
+
331
+ item = self._tree_model.insert("", tk.END, text=selo, tags=("m_optimizer",))
332
+ invisible = configModels.param_default(selo, visible=False)
333
+ for kk, vv in param.items():
334
+ if kk in invisible.keys(): continue
335
+ self._tree_model.insert(item, tk.END, text=kk +" = "+ str(vv))
336
+
337
+
338
+ def _btn_refresh(self):
339
+ _, tx = self._symb_table.get()
340
+ self._symb_table.write_order(tx)
341
+
342
+
343
+ def _btn_cancel(self):
344
+ if self._initial_portfolio_name is not None:
345
+ config.PortDataDict[self._initial_portfolio_name].status = 'Set'
346
+ config.appPortfolioFrame.refresh()
347
+
348
+ self._window.grab_release()
349
+ if (self._master is not None) and self._master.winfo_exists():
350
+ self._master.focus_set()
351
+ self._window.destroy()
352
+ self.portfolio = None
353
+
354
+
355
+ def _on_exit(self):
356
+ self._btn_cancel()
357
+
358
+
359
+ def _btn_set(self):
360
+ # portfolio name
361
+ pname = self._ent_name.get()
362
+ status, val = mpv._validate_portfolio_name(pname)
363
+ if not status:
364
+ tk.messagebox.showwarning("Warning", val, parent=self._window)
365
+ return
366
+
367
+ if ((val != self._initial_portfolio_name) and (val in config.PortDataDict.keys())):
368
+ tk.messagebox.showwarning("Warning", configMSG._validate_portfolio_name_exist_msg,
369
+ parent=self._window)
370
+ return
371
+
372
+ self.portfolio.name = val
373
+
374
+ # symbols
375
+ status, tx = self._symb_table.get()
376
+ if (not status) or (len(tx) < 1):
377
+ tk.messagebox.showwarning("Warning", configMSG._validate_symbols_name_msg,
378
+ parent=self._window)
379
+ return
380
+
381
+ # validate symbols
382
+ source = list(configSettings.MasterApplicationSettings["Provider"].keys())[0]
383
+ _, sd, ed = sdate_edate_validate('', 'today')
384
+
385
+ gmd = GetMktData(source, force=False, validate=True)
386
+ gmd.getMkTDataSymb(tx, sd, ed)
387
+ symbols = gmd.symbols
388
+ error_symbols = gmd.errorsymb
389
+
390
+ self._symb_table.write_order(symbols)
391
+ if len(error_symbols) > 0:
392
+ msg = configMSG._validate_symbols_final_msg + '\n' + str(error_symbols)
393
+ tk.messagebox.showwarning("Warning", msg, parent=self._window)
394
+
395
+ if len(symbols) == 0:
396
+ tk.messagebox.showwarning("Warning", configMSG._validate_symbols_nr_msg,
397
+ parent=self._window)
398
+ return
399
+
400
+ self.portfolio.set_symbol(symbols)
401
+ nsymb = self.portfolio.number_symmbols()
402
+
403
+ # selectors
404
+ selectors = self.portfolio.to_list_selector()
405
+ for sel in selectors:
406
+ model_name = sel['name']
407
+ for fun in configModels.selector_models[model_name]['validate']:
408
+ param_name = list(fun.keys())[0]
409
+ if param_name not in sel['param'].keys(): continue
410
+ status, val = list(fun.values())[0](sel['param'], nsymb)
411
+ if not status:
412
+ msg = f"In {model_name}::{param_name}\n{val}"
413
+ tk.messagebox.showwarning("Warning", msg, parent = self._window)
414
+ return
415
+ sel['param'][param_name] = val
416
+ self.portfolio.update_selector(model_name, sel['param'])
417
+
418
+ # optimizer
419
+ optimizer = self.portfolio.get_optimizer()
420
+ if optimizer is None:
421
+ tk.messagebox.showwarning("Warning", configMSG._validate_portfolio_optimizer_msg,
422
+ parent=self._window)
423
+ return
424
+
425
+ model_name = optimizer['name']
426
+ model_family = configModels.get_model_family(model_name)
427
+ for fun in configModels.portfolio_model_family[model_family][model_name]['validate']:
428
+ param_name = list(fun.keys())[0]
429
+ if param_name not in optimizer['param'].keys(): continue
430
+ status, val = list(fun.values())[0](optimizer['param'], nsymb)
431
+ if not status:
432
+ msg = "In " + model_name + "::" + param_name + "\n" + val
433
+ tk.messagebox.showwarning("Warning", msg, parent = self._window)
434
+ return
435
+ optimizer['param'][param_name] = val
436
+ self.portfolio.update_optimizer(optimizer['param'])
437
+
438
+ # set
439
+ self.portfolio.status = "Set"
440
+ self.portfolio.saved = False
441
+ config.PortDataDict[self.portfolio.name] = self.portfolio
442
+ if ((self._initial_portfolio_name is not None) and
443
+ (self._initial_portfolio_name != self.portfolio.name)):
444
+ del config.PortDataDict[self._initial_portfolio_name]
445
+
446
+ config.appPortfolioFrame.refresh()
447
+
448
+ self._window.grab_release()
449
+ if (self._master is not None) and self._master.winfo_exists():
450
+ self._master.focus_set()
451
+ self._window.destroy()
452
+
453
+
454
+ def _menu_help_func(self):
455
+ webbrowser.open_new_tab(configHelps._Portfolio_Edit_help)
456
+
457
+
458
+ def _btn_symb_validate(self):
459
+ _, tx = self._symb_table.get()
460
+ if len(tx) < 1: return True
461
+
462
+ title = "Symbols validation"
463
+ btn_text = "Validate"
464
+ svw = SymbExtractWindow(master=self._window,
465
+ title=title, symbols=tx, btn_text=btn_text,
466
+ validate=True)
467
+
468
+ if len(svw.errorSymb) < 1:
469
+ self._symb_table.write_order(tx)
470
+ return
471
+
472
+ msg = f"These symbols cannot be retrieved.\n{svw.errorSymb}\nDo you want to delete them?"
473
+ askr = tk.messagebox.askyesno("Validation Failed", msg, parent=self._window)
474
+ self._symb_table.write_order(svw.symbols if askr else tx)
475
+
@@ -0,0 +1,35 @@
1
+ import tkinter as tk
2
+ from copy import deepcopy
3
+
4
+ import azapyGUI.config as config
5
+ import azapyGUI.configMSG as configMSG
6
+ from azapyGUI.modelParametersValidation import _validate_portfolio_name
7
+ from azapyGUI.EntryNameWindow import EntryNameWindow
8
+
9
+ class EntryClonePortfolioWindow(EntryNameWindow):
10
+ def __init__(self, master, pname):
11
+ self._pname = pname
12
+
13
+ title = "Portfolio Clone"
14
+ text = f"Target portfolio: {pname}\nNew name:"
15
+ tip_text = "Unique portfolio name (letters + digits + .-_)"
16
+ super().__init__(master=master, title=title, text=text, tip_text=tip_text)
17
+
18
+
19
+ def _btn_save(self):
20
+ nname = self._ent.get()
21
+ status, val = _validate_portfolio_name(nname)
22
+ if status:
23
+ if val in config.PortDataDict.keys():
24
+ tk.messagebox.showwarning("Warning", configMSG._validate_portfolio_name_exist_msg,
25
+ parent=self._window)
26
+ return
27
+ port = deepcopy(config.PortDataDict[self._pname])
28
+ port.name = val
29
+ port.saved = False
30
+ port.status = 'Set'
31
+ config.PortDataDict[val] = port
32
+ config.appPortfolioFrame.refresh()
33
+ self._btn_cancel()
34
+ return
35
+ tk.messagebox.showwarning("Warning", val, parent=self._window)
@@ -0,0 +1,55 @@
1
+ import tkinter as tk
2
+
3
+ import azapyGUI.config as config
4
+
5
+ class EntryNameWindow:
6
+ def __init__(self, master=None, title=None, text=None, tip_text=None, btn_text="OK"):
7
+ self._master = master
8
+
9
+ self._window = tk.Toplevel()
10
+ self._window.geometry("200x150")
11
+ self._window.title(title)
12
+
13
+ frm = tk.LabelFrame(master=self._window, text=title, font=("Forte", 10) )
14
+ frm.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
15
+
16
+ row = 0
17
+ frm.rowconfigure(row, weight=1)
18
+ lbl = tk.Label(master=frm, text=text)
19
+ lbl.grid(row=row, columnspan=2, pady=5, padx=5, sticky=tk.EW)
20
+
21
+ row += 1
22
+ frm.rowconfigure(row, weight=1)
23
+ self._ent = tk.Entry(master=frm, width=20)
24
+ self._ent.grid(row=row, columnspan=2, padx=10, pady=5, sticky=tk.EW)
25
+ self._ent.bind('<Return>', lambda event: self._btn_save())
26
+ self._ent.focus()
27
+ if tip_text is not None:
28
+ config.tiptil.bind(self._ent, tip_text)
29
+
30
+ row += 1
31
+ frm.columnconfigure(0, weight=1)
32
+ btn_calcel = tk.Button(master=frm, text="Cancel", command=self._btn_cancel, width=8)
33
+ btn_calcel.grid(row=row, column=0, padx=5, pady=5, sticky=tk.W)
34
+ frm.columnconfigure(0, weight=1)
35
+
36
+ btn_save = tk.Button(master=frm, text=btn_text, command=self._btn_save, width=8)
37
+ btn_save.grid(row=row, column=1, padx=5, pady=5, sticky=tk.E)
38
+ frm.columnconfigure(1, weight=1)
39
+
40
+ self._window.update()
41
+ if (self._master is not None) and self._master.winfo_exists():
42
+ self._master.wait_window(self._window)
43
+
44
+
45
+ def _btn_cancel(self):
46
+ self._window.grab_release()
47
+ if (self._master is not None) and self._master.winfo_exists():
48
+ self._master.focus_set()
49
+ self._window.destroy()
50
+
51
+
52
+ def _btn_save(self):
53
+ # to be implemented by derived class
54
+ pass
55
+
@@ -0,0 +1,33 @@
1
+ import tkinter as tk
2
+
3
+ import azapyGUI.config as config
4
+ import azapyGUI.configMSG as configMSG
5
+ from azapyGUI.modelParametersValidation import _validate_portfolio_name
6
+ from azapyGUI.EntryNameWindow import EntryNameWindow
7
+
8
+ class EntryRenamePortfolioWindow(EntryNameWindow):
9
+ def __init__(self, master, pname):
10
+ self._pname = pname
11
+
12
+ title = "Portfolio Rename"
13
+ text = f"Old name: {pname}\nNew name:"
14
+ tip_text = "Unique portfolio name (letters + digits + .-_)"
15
+ super().__init__(master=master, title=title, text=text, tip_text=tip_text)
16
+
17
+
18
+ def _btn_save(self):
19
+ nname = self._ent.get()
20
+ status, val = _validate_portfolio_name(nname)
21
+ if status:
22
+ if val in config.PortDataDict.keys():
23
+ tk.messagebox.showwarning("Warning", configMSG._validate_portfolio_name_exist_msg,
24
+ parent=self._window)
25
+ return
26
+ port = config.PortDataDict.pop(self._pname)
27
+ port.name = val
28
+ port.saved = False
29
+ config.PortDataDict[val] = port
30
+ config.appPortfolioFrame.refresh()
31
+ self._btn_cancel()
32
+ return
33
+ tk.messagebox.showwarning("Warning", val, parent=self._window)
azapyGUI/GetMktData.py ADDED
@@ -0,0 +1,85 @@
1
+ import azapy as az
2
+ from copy import deepcopy
3
+
4
+ import azapyGUI.config as config
5
+ import azapyGUI.configSettings as configSettings
6
+ from azapyGUI.MktDataNode import MktDataNode
7
+
8
+
9
+ class GetMktData():
10
+ def __init__(self, source, force=False, validate=False):
11
+ self._validate = validate
12
+
13
+ api_key = None
14
+ param = {}
15
+ for kk, vv in configSettings.MasterApplicationSettings["Provider"][source].items():
16
+ if kk == "key":
17
+ api_key = vv["key"]
18
+ else:
19
+ param[kk] = vv
20
+
21
+ file_dir = configSettings.MasterApplicationSettings["UserMktDataDirectory"]
22
+
23
+ self._symb_req_default = {"symbol": None,
24
+ "sdate": None,
25
+ "edate": None,
26
+ "output_format": 'dict',
27
+ "source": source,
28
+ "force": force,
29
+ "save": True,
30
+ "file_dir": file_dir,
31
+ "file_format": 'csv',
32
+ 'api_key': api_key,
33
+ 'param': param,
34
+ }
35
+ self._mktr = az.MkTreader(verbose=False)
36
+
37
+
38
+ def getMkTDataSymb(self, symbols, sdate, edate):
39
+ # sdate and edate are assumed to be validated
40
+ self._symb_req = deepcopy(self._symb_req_default)
41
+
42
+ self._symb_req["sdate"] = sdate
43
+ self._symb_req["edate"] = edate
44
+
45
+ symb = [sy for sy in symbols if not self._checkCollection(sy, sdate, edate)]
46
+ if len(symb) == 0:
47
+ # all symb in the system - OK nothing else to do
48
+ self.symbols = symbols
49
+ self.errorsymb = []
50
+ return
51
+
52
+ # retrieve symb (not in the system)
53
+ self._symb_req["symbol"] = symb
54
+ mktdata = self._mktr.get(calendar=config.calendar, **self._symb_req)
55
+ if len(mktdata.keys()) == 0:
56
+ # no extraction was possible (all symb are error symbols)
57
+ self.symbols = list(set(symbols) - set(symb))
58
+ self.errorsymb = symb
59
+ return
60
+
61
+ status = self._mktr.get_request_status()
62
+ error_log = self._mktr.get_error_log()
63
+ self.symbols = list(mktdata.keys())
64
+ self.errorsymb = list(set(symb) - set(self.symbols))
65
+
66
+ # put in the collection newly extracted symbols
67
+ for sy in self.symbols:
68
+ mktData = MktDataNode()
69
+ mktData.name = sy
70
+ mktData.mktdata = mktdata[sy].copy()
71
+ mktData.stats = status[sy].copy()
72
+ mktData.error_log = error_log[sy].copy() if sy in error_log.keys() else {}
73
+ config.MktDataDict[sy] = mktData
74
+ config.appMktDataFrame.refresh()
75
+
76
+
77
+ def _checkCollection(self, symb, sdate, edate):
78
+ if symb not in config.MktDataDict.keys(): return False
79
+
80
+ if self._validate: return True
81
+
82
+ if ((self._symb_req["sdate"] >= config.MktDataDict[symb].sdate()) and
83
+ (self._symb_req["edate"] <= config.MktDataDict[symb].edate())): return True
84
+
85
+ return False