-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathrenderer.py
214 lines (179 loc) · 6.59 KB
/
renderer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import io
import utils
import portfolio
from colorama import Fore, Style, Back
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import List, Callable
@dataclass
class CellData:
value: str
color: str = Fore.RESET
@dataclass
class ColumnFormatter:
header: str
width: int
# function that takes a thing and produces a printable string for it
generator: Callable[[object], CellData] = lambda v: CellData(str(v))
def generate_string(self, input) -> str:
cell_data = self.generator(input)
return cell_data.value
def format_number(value) -> str:
return str(abs(utils.round_value(value, "math", 2)))
def format_gl(value: float, is_currency: bool = True) -> str:
change_symbol = "+" if value >= 0 else "-"
if is_currency:
change_symbol += "$"
return change_symbol + format_number(value)
_stock_column_formatters = {
"Ticker": ColumnFormatter("Ticker", 9, lambda stock: CellData(stock.symbol)),
"Current Price": ColumnFormatter(
"Last", 12, lambda stock: CellData(format_number(stock.curr_value))
),
"Daily Change Amount": ColumnFormatter(
"Chg",
12,
lambda stock: CellData(
format_gl(stock.change_amount),
Fore.GREEN if stock.change_amount >= 0 else Fore.RED,
),
),
"Daily Change Percentage": ColumnFormatter(
"Chg%",
10,
lambda stock: CellData(
format_gl(stock.change_percentage, False) + "%",
Fore.GREEN if stock.change_percentage >= 0 else Fore.RED,
),
),
"Low": ColumnFormatter("Low", 12, lambda stock: CellData(format_number(stock.low))),
"High": ColumnFormatter(
"High", 12, lambda stock: CellData(format_number(stock.high))
),
"Daily Average Price": ColumnFormatter(
"Avg", 12, lambda stock: CellData(format_number(stock.average))
),
}
_portfolio_column_formatters = {
"Stocks Owned": ColumnFormatter(
"Owned", 9, lambda entry: CellData(format_number(entry.count))
),
"Gains per Share": ColumnFormatter(
"G/L/S",
12,
lambda entry: CellData(
format_gl(entry.gains_per_share),
Fore.GREEN if entry.gains_per_share >= 0 else Fore.RED,
),
),
"Current Market Value": ColumnFormatter(
"Mkt V", 12, lambda entry: CellData(format_number(entry.holding_market_value))
),
"Average Buy Price": ColumnFormatter(
"Buy", 12, lambda entry: CellData(format_number(entry.average_cost))
),
"Total Share Gains": ColumnFormatter(
"G/L/T",
12,
lambda entry: CellData(
format_gl(entry.gains), Fore.GREEN if entry.gains >= 0 else Fore.RED
),
),
"Total Share Cost": ColumnFormatter(
"Cost", 12, lambda entry: CellData(format_number(entry.cost_basis))
),
}
class Renderer(metaclass=utils.Singleton):
def __init__(self, rounding: str, portfolio: portfolio.Portfolio, *args, **kwargs):
self.mode = rounding
self.portfolio = portfolio
return
def render(self):
print()
for graph in self.portfolio.graphs:
graph.draw()
self.print_new_table()
print()
return
def print_gains(self, format_str, gain, timespan):
positive_gain = gain >= 0
gain_symbol = "+" if positive_gain else "-"
gain_verboge = "Gained" if positive_gain else "Lost"
print("{:25}".format("Value " + gain_verboge + " " + timespan + ": "), end="")
print(Fore.GREEN if positive_gain else Fore.RED, end="")
# This prevents a runtime warning by making sure we are never dividing by zero
if self.portfolio.cost_value == 0:
gain_val = 0
else:
gain_val = gain / self.portfolio.cost_value * 100
print(
format_str.format(
gain_symbol + "$" + str(abs(utils.round_value(gain, self.mode, 2)))
)
+ format_str.format(
gain_symbol + str(abs(utils.round_value(gain_val, self.mode, 2))) + "%"
)
)
print(Style.RESET_ALL, end="")
return
def print_overall_summary(self):
print(
"\n"
+ "{:25}".format("Current Time: ")
+ "{:13}".format(datetime.now().strftime("%A %b %d, %Y - %I:%M:%S %p"))
)
print(
"{:25}".format("Total Cost: ")
+ "{:13}".format("$" + format_number(self.portfolio.cost_value))
)
print(
"{:25}".format("Total Value: ")
+ "{:13}".format("$" + format_number(self.portfolio.market_value))
)
# print daily value
value_gained_day = (
self.portfolio.market_value - self.portfolio.open_market_value
)
self.print_gains("{:13}", value_gained_day, "Today")
# print overall value
value_gained_all = self.portfolio.market_value - self.portfolio.cost_value
self.print_gains("{:13}", value_gained_all, "Overall")
return
def print_new_table(
self,
stock_cols=list(_stock_column_formatters.keys()),
portfolio_cols=list(_portfolio_column_formatters.keys()),
):
# print heading
print("\nPortfolio Summary:\n")
# print the heading
heading = "\t"
divider = "\t"
for col in stock_cols + portfolio_cols:
column = _stock_column_formatters.get(
col
) or _portfolio_column_formatters.get(col)
heading += ("{:" + str(column.width) + "}").format(column.header)
divider += "-" * column.width
print(heading + "\n" + divider)
# now print every portfolio entry
for i, entry in enumerate(self.portfolio.stocks.values()):
stock = entry.stock
line = "\t"
highlight_color = Back.LIGHTBLACK_EX if i % 2 == 0 else Back.RESET
line += highlight_color
for i, col in enumerate(stock_cols + portfolio_cols):
col_formatter = _stock_column_formatters.get(col)
is_stock = col_formatter != None
if not is_stock:
col_formatter = _portfolio_column_formatters.get(col)
cell_data = col_formatter.generator(stock if is_stock else entry)
line += cell_data.color + (
"{:" + str(col_formatter.width) + "}"
).format(cell_data.value)
# print the entry
line += Style.RESET_ALL
print(line)
# TODO: print totals line
self.print_overall_summary()
return