azapyGUI 0.0.2__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {azapygui-0.0.2 → azapygui-0.1.1}/PKG-INFO +6 -6
- {azapygui-0.0.2 → azapygui-0.1.1}/README.md +3 -3
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/AppSettingsPage.py +0 -1
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/AppSettingsPageMisc.py +30 -7
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/GetMktData.py +4 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/MktDataNode.py +20 -14
- azapygui-0.1.1/azapyGUI/ViewTip.py +12 -0
- azapygui-0.1.1/azapyGUI/ViewTip_Ubuntu.py +52 -0
- azapygui-0.1.1/azapyGUI/ViewTip_fade.py +73 -0
- azapygui-0.1.1/azapyGUI/ViewTip_fast.py +55 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/azapyApp.py +1 -1
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/config.py +1 -1
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/configSettings.py +21 -2
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/configTips.py +6 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/modelParametersValidation.py +5 -0
- azapygui-0.1.1/azapyGUI/serviceMasterUserConfig.py +42 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI.egg-info/PKG-INFO +6 -6
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI.egg-info/SOURCES.txt +6 -0
- azapygui-0.1.1/azapyGUI.egg-info/requires.txt +7 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/setup.py +3 -3
- azapygui-0.0.2/azapyGUI/ViewTip.py +0 -72
- azapygui-0.0.2/azapyGUI/serviceMasterUserConfig.py +0 -28
- azapygui-0.0.2/azapyGUI.egg-info/requires.txt +0 -5
- {azapygui-0.0.2 → azapygui-0.1.1}/LICENSE +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/AppSettingsWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/BacktestComputation.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/BacktestEntryWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/BacktestMenuPortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/CloneMenuPortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/CrossHairBCursor.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/DF_Window.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/DF_table.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/EditMenuPortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/EditPortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/EntryClonePortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/EntryNameWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/EntryRenamePortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/MenuApp.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/MktDataFrame.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/ModelParamEditWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/NrShares_table.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/PortAnalyseWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/PortDataNode.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/PortfolioFrame.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/RebalanceMenuPortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/RemoveMenuPortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/SaveMenuPortfolioWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/Scrollable.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/SelectOneWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/SymbAnalyseWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/SymbExtractWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/SymbTableEntry.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/TimeSeriesViewWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/WeightsWindow.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/__init__.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/azHelper.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/configHelps.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/configMSG.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/configModels.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/configPlot.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/mktDataValidation.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI/tkHelper.py +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI.egg-info/dependency_links.txt +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/azapyGUI.egg-info/top_level.txt +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/pyproject.toml +0 -0
- {azapygui-0.0.2 → azapygui-0.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: azapyGUI
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: GUI for azapy library - Financial Portfolio Optimization Algorithms
|
|
5
5
|
Home-page: https://github.com/Mircea-MMXXI/azapyGUI.git
|
|
6
6
|
Author: Mircea Marinescu
|
|
@@ -15,11 +15,11 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Requires-Python: >=3.11
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: azapy>=1.2.
|
|
18
|
+
Requires-Dist: azapy>=1.2.5
|
|
19
19
|
Requires-Dist: numpy
|
|
20
20
|
Requires-Dist: pandas
|
|
21
21
|
Requires-Dist: matplotlib
|
|
22
|
-
Requires-Dist: xlsxwriter
|
|
22
|
+
Requires-Dist: xlsxwriter; platform_system == "Windows"
|
|
23
23
|
|
|
24
24
|
# azapyGUI project
|
|
25
25
|
|
|
@@ -99,7 +99,7 @@ and then run in a powershell: `python my_azapy.py`
|
|
|
99
99
|
|
|
100
100
|
1. Kelly's portfolio (as in John Larry Kelly Jr. scientist 1923-1965) -
|
|
101
101
|
maximization of portfolio log returns
|
|
102
|
-
2. Universal portfolio (Thomas M. Cover 1996) <span style="color:
|
|
102
|
+
2. Universal portfolio (Thomas M. Cover 1996) <span style="color:blue">(beta version)</span>
|
|
103
103
|
|
|
104
104
|
### D. Market Selectors
|
|
105
105
|
|
|
@@ -118,9 +118,9 @@ and then run in a powershell: `python my_azapy.py`
|
|
|
118
118
|
### Required packages
|
|
119
119
|
|
|
120
120
|
* python 3.11.8
|
|
121
|
-
* azapy 1.2.
|
|
121
|
+
* azapy 1.2.5
|
|
122
122
|
* pandas 2.2.0
|
|
123
123
|
* numpy 1.26.0
|
|
124
124
|
* matplotlib 3.8.0
|
|
125
|
-
* xlsxwriter 3.1.1
|
|
125
|
+
* xlsxwriter 3.1.1 (for non-Linux installations)
|
|
126
126
|
|
|
@@ -76,7 +76,7 @@ and then run in a powershell: `python my_azapy.py`
|
|
|
76
76
|
|
|
77
77
|
1. Kelly's portfolio (as in John Larry Kelly Jr. scientist 1923-1965) -
|
|
78
78
|
maximization of portfolio log returns
|
|
79
|
-
2. Universal portfolio (Thomas M. Cover 1996) <span style="color:
|
|
79
|
+
2. Universal portfolio (Thomas M. Cover 1996) <span style="color:blue">(beta version)</span>
|
|
80
80
|
|
|
81
81
|
### D. Market Selectors
|
|
82
82
|
|
|
@@ -95,9 +95,9 @@ and then run in a powershell: `python my_azapy.py`
|
|
|
95
95
|
### Required packages
|
|
96
96
|
|
|
97
97
|
* python 3.11.8
|
|
98
|
-
* azapy 1.2.
|
|
98
|
+
* azapy 1.2.5
|
|
99
99
|
* pandas 2.2.0
|
|
100
100
|
* numpy 1.26.0
|
|
101
101
|
* matplotlib 3.8.0
|
|
102
|
-
* xlsxwriter 3.1.1
|
|
102
|
+
* xlsxwriter 3.1.1 (for non-Linux installations)
|
|
103
103
|
|
|
@@ -36,7 +36,6 @@ class AppSettingsPage(tk.Frame):
|
|
|
36
36
|
|
|
37
37
|
# on frm_set
|
|
38
38
|
self._setDef = configSettings.settings_model[self._category]
|
|
39
|
-
#self.settings = deepcopy(configSettings.MasterApplicationSettings)
|
|
40
39
|
self.settings = {key: configSettings.MasterApplicationSettings[key] for key in self._setDef.keys()}
|
|
41
40
|
row = 0
|
|
42
41
|
self._chk_val = {}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import tkinter as tk
|
|
2
|
+
import tkinter.ttk as ttk
|
|
3
|
+
import platform
|
|
2
4
|
from copy import deepcopy
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import azapy as az
|
|
3
7
|
|
|
4
8
|
import azapyGUI.config as config
|
|
5
9
|
import azapyGUI.configSettings as configSettings
|
|
@@ -26,7 +30,6 @@ class AppSettingsPageMisc(tk.Frame):
|
|
|
26
30
|
|
|
27
31
|
# on frm
|
|
28
32
|
self._setDef = configSettings.settings_model[self._category]
|
|
29
|
-
#self.settings = deepcopy(configSettings.MasterApplicationSettings)
|
|
30
33
|
self.settings = {key: configSettings.MasterApplicationSettings[key] for key in self._setDef.keys()}
|
|
31
34
|
row = 0
|
|
32
35
|
self._chk_val = {}
|
|
@@ -35,26 +38,39 @@ class AppSettingsPageMisc(tk.Frame):
|
|
|
35
38
|
match value["type"]:
|
|
36
39
|
case 'Checkbutton':
|
|
37
40
|
lbl = tk.Label(master=frm_set, text=value["field"], anchor=tk.W)
|
|
38
|
-
lbl.grid(row=row, column=0, padx=5, pady=
|
|
41
|
+
lbl.grid(row=row, column=0, padx=5, pady=2, sticky=tk.EW)
|
|
39
42
|
chk_var = tk.BooleanVar(master=frm_set, value=self.settings[param])
|
|
40
43
|
chk_btn = tk.Checkbutton(master=frm_set, variable = chk_var,
|
|
41
44
|
onvalue = True, offvalue = False,
|
|
42
|
-
height=
|
|
43
|
-
chk_btn.grid(row=row, column=1, padx=5, pady=
|
|
45
|
+
height=1, width=1, anchor=tk.W)
|
|
46
|
+
chk_btn.grid(row=row, column=1, padx=5, pady=2, sticky=tk.W)
|
|
44
47
|
self._chk_val[param] = chk_var
|
|
45
48
|
self._chk_btn[param] = chk_btn
|
|
46
49
|
config.tiptil.bind(chk_btn, value["tip"])
|
|
50
|
+
if (param == 'OpenExcel') and (platform.system() == 'Linux'):
|
|
51
|
+
chk_var.set(False)
|
|
52
|
+
chk_btn.config(state=tk.DISABLED)
|
|
47
53
|
row += 1
|
|
48
54
|
case 'Entry':
|
|
49
55
|
lbl = tk.Label(master=frm_set, text=value["field"], anchor=tk.W)
|
|
50
|
-
lbl.grid(row=row, column=0, padx=5, pady=
|
|
56
|
+
lbl.grid(row=row, column=0, padx=5, pady=2, sticky=tk.W)
|
|
51
57
|
ent_var = tk.StringVar(master=frm_set, value=self.settings[param])
|
|
52
58
|
ent = tk.Entry(master=frm_set, textvariable=ent_var, validate='key', width=10)
|
|
53
59
|
ent['validatecommand'] = (ent.register(value["validate"]),'%S','%d','%P')
|
|
54
|
-
ent.grid(row=row, column=1, padx=5, pady=
|
|
60
|
+
ent.grid(row=row, column=1, padx=5, pady=2, sticky=tk.W)
|
|
55
61
|
self._chk_val[param] = ent_var
|
|
56
62
|
config.tiptil.bind(ent, value["tip"])
|
|
57
63
|
row += 1
|
|
64
|
+
case 'Combobox':
|
|
65
|
+
lbl = tk.Label(master=frm_set, text=value["field"], anchor=tk.W)
|
|
66
|
+
lbl.grid(row=row, column=0, padx=5, pady=2, sticky=tk.EW)
|
|
67
|
+
cbx_var = tk.StringVar(master=frm_set, value=self.settings[param])
|
|
68
|
+
cbx = ttk.Combobox(master=frm_set, textvariable=cbx_var, width=10, state='readonly')
|
|
69
|
+
cbx["values"] = value["values"]
|
|
70
|
+
cbx.grid(row=row, column=1, padx=5, pady=2, sticky=tk.W)
|
|
71
|
+
self._chk_val[param] = cbx_var
|
|
72
|
+
config.tiptil.bind(cbx, value["tip"])
|
|
73
|
+
row += 1
|
|
58
74
|
case _:
|
|
59
75
|
# you should not be here
|
|
60
76
|
raise ValueError("Error: Unknown configSetting type")
|
|
@@ -88,14 +104,21 @@ class AppSettingsPageMisc(tk.Frame):
|
|
|
88
104
|
self._chk_btn[kk].select()
|
|
89
105
|
else:
|
|
90
106
|
self._chk_btn[kk].deselect()
|
|
91
|
-
case "Entry":
|
|
107
|
+
#case "Entry":
|
|
108
|
+
case _:
|
|
92
109
|
self._chk_val[kk].set(self.settings[kk])
|
|
93
110
|
|
|
94
111
|
|
|
95
112
|
def _btn_save_func(self):
|
|
113
|
+
if self.settings['calendar'] != self._chk_val['calendar'].get():
|
|
114
|
+
config.MktDataDict.clear()
|
|
115
|
+
config.appMktDataFrame.refresh()
|
|
116
|
+
|
|
96
117
|
self.settings.update({key: self._chk_val[key].get() for key in self._chk_val.keys()})
|
|
97
118
|
configSettings.MasterApplicationSettings.update(self.settings)
|
|
98
119
|
_saveMasterUserConfig(configSettings.MasterApplicationSettings)
|
|
99
120
|
config.tiptil.turned(on=configSettings.MasterApplicationSettings["ShowTips"])
|
|
121
|
+
config.calendar = az.calendarGen(configSettings.MasterApplicationSettings["calendar"])
|
|
122
|
+
config._bday = pd.offsets.CustomBusinessDay(calendar=config.calendar)
|
|
100
123
|
|
|
101
124
|
|
|
@@ -4,6 +4,7 @@ from copy import deepcopy
|
|
|
4
4
|
import azapyGUI.config as config
|
|
5
5
|
import azapyGUI.configSettings as configSettings
|
|
6
6
|
from azapyGUI.MktDataNode import MktDataNode
|
|
7
|
+
from azapyGUI.modelParametersValidation import _validStr
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class GetMktData():
|
|
@@ -53,6 +54,9 @@ class GetMktData():
|
|
|
53
54
|
# retrieve symb (not in the system)
|
|
54
55
|
self._symb_req["symbol"] = symb
|
|
55
56
|
mktdata = self._mktr.get(calendar=config.calendar, **self._symb_req)
|
|
57
|
+
imputation_method = _validStr(configSettings.MasterApplicationSettings['imputation'])
|
|
58
|
+
if imputation_method is not None:
|
|
59
|
+
mktdata = self._mktr.set_imputation(method=imputation_method)
|
|
56
60
|
if len(mktdata.keys()) == 0:
|
|
57
61
|
# no extraction was possible (all symb are error symbols)
|
|
58
62
|
self.symbols = list(set(symbols) - set(symb))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import azapy as az
|
|
2
|
+
import azapyGUI.config as config
|
|
2
3
|
|
|
3
4
|
class MktDataNode:
|
|
4
5
|
def __init__(self):
|
|
@@ -7,28 +8,33 @@ class MktDataNode:
|
|
|
7
8
|
self.stats = None
|
|
8
9
|
self.status = False
|
|
9
10
|
self._summary = None
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _set_summary(self):
|
|
14
|
+
if self._summary is None:
|
|
15
|
+
self._summary = az.summary_MkTdata(self.mktdata, calendar=config.calendar)
|
|
16
|
+
|
|
17
|
+
|
|
12
18
|
def sdate(self):
|
|
13
|
-
|
|
19
|
+
self._set_summary()
|
|
14
20
|
return self._summary.begin[0]
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
|
|
22
|
+
|
|
17
23
|
def edate(self):
|
|
18
|
-
|
|
24
|
+
self._set_summary()
|
|
19
25
|
return self._summary.end[0]
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
|
|
27
|
+
|
|
22
28
|
def nrow(self):
|
|
23
|
-
|
|
29
|
+
self._set_summary()
|
|
24
30
|
return self._summary.length[0]
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
|
|
32
|
+
|
|
27
33
|
def iserror(self):
|
|
28
|
-
|
|
34
|
+
self._set_summary()
|
|
29
35
|
return (self._summary.na_total[0] + self._summary.cont[0]) > 0
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
|
|
37
|
+
|
|
32
38
|
def get_mktdata(self, edate=None):
|
|
33
39
|
return self.mktdata if edate is None else self.mktdata[self.mktdata.index <= edate]
|
|
34
40
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import platform
|
|
2
|
+
|
|
3
|
+
from azapyGUI.ViewTip_Ubuntu import ViewTip_Ubuntu
|
|
4
|
+
from azapyGUI.ViewTip_fade import ViewTip_fade
|
|
5
|
+
from azapyGUI.ViewTip_fast import ViewTip_fast
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def ViewTip(master, **kwargs):
|
|
9
|
+
if platform.system() == 'Windows':
|
|
10
|
+
return ViewTip_fade(master, **kwargs)
|
|
11
|
+
else:
|
|
12
|
+
return ViewTip_Ubuntu(master, **kwargs)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
Widget = Union[tk.Widget, ttk.Widget]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ViewTip_Ubuntu(tk.Toplevel):
|
|
9
|
+
def __init__(self, master, **kwargs):
|
|
10
|
+
tk.Toplevel.__init__(self, master)
|
|
11
|
+
|
|
12
|
+
self.attributes('-alpha', 0, '-topmost', True)
|
|
13
|
+
self.overrideredirect(True)
|
|
14
|
+
|
|
15
|
+
style = dict(bd=2, relief='raised', font='Ariel 10', bg='#D4D4D4',
|
|
16
|
+
anchor='w', justify='left')
|
|
17
|
+
self._label = tk.Label(self, **{**style, **kwargs})
|
|
18
|
+
self._label.grid(row=0, column=0, sticky='w')
|
|
19
|
+
|
|
20
|
+
self._view = True
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def bind(self, target:Widget, text:str, **kwargs):
|
|
24
|
+
target.bind('<Enter>', lambda e: self._goin(text, e), add="+")
|
|
25
|
+
target.bind('<Leave>', lambda e: self._goout(), add="+")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _goin(self, text:str=None, event:tk.Event=None):
|
|
29
|
+
if not self._view: return
|
|
30
|
+
self.deiconify()
|
|
31
|
+
|
|
32
|
+
self._label.configure(text=f'{text:^{len(text) + 2}}')
|
|
33
|
+
self.update()
|
|
34
|
+
|
|
35
|
+
offset_x = event.widget.winfo_width() + 2
|
|
36
|
+
offset_y = int((event.widget.winfo_height() - self._label.winfo_height()) / 2)
|
|
37
|
+
|
|
38
|
+
w = self._label.winfo_width()
|
|
39
|
+
h = self._label.winfo_height()
|
|
40
|
+
x = event.widget.winfo_rootx() + offset_x
|
|
41
|
+
y = event.widget.winfo_rooty() + offset_y
|
|
42
|
+
|
|
43
|
+
self.geometry(f'{w}x{h}+{x}+{y}')
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _goout(self):
|
|
47
|
+
if not self._view: return
|
|
48
|
+
self.withdraw()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def turned(self, on:bool=True):
|
|
52
|
+
self._view = on
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
Widget = Union[tk.Widget, ttk.Widget]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ViewTip_fade(tk.Toplevel):
|
|
9
|
+
def __init__(self, master, fade_inc=0.07, fade_ms=20, **kwargs):
|
|
10
|
+
tk.Toplevel.__init__(self, master)
|
|
11
|
+
self._fade_inc = fade_inc
|
|
12
|
+
self._fade_ms = fade_ms
|
|
13
|
+
|
|
14
|
+
self.attributes('-alpha', 0, '-topmost', True)
|
|
15
|
+
self.overrideredirect(1)
|
|
16
|
+
|
|
17
|
+
style = dict(bd=2, relief='raised', font='Ariel 10', bg='#D4D4D4',
|
|
18
|
+
anchor='w', justify='left')
|
|
19
|
+
self._label = tk.Label(self, **{**style, **kwargs})
|
|
20
|
+
self._label.grid(row=0, column=0, sticky='w')
|
|
21
|
+
|
|
22
|
+
self._fout:bool = False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def bind(self, target:Widget, text:str, **kwargs):
|
|
26
|
+
target.bind('<Enter>', lambda e: self._fadein(0, text, e), add="+")
|
|
27
|
+
target.bind('<Leave>', lambda e: self._fadeout(1 - self._fade_inc, e), add="+")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _fadein(self, alpha:float, text:str=None, event:tk.Event=None):
|
|
31
|
+
if event and text:
|
|
32
|
+
if self._fout:
|
|
33
|
+
self.attributes('-alpha', 0)
|
|
34
|
+
self._fout = False
|
|
35
|
+
|
|
36
|
+
self._label.configure(text=f'{text:^{len(text) + 2}}')
|
|
37
|
+
self.update()
|
|
38
|
+
|
|
39
|
+
offset_x = event.widget.winfo_width() + 2
|
|
40
|
+
offset_y = int((event.widget.winfo_height() - self._label.winfo_height()) / 2)
|
|
41
|
+
|
|
42
|
+
w = self._label.winfo_width()
|
|
43
|
+
h = self._label.winfo_height()
|
|
44
|
+
x = event.widget.winfo_rootx() + offset_x
|
|
45
|
+
y = event.widget.winfo_rooty() + offset_y
|
|
46
|
+
|
|
47
|
+
self.geometry(f'{w}x{h}+{x}+{y}')
|
|
48
|
+
|
|
49
|
+
if not self._fout:
|
|
50
|
+
self.attributes('-alpha', alpha)
|
|
51
|
+
|
|
52
|
+
if alpha < 1:
|
|
53
|
+
self.after(self._fade_ms,
|
|
54
|
+
lambda: self._fadein(min(alpha + self._fade_inc, 1)))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _fadeout(self, alpha:float, event:tk.Event=None):
|
|
58
|
+
if event:
|
|
59
|
+
self._fout = True
|
|
60
|
+
|
|
61
|
+
if self._fout:
|
|
62
|
+
self.attributes('-alpha', alpha)
|
|
63
|
+
|
|
64
|
+
if alpha > 0:
|
|
65
|
+
self.after(self._fade_ms,
|
|
66
|
+
lambda: self._fadeout(max(alpha - self._fade_inc, 0)))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def turned(self, on=True):
|
|
70
|
+
if on:
|
|
71
|
+
self.deiconify()
|
|
72
|
+
else:
|
|
73
|
+
self.withdraw()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
Widget = Union[tk.Widget, ttk.Widget]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ViewTip_fast(tk.Toplevel):
|
|
9
|
+
def __init__(self, master, fade_inc=0.07, fade_ms=20, **kwargs):
|
|
10
|
+
tk.Toplevel.__init__(self, master)
|
|
11
|
+
self._fade_inc = fade_inc
|
|
12
|
+
self._fade_ms = fade_ms
|
|
13
|
+
|
|
14
|
+
self.attributes('-alpha', 0, '-topmost', True)
|
|
15
|
+
self.overrideredirect(True)
|
|
16
|
+
|
|
17
|
+
style = dict(bd=2, relief='raised', font='Ariel 10', bg='#D4D4D4',
|
|
18
|
+
anchor='w', justify='left')
|
|
19
|
+
self._label = tk.Label(self, **{**style, **kwargs})
|
|
20
|
+
self._label.grid(row=0, column=0, sticky='w')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def bind(self, target:Widget, text:str, **kwargs):
|
|
24
|
+
target.bind('<Enter>', lambda e: self._goin(text, e), add="+")
|
|
25
|
+
target.bind('<Leave>', lambda e: self._goout(), add="+")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _goin(self, text:str=None, event:tk.Event=None):
|
|
29
|
+
if text is None: return
|
|
30
|
+
|
|
31
|
+
self._label.configure(text=f'{text:^{len(text) + 2}}')
|
|
32
|
+
self.update()
|
|
33
|
+
|
|
34
|
+
offset_x = event.widget.winfo_width() + 2
|
|
35
|
+
offset_y = int((event.widget.winfo_height() - self._label.winfo_height()) / 2)
|
|
36
|
+
|
|
37
|
+
w = self._label.winfo_width()
|
|
38
|
+
h = self._label.winfo_height()
|
|
39
|
+
x = event.widget.winfo_rootx() + offset_x
|
|
40
|
+
y = event.widget.winfo_rooty() + offset_y
|
|
41
|
+
|
|
42
|
+
self.geometry(f'{w}x{h}+{x}+{y}')
|
|
43
|
+
|
|
44
|
+
self.attributes('-alpha', 1)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _goout(self):
|
|
48
|
+
self.attributes('-alpha', 0)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def turned(self, on=True):
|
|
52
|
+
if on:
|
|
53
|
+
self.deiconify()
|
|
54
|
+
else:
|
|
55
|
+
self.withdraw()
|
|
@@ -37,7 +37,7 @@ class app:
|
|
|
37
37
|
config.MktDataDict = {}
|
|
38
38
|
config.PortDataDict = {}
|
|
39
39
|
config.count_SymbTableEntry = -1
|
|
40
|
-
config.calendar = az.
|
|
40
|
+
config.calendar = az.calendarGen(configSettings.MasterApplicationSettings["calendar"])
|
|
41
41
|
config._bday = pd.offsets.CustomBusinessDay(calendar=config.calendar)
|
|
42
42
|
img = base64.b64decode(config.iconimgdata)
|
|
43
43
|
config.photo = tk.PhotoImage(data=img, master=self._root)
|
|
@@ -2,7 +2,15 @@ import os
|
|
|
2
2
|
import azapyGUI.configTips as configTips
|
|
3
3
|
from azapyGUI.modelParametersValidation import _validDateMMDDYYYY, _validInt, _validIntNegative, _validIntPositive
|
|
4
4
|
|
|
5
|
-
Version = '0.0
|
|
5
|
+
Version = '0.1.0'
|
|
6
|
+
|
|
7
|
+
_exchange_calendar_values = ('NYSE', 'XBUE', 'XASX', 'XWBO', 'XBRU', 'BVMF', 'XTSE', 'XSGO', 'XSHG', 'XBOG',
|
|
8
|
+
'XPRA', 'XCSE', 'XLON', 'XHEL', 'XPAR', 'XFRA', 'ASEX', 'XHKG', 'XBUD', 'XICE',
|
|
9
|
+
'XBOM', 'XIDX', 'XDUB', 'XMIL', 'XTKS', 'XKLS', 'XMEX', 'XAMS', 'XNZE', 'XOSL',
|
|
10
|
+
'XKAR', 'XLIM', 'XPHS', 'XWAR', 'XLIS', 'XMOS', 'XSES', 'XJSE', 'XKRX', 'XMAD',
|
|
11
|
+
'XSTO', 'XSWX', 'XTAI', 'XBKK', 'XIST', 'XNYS')
|
|
12
|
+
|
|
13
|
+
_imputation_method_values = ('None', 'linear')
|
|
6
14
|
|
|
7
15
|
settings_model = {"Directors": {"UserPortfolioDirectory": {"default": None,
|
|
8
16
|
"type": 'ButtonDir',
|
|
@@ -93,6 +101,18 @@ settings_model = {"Directors": {"UserPortfolioDirectory": {"default": None,
|
|
|
93
101
|
"tip": configTips._settings_capital_default_tip,
|
|
94
102
|
"validate": _validIntPositive,
|
|
95
103
|
},
|
|
104
|
+
"calendar": {"default": 'NYSE',
|
|
105
|
+
"type": 'Combobox',
|
|
106
|
+
"field": 'Exchange calendar',
|
|
107
|
+
"tip": configTips._exchange_calendar_tip,
|
|
108
|
+
"values": _exchange_calendar_values,
|
|
109
|
+
},
|
|
110
|
+
"imputation": {"default": 'linear',
|
|
111
|
+
"type": 'Combobox',
|
|
112
|
+
"field": 'Imputation method',
|
|
113
|
+
"tip": configTips._imputation_method_tip,
|
|
114
|
+
"values": _imputation_method_values,
|
|
115
|
+
},
|
|
96
116
|
"nsh_round": {"default": True,
|
|
97
117
|
"type": 'Checkbutton',
|
|
98
118
|
"field": 'Int. nr. shares',
|
|
@@ -113,7 +133,6 @@ settings_model = {"Directors": {"UserPortfolioDirectory": {"default": None,
|
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
|
|
116
|
-
|
|
117
136
|
def get_settings_default(category):
|
|
118
137
|
rout = {kk: vv["default"] for kk, vv in settings_model[category].items()}
|
|
119
138
|
return rout
|
|
@@ -440,3 +440,8 @@ def _validDateMMDDYYYY(inp, acttype, val):
|
|
|
440
440
|
|
|
441
441
|
def _list2string(lnames, bk=10):
|
|
442
442
|
return '\n'.join([', '.join(lnames[k : (k + bk)]) for k in range(0, len(lnames), bk)])
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _validStr(name):
|
|
446
|
+
rout = str(name)
|
|
447
|
+
return rout if rout.upper() not in ('NULL', 'NAN', 'NA', 'NONE') else None
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
import json
|
|
3
|
+
import tkinter as tk
|
|
4
|
+
|
|
5
|
+
import azapyGUI.configSettings as configSettings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _fileMasterUserConfig():
|
|
9
|
+
return pathlib.Path.home().joinpath(".azapyGUI/MasterUserConfig.json")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _readMasterUserConfig():
|
|
13
|
+
try:
|
|
14
|
+
out_file = _fileMasterUserConfig()
|
|
15
|
+
with open(out_file, 'r') as fp:
|
|
16
|
+
data = json.load(fp)
|
|
17
|
+
_update_version(data)
|
|
18
|
+
except FileNotFoundError:
|
|
19
|
+
data = configSettings.get_settings_default_all()
|
|
20
|
+
_saveMasterUserConfig(data)
|
|
21
|
+
|
|
22
|
+
return data
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _saveMasterUserConfig(data):
|
|
26
|
+
out_file = _fileMasterUserConfig()
|
|
27
|
+
out_file.parent.mkdir(exist_ok=True, parents=True)
|
|
28
|
+
with open(out_file, 'w') as fp:
|
|
29
|
+
json.dump(data, fp)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _update_version(data):
|
|
33
|
+
d_data = configSettings.get_settings_default_all()
|
|
34
|
+
if data['Version'] == d_data['Version']:
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
msg = f"The setting version was updated from {data['Version']} to {d_data['Version']}." + \
|
|
38
|
+
"Please check the Settings for new updated values."
|
|
39
|
+
tk.messagebox.showinfo(title='Setting Version Update', message=msg)
|
|
40
|
+
data = {kk: data.get(kk, vv) for kk, vv in d_data.items()}
|
|
41
|
+
data['Version'] = d_data['Version']
|
|
42
|
+
_saveMasterUserConfig(data)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: azapyGUI
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: GUI for azapy library - Financial Portfolio Optimization Algorithms
|
|
5
5
|
Home-page: https://github.com/Mircea-MMXXI/azapyGUI.git
|
|
6
6
|
Author: Mircea Marinescu
|
|
@@ -15,11 +15,11 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Requires-Python: >=3.11
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: azapy>=1.2.
|
|
18
|
+
Requires-Dist: azapy>=1.2.5
|
|
19
19
|
Requires-Dist: numpy
|
|
20
20
|
Requires-Dist: pandas
|
|
21
21
|
Requires-Dist: matplotlib
|
|
22
|
-
Requires-Dist: xlsxwriter
|
|
22
|
+
Requires-Dist: xlsxwriter; platform_system == "Windows"
|
|
23
23
|
|
|
24
24
|
# azapyGUI project
|
|
25
25
|
|
|
@@ -99,7 +99,7 @@ and then run in a powershell: `python my_azapy.py`
|
|
|
99
99
|
|
|
100
100
|
1. Kelly's portfolio (as in John Larry Kelly Jr. scientist 1923-1965) -
|
|
101
101
|
maximization of portfolio log returns
|
|
102
|
-
2. Universal portfolio (Thomas M. Cover 1996) <span style="color:
|
|
102
|
+
2. Universal portfolio (Thomas M. Cover 1996) <span style="color:blue">(beta version)</span>
|
|
103
103
|
|
|
104
104
|
### D. Market Selectors
|
|
105
105
|
|
|
@@ -118,9 +118,9 @@ and then run in a powershell: `python my_azapy.py`
|
|
|
118
118
|
### Required packages
|
|
119
119
|
|
|
120
120
|
* python 3.11.8
|
|
121
|
-
* azapy 1.2.
|
|
121
|
+
* azapy 1.2.5
|
|
122
122
|
* pandas 2.2.0
|
|
123
123
|
* numpy 1.26.0
|
|
124
124
|
* matplotlib 3.8.0
|
|
125
|
-
* xlsxwriter 3.1.1
|
|
125
|
+
* xlsxwriter 3.1.1 (for non-Linux installations)
|
|
126
126
|
|
|
@@ -36,6 +36,9 @@ setup.py
|
|
|
36
36
|
./azapyGUI/SymbTableEntry.py
|
|
37
37
|
./azapyGUI/TimeSeriesViewWindow.py
|
|
38
38
|
./azapyGUI/ViewTip.py
|
|
39
|
+
./azapyGUI/ViewTip_Ubuntu.py
|
|
40
|
+
./azapyGUI/ViewTip_fade.py
|
|
41
|
+
./azapyGUI/ViewTip_fast.py
|
|
39
42
|
./azapyGUI/WeightsWindow.py
|
|
40
43
|
./azapyGUI/__init__.py
|
|
41
44
|
./azapyGUI/azHelper.py
|
|
@@ -85,6 +88,9 @@ azapyGUI/SymbExtractWindow.py
|
|
|
85
88
|
azapyGUI/SymbTableEntry.py
|
|
86
89
|
azapyGUI/TimeSeriesViewWindow.py
|
|
87
90
|
azapyGUI/ViewTip.py
|
|
91
|
+
azapyGUI/ViewTip_Ubuntu.py
|
|
92
|
+
azapyGUI/ViewTip_fade.py
|
|
93
|
+
azapyGUI/ViewTip_fast.py
|
|
88
94
|
azapyGUI/WeightsWindow.py
|
|
89
95
|
azapyGUI/__init__.py
|
|
90
96
|
azapyGUI/azHelper.py
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as readme:
|
|
|
5
5
|
|
|
6
6
|
setuptools.setup(
|
|
7
7
|
name="azapyGUI",
|
|
8
|
-
version="0.
|
|
8
|
+
version="0.1.1",
|
|
9
9
|
author="Mircea Marinescu",
|
|
10
10
|
author_email="mircea.marinescu@outlook.com",
|
|
11
11
|
description="GUI for azapy library - Financial Portfolio Optimization Algorithms",
|
|
@@ -27,10 +27,10 @@ setuptools.setup(
|
|
|
27
27
|
packages=setuptools.find_packages(),
|
|
28
28
|
python_requires=">=3.11",
|
|
29
29
|
install_requires=[
|
|
30
|
-
'azapy>=1.2.
|
|
30
|
+
'azapy>=1.2.5',
|
|
31
31
|
'numpy',
|
|
32
32
|
'pandas',
|
|
33
33
|
'matplotlib',
|
|
34
|
-
'xlsxwriter'
|
|
34
|
+
'xlsxwriter; platform_system=="Windows"'
|
|
35
35
|
],
|
|
36
36
|
)
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import tkinter as tk
|
|
2
|
-
from tkinter import ttk
|
|
3
|
-
from typing import Union
|
|
4
|
-
|
|
5
|
-
Widget = Union[tk.Widget, ttk.Widget]
|
|
6
|
-
|
|
7
|
-
class ViewTip(tk.Toplevel):
|
|
8
|
-
_FADE_INC:float = .07
|
|
9
|
-
_FADE_MS :int = 20
|
|
10
|
-
|
|
11
|
-
def __init__(self, master, **kwargs):
|
|
12
|
-
tk.Toplevel.__init__(self, master)
|
|
13
|
-
|
|
14
|
-
self.attributes('-alpha', 0, '-topmost', True)
|
|
15
|
-
self.overrideredirect(1)
|
|
16
|
-
|
|
17
|
-
style = dict(bd=2, relief='raised', font='Ariel 10', bg='#D4D4D4',
|
|
18
|
-
anchor='w', justify='left')
|
|
19
|
-
self.label = tk.Label(self, **{**style, **kwargs})
|
|
20
|
-
self.label.grid(row=0, column=0, sticky='w')
|
|
21
|
-
|
|
22
|
-
self.fout:bool = False
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def bind(self, target:Widget, text:str, **kwargs):
|
|
26
|
-
target.bind('<Enter>', lambda e: self._fadein(0, text, e))
|
|
27
|
-
target.bind('<Leave>', lambda e: self._fadeout(1 - ViewTip._FADE_INC, e))
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _fadein(self, alpha:float, text:str=None, event:tk.Event=None):
|
|
31
|
-
if event and text:
|
|
32
|
-
if self.fout:
|
|
33
|
-
self.attributes('-alpha', 0)
|
|
34
|
-
self.fout = False
|
|
35
|
-
self.label.configure(text=f'{text:^{len(text)+2}}')
|
|
36
|
-
self.update()
|
|
37
|
-
|
|
38
|
-
offset_x = event.widget.winfo_width()+2
|
|
39
|
-
offset_y = int((event.widget.winfo_height() - self.label.winfo_height()) / 2)
|
|
40
|
-
|
|
41
|
-
w = self.label.winfo_width()
|
|
42
|
-
h = self.label.winfo_height()
|
|
43
|
-
x = event.widget.winfo_rootx()+offset_x
|
|
44
|
-
y = event.widget.winfo_rooty()+offset_y
|
|
45
|
-
|
|
46
|
-
self.geometry(f'{w}x{h}+{x}+{y}')
|
|
47
|
-
|
|
48
|
-
if not self.fout:
|
|
49
|
-
self.attributes('-alpha', alpha)
|
|
50
|
-
|
|
51
|
-
if alpha < 1:
|
|
52
|
-
self.after(ViewTip._FADE_MS,
|
|
53
|
-
lambda: self._fadein(min(alpha + ViewTip._FADE_INC, 1)))
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def _fadeout(self, alpha:float, event:tk.Event=None):
|
|
57
|
-
if event:
|
|
58
|
-
self.fout = True
|
|
59
|
-
|
|
60
|
-
if self.fout:
|
|
61
|
-
self.attributes('-alpha', alpha)
|
|
62
|
-
|
|
63
|
-
if alpha > 0:
|
|
64
|
-
self.after(ViewTip._FADE_MS,
|
|
65
|
-
lambda: self._fadeout(max(alpha - ViewTip._FADE_INC, 0)))
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def turned(self, on=True):
|
|
69
|
-
if on:
|
|
70
|
-
self.deiconify()
|
|
71
|
-
else:
|
|
72
|
-
self.withdraw()
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import pathlib
|
|
2
|
-
import json
|
|
3
|
-
|
|
4
|
-
import azapyGUI.configSettings as configSettings
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def _fileMasterUserConfig():
|
|
8
|
-
return pathlib.Path.home().joinpath(".azapyGUI/MasterUserConfig.json")
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def _readMasterUserConfig():
|
|
12
|
-
out_file = _fileMasterUserConfig()
|
|
13
|
-
try:
|
|
14
|
-
with open(out_file, 'r') as fp:
|
|
15
|
-
data = json.load(fp)
|
|
16
|
-
except FileNotFoundError:
|
|
17
|
-
data = configSettings.get_settings_default_all()
|
|
18
|
-
out_file.parent.mkdir(exist_ok=True, parents=True)
|
|
19
|
-
with open(out_file, 'w') as fp:
|
|
20
|
-
json.dump(data, fp)
|
|
21
|
-
return data
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def _saveMasterUserConfig(data):
|
|
25
|
-
out_file = _fileMasterUserConfig()
|
|
26
|
-
out_file.parent.mkdir(exist_ok=True, parents=True)
|
|
27
|
-
with open(out_file, 'w') as fp:
|
|
28
|
-
json.dump(data, fp)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|