neurostats-API 0.0.3__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- neurostats_API/__init__.py +1 -0
- neurostats_API/cli.py +35 -0
- neurostats_API/fetchers/__init__.py +1 -0
- neurostats_API/fetchers/balance_sheet.py +0 -0
- neurostats_API/fetchers/base.py +59 -0
- neurostats_API/fetchers/finance_overview.py +501 -0
- neurostats_API/fetchers/month_revenue.py +0 -0
- neurostats_API/fetchers/profit_lose.py +0 -0
- neurostats_API/fetchers/tech.py +312 -0
- neurostats_API/fetchers/value_invest.py +89 -0
- neurostats_API/main.py +27 -0
- neurostats_API/utils/__init__.py +2 -0
- neurostats_API/utils/datetime.py +21 -0
- neurostats_API/utils/db_client.py +10 -0
- neurostats_API/utils/fetcher.py +1137 -0
- neurostats_API-0.0.3.dist-info/METADATA +440 -0
- neurostats_API-0.0.3.dist-info/RECORD +19 -0
- neurostats_API-0.0.3.dist-info/WHEEL +5 -0
- neurostats_API-0.0.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
|
|
1
|
+
__version__='0.0.3'
|
neurostats_API/cli.py
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
import argparse
|
2
|
+
import pprint
|
3
|
+
from fetchers.value_invest import ValueFetcher
|
4
|
+
import pandas as pd
|
5
|
+
pp = pprint.PrettyPrinter(
|
6
|
+
indent = 2
|
7
|
+
)
|
8
|
+
|
9
|
+
pd.options.display.max_rows = 4
|
10
|
+
pd.options.display.max_columns = 4
|
11
|
+
|
12
|
+
def main():
|
13
|
+
parser = argparse.ArgumentParser(
|
14
|
+
prog="Neurostats Datasets fetching",
|
15
|
+
description="Main function of fetching Neurostat Dataset",
|
16
|
+
)
|
17
|
+
|
18
|
+
parser.add_argument("--ticker", type=str, default="2330")
|
19
|
+
parser.add_argument("--start_date", type=str, default="2018-01-01")
|
20
|
+
parser.add_argument("--end_date", type=str, default="2199-12-31")
|
21
|
+
|
22
|
+
args = parser.parse_args()
|
23
|
+
|
24
|
+
fetcher = ValueFetcher(args.ticker)
|
25
|
+
|
26
|
+
query_data = fetcher.query_data()
|
27
|
+
print("{")
|
28
|
+
for key, value in query_data.items():
|
29
|
+
print(f"\t\"{key}\":")
|
30
|
+
|
31
|
+
print(f"{value}")
|
32
|
+
print("}")
|
33
|
+
|
34
|
+
if (__name__ == "__main__"):
|
35
|
+
main()
|
@@ -0,0 +1 @@
|
|
1
|
+
from .finance_overview import FinanceOverviewFetcher
|
File without changes
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from pymongo import MongoClient
|
2
|
+
import pandas as pd
|
3
|
+
import json
|
4
|
+
import pytz
|
5
|
+
from datetime import datetime, timedelta, date
|
6
|
+
from utils import StatsDateTime
|
7
|
+
import yaml
|
8
|
+
from .utils.db_client import shared_client
|
9
|
+
|
10
|
+
class StatsFetcher:
|
11
|
+
|
12
|
+
def __init__(self, ticker):
|
13
|
+
self.ticker = ticker
|
14
|
+
self.db = shared_client[
|
15
|
+
"company"] # Replace with your database name
|
16
|
+
self.collection = self.db["twse_stats"]
|
17
|
+
|
18
|
+
self.timezone = pytz.timezone("Asia/Taipei")
|
19
|
+
|
20
|
+
self.target_metric_dict = {
|
21
|
+
'value': ['value'],
|
22
|
+
'value_and_percentage': ['value', 'percentage'],
|
23
|
+
'percentage': ['percentage'],
|
24
|
+
'grand_total': ['grand_total'],
|
25
|
+
'grand_total_values': ['grand_total', 'grand_total_percentage'],
|
26
|
+
'grand_total_percentage': ['grand_total_percentage'],
|
27
|
+
'growth': [f'YoY_{i}' for i in [1, 3, 5, 10]],
|
28
|
+
'grand_total_growth': [f"YoY_{i}" for i in [1, 3, 5, 10]]
|
29
|
+
}
|
30
|
+
|
31
|
+
def prepare_query(self):
|
32
|
+
return [
|
33
|
+
{
|
34
|
+
"$match": {
|
35
|
+
"ticker": self.ticker,
|
36
|
+
}
|
37
|
+
},
|
38
|
+
]
|
39
|
+
|
40
|
+
def collect_data(self, start_date, end_date):
|
41
|
+
pipeline = self.prepare_query(start_date, end_date)
|
42
|
+
|
43
|
+
fetched_data = list(self.collection.aggregate(pipeline))
|
44
|
+
|
45
|
+
return fetched_data[0]
|
46
|
+
|
47
|
+
def str_to_datetime(self, date_str):
|
48
|
+
year, month, day = [int(num) for num in date_str.split("-")]
|
49
|
+
|
50
|
+
date = datetime.strptime(date_str, "%Y-%m-%d")
|
51
|
+
date = self.timezone.localize(date)
|
52
|
+
|
53
|
+
season = (month - 1) // 3 + 1
|
54
|
+
|
55
|
+
return StatsDateTime(date, year, month, day, season)
|
56
|
+
|
57
|
+
|
58
|
+
class StatsProcessor:
|
59
|
+
pass
|
@@ -0,0 +1,501 @@
|
|
1
|
+
from .base import StatsFetcher, StatsProcessor, StatsDateTime
|
2
|
+
import json
|
3
|
+
import pandas as pd
|
4
|
+
from utils import StatsDateTime
|
5
|
+
import yaml
|
6
|
+
|
7
|
+
|
8
|
+
class FinanceOverviewFetcher(StatsFetcher):
|
9
|
+
"""
|
10
|
+
對應iFa.ai -> 財務分析 -> 重要指標(finance_overview)
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, ticker):
|
14
|
+
super().__init__(ticker)
|
15
|
+
|
16
|
+
with open("./tools/finance_overview_dict.yaml") as f:
|
17
|
+
self.target_fields = yaml.safe_load(f)
|
18
|
+
|
19
|
+
with open("./tools/seasonal_data_field_dict.txt") as f:
|
20
|
+
self.inverse_dict = json.load(f)
|
21
|
+
|
22
|
+
def prepare_query(self, target_year, target_season):
|
23
|
+
|
24
|
+
pipeline = super().prepare_query()
|
25
|
+
|
26
|
+
target_query = {
|
27
|
+
"year": "$$target_season_data.year",
|
28
|
+
"season": "$$target_season_data.season",
|
29
|
+
}
|
30
|
+
|
31
|
+
for key, target_sets in self.target_fields.items():
|
32
|
+
try:
|
33
|
+
small_target = target_sets['field']
|
34
|
+
big_target = self.inverse_dict[
|
35
|
+
small_target] # balance_sheet/profit_lose/cash_flow
|
36
|
+
value_index = target_sets['value'] # "金額" or "%"
|
37
|
+
|
38
|
+
target_query.update({
|
39
|
+
f"{key}":
|
40
|
+
f"$$target_season_data.{big_target}.{small_target}.{value_index}"
|
41
|
+
})
|
42
|
+
except Exception:
|
43
|
+
continue
|
44
|
+
|
45
|
+
pipeline.append({
|
46
|
+
"$project": {
|
47
|
+
"_id": 0,
|
48
|
+
"ticker": 1,
|
49
|
+
"company_name": 1,
|
50
|
+
"seasonal_data": {
|
51
|
+
"$map": {
|
52
|
+
"input": {
|
53
|
+
"$filter": {
|
54
|
+
"input": "$seasonal_data",
|
55
|
+
"as": "season",
|
56
|
+
"cond": {
|
57
|
+
"$and": [{
|
58
|
+
"$eq": ["$$season.year", target_year]
|
59
|
+
}, {
|
60
|
+
"$eq":
|
61
|
+
["$$season.season", target_season]
|
62
|
+
}]
|
63
|
+
}
|
64
|
+
}
|
65
|
+
},
|
66
|
+
"as": "target_season_data",
|
67
|
+
"in": target_query
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
})
|
72
|
+
|
73
|
+
return pipeline
|
74
|
+
|
75
|
+
def collect_data(self, target_year, target_season):
|
76
|
+
pipeline = self.prepare_query(target_year, target_season)
|
77
|
+
|
78
|
+
fetched_data = self.collection.aggregate(pipeline)
|
79
|
+
|
80
|
+
fetched_data = list(fetched_data)
|
81
|
+
|
82
|
+
return fetched_data[0]
|
83
|
+
|
84
|
+
def query_data(self):
|
85
|
+
today = StatsDateTime.get_today()
|
86
|
+
|
87
|
+
fetched_data = self.collect_data(2024, 2)
|
88
|
+
finance_dict = fetched_data['seasonal_data'][0]
|
89
|
+
FinanceOverviewProcessor.process_all(finance_dict)
|
90
|
+
fetched_data['seasonal_data'] = finance_dict
|
91
|
+
return fetched_data
|
92
|
+
|
93
|
+
|
94
|
+
class FinanceOverviewProcessor(StatsProcessor):
|
95
|
+
|
96
|
+
@classmethod
|
97
|
+
def process_all(cls, finance_dict):
|
98
|
+
methods = [
|
99
|
+
cls.cal_EBIT,
|
100
|
+
cls.cal_share_outstanding,
|
101
|
+
cls.cal_fcf,
|
102
|
+
cls.cal_revenue_per_share,
|
103
|
+
cls.cal_gross_per_share,
|
104
|
+
cls.cal_operating_income_per_share,
|
105
|
+
cls.cal_operating_cash_flow_per_share,
|
106
|
+
cls.fcf_per_share,
|
107
|
+
cls.cal_roa,
|
108
|
+
cls.cal_roe,
|
109
|
+
cls.cal_gross_over_asset,
|
110
|
+
cls.cal_roce,
|
111
|
+
cls.cal_gross_profit_marginal,
|
112
|
+
cls.cal_operation_profit_rate,
|
113
|
+
cls.cal_operating_cash_flow_profit_rate,
|
114
|
+
cls.cal_dso,
|
115
|
+
cls.cal_account_receive_over_revenue,
|
116
|
+
cls.cal_dpo,
|
117
|
+
cls.cal_inventories_cycle_ratio,
|
118
|
+
cls.cal_dio,
|
119
|
+
cls.cal_inventories_revenue_ratio,
|
120
|
+
cls.cal_cash_of_conversion_cycle,
|
121
|
+
cls.cal_asset_turnover,
|
122
|
+
cls.cal_application_turnover,
|
123
|
+
cls.cal_current_ratio,
|
124
|
+
cls.cal_quick_ratio,
|
125
|
+
cls.cal_debt_to_equity_ratio,
|
126
|
+
cls.cal_net_debt_to_equity_ratio,
|
127
|
+
cls.cal_interest_coverage_ratio,
|
128
|
+
cls.cal_debt_to_operating_cash_flow,
|
129
|
+
cls.cal_debt_to_free_cash_flow,
|
130
|
+
cls.cal_cash_flow_ratio
|
131
|
+
]
|
132
|
+
|
133
|
+
for method in methods:
|
134
|
+
method(finance_dict)
|
135
|
+
|
136
|
+
@classmethod
|
137
|
+
def cal_EBIT(cls, finance_dict):
|
138
|
+
"""
|
139
|
+
計算EBIT
|
140
|
+
EBIT = 營業收入 - 營業成本 - 營業費用
|
141
|
+
"""
|
142
|
+
try:
|
143
|
+
finance_dict['EBIT'] = (finance_dict['revenue'] -
|
144
|
+
finance_dict['operating_cost'] -
|
145
|
+
finance_dict['operating_expenses'])
|
146
|
+
except (KeyError, TypeError) as e:
|
147
|
+
finance_dict['EBIT'] = None
|
148
|
+
print(f"Error calculating EBIT: {e}")
|
149
|
+
|
150
|
+
@classmethod
|
151
|
+
def cal_fcf(cls, finance_dict):
|
152
|
+
"""
|
153
|
+
計算自由現金流(FCF):
|
154
|
+
自由現金流 = 營業現金流 + 投資現金流
|
155
|
+
"""
|
156
|
+
try:
|
157
|
+
finance_dict["fcf"] = (finance_dict["operating_cash_flow"] +
|
158
|
+
finance_dict["financing_cash_flow"])
|
159
|
+
except Exception as e:
|
160
|
+
finance_dict['fcf'] = None
|
161
|
+
print(f"Error calculating FCF: {e}")
|
162
|
+
|
163
|
+
@classmethod
|
164
|
+
def cal_share_outstanding(cls, finance_dict):
|
165
|
+
"""
|
166
|
+
計算流通股數
|
167
|
+
流通股數 = 本期淨利 ÷ 基本每股盈餘
|
168
|
+
"""
|
169
|
+
try:
|
170
|
+
finance_dict["share_outstanding"] = (finance_dict['net_income'] /
|
171
|
+
finance_dict['eps'])
|
172
|
+
except KeyError as e:
|
173
|
+
finance_dict['share_outstanding'] = None
|
174
|
+
print(f"share_outstanding failed because of {str(e)}")
|
175
|
+
|
176
|
+
@classmethod
|
177
|
+
def cal_revenue_per_share(cls, finance_dict):
|
178
|
+
"""
|
179
|
+
計算每股營收
|
180
|
+
每股營收 = 營業收入 / 在外流通股數
|
181
|
+
"""
|
182
|
+
try:
|
183
|
+
finance_dict['revenue_per_share'] = (
|
184
|
+
finance_dict['revenue'] / finance_dict['share_outstanding'])
|
185
|
+
except KeyError as e:
|
186
|
+
finance_dict['revenue_per_share'] = None
|
187
|
+
print(f"revenue_per_share failed because of {str(e)}")
|
188
|
+
|
189
|
+
@classmethod
|
190
|
+
def cal_gross_per_share(cls, finance_dict):
|
191
|
+
"""
|
192
|
+
計算每股毛利
|
193
|
+
= (當期營業毛利)÷(當期在外流通股數)
|
194
|
+
"""
|
195
|
+
|
196
|
+
try:
|
197
|
+
finance_dict['gross_per_share'] = (
|
198
|
+
finance_dict['gross_profit'] /
|
199
|
+
finance_dict['share_outstanding'])
|
200
|
+
except KeyError as e:
|
201
|
+
finance_dict['gross_per_share'] = None
|
202
|
+
print(f"gross_per_share failed because of {str(e)}")
|
203
|
+
|
204
|
+
@classmethod
|
205
|
+
def cal_operating_income_per_share(cls, finance_dict):
|
206
|
+
"""
|
207
|
+
計算每股營業利益
|
208
|
+
每股營業利益= (當期營業利益)÷(當期在外流通股數)
|
209
|
+
"""
|
210
|
+
try:
|
211
|
+
finance_dict['operating_income_per_share'] = (
|
212
|
+
finance_dict['operating_income'] /
|
213
|
+
finance_dict['share_outstanding'])
|
214
|
+
except KeyError as e:
|
215
|
+
finance_dict['operating_income_per_share'] = None
|
216
|
+
print(f"operating_income_per_share failed because of {str(e)}")
|
217
|
+
|
218
|
+
@classmethod
|
219
|
+
def cal_operating_cash_flow_per_share(cls, finance_dict):
|
220
|
+
"""
|
221
|
+
計算每股營業現金流
|
222
|
+
= (當期營業現金流) ÷(當期在外流通股數)
|
223
|
+
"""
|
224
|
+
try:
|
225
|
+
finance_dict["operating_cash_flow_per_share"] = (
|
226
|
+
finance_dict["operating_cash_flow"] /
|
227
|
+
finance_dict['share_outstanding'])
|
228
|
+
except KeyError as e:
|
229
|
+
finance_dict['operating_cash_flow_per_share'] = None
|
230
|
+
print(f'operating_cash_flow_per_share because of {str(e)}')
|
231
|
+
|
232
|
+
@classmethod
|
233
|
+
def fcf_per_share(cls, finance_dict):
|
234
|
+
"""
|
235
|
+
計算每股自由現金流
|
236
|
+
每股自由現金流 = (當期自由現金流) ÷(當期在外流通股數)
|
237
|
+
"""
|
238
|
+
try:
|
239
|
+
finance_dict['fcf_per_share'] = (finance_dict['fcf'] /
|
240
|
+
finance_dict['share_outstanding'])
|
241
|
+
except KeyError as e:
|
242
|
+
finance_dict['fcf_per_share'] = None
|
243
|
+
print(f"fcf_per_share failed because of {str(e)}")
|
244
|
+
|
245
|
+
# 盈利能力
|
246
|
+
|
247
|
+
@classmethod
|
248
|
+
def cal_roa(cls, finance_dict):
|
249
|
+
"""
|
250
|
+
計算資產報酬率(ROA)
|
251
|
+
ROA = [ 本期淨利 + 利息費用 × (1-有效稅率) ] ÷(資產總額)
|
252
|
+
"""
|
253
|
+
finance_dict["roa"] = (
|
254
|
+
finance_dict['net_income'] + finance_dict['interest'] +
|
255
|
+
(1 * 0.1) # 有效稅率需要改,這裡先設0.1
|
256
|
+
) / finance_dict['inventories']
|
257
|
+
|
258
|
+
@classmethod
|
259
|
+
def cal_roe(cls, finance_dict):
|
260
|
+
"""
|
261
|
+
計算股東權益報酬率(ROE)
|
262
|
+
ROE = (本期淨利) ÷(權益總額)
|
263
|
+
"""
|
264
|
+
finance_dict['roe'] = (finance_dict['net_income'] /
|
265
|
+
finance_dict['equity'])
|
266
|
+
|
267
|
+
@classmethod
|
268
|
+
def cal_gross_over_asset(cls, finance_dict):
|
269
|
+
"""
|
270
|
+
計算營業毛利/總資產
|
271
|
+
"""
|
272
|
+
finance_dict['gross_over_asset'] = (finance_dict['gross_profit'] /
|
273
|
+
finance_dict['total_asset'])
|
274
|
+
|
275
|
+
@classmethod
|
276
|
+
def cal_roce(cls, finance_dict):
|
277
|
+
"""
|
278
|
+
計算資本運用報酬率(ROCE):
|
279
|
+
ROCE = (稅前淨利+利息費用) / (資產總額-流動負債)
|
280
|
+
"""
|
281
|
+
try:
|
282
|
+
finance_dict['roce'] = (
|
283
|
+
(finance_dict['net_income_before_tax'] +
|
284
|
+
finance_dict['interest']) /
|
285
|
+
(finance_dict['asset'] - finance_dict['current_liabilities']))
|
286
|
+
except KeyError as e:
|
287
|
+
finance_dict['roce'] = None
|
288
|
+
print(f"ROCE failed because of {str(e)}")
|
289
|
+
|
290
|
+
@classmethod
|
291
|
+
def cal_gross_profit_marginal(cls, finance_dict):
|
292
|
+
"""
|
293
|
+
計算營業毛利率(gross profit margin)
|
294
|
+
營業毛利率 = 營業毛利 ÷ 營業收入
|
295
|
+
"""
|
296
|
+
try:
|
297
|
+
finance_dict['gross_profit_margin'] = (
|
298
|
+
finance_dict['gross_profit'] / finance_dict['revenue'])
|
299
|
+
except:
|
300
|
+
finance_dict['gross_profit_margin'] = None
|
301
|
+
print(f"gross_profit_margin failed because of {str(e)}")
|
302
|
+
|
303
|
+
@classmethod
|
304
|
+
def cal_operation_profit_rate(cls, finance_dict):
|
305
|
+
"""
|
306
|
+
計算營業利益率
|
307
|
+
營業利益率 = ( 營業收入-營業成本-營業費用)÷ 營業收入
|
308
|
+
"""
|
309
|
+
try:
|
310
|
+
finance_dict["operation_profit_rate"] = (
|
311
|
+
finance_dict['revenue'] - finance_dict['operating_cost'] -
|
312
|
+
finance_dict['operating_price']) / finance_dict['revenue']
|
313
|
+
except KeyError as e:
|
314
|
+
finance_dict["operation_profit_rate"] = None
|
315
|
+
print(f"operation_profit failed because of {str(e)}")
|
316
|
+
|
317
|
+
@classmethod
|
318
|
+
def cal_operating_cash_flow_profit_rate(cls, finance_dict):
|
319
|
+
"""
|
320
|
+
計算營業現金流利潤率
|
321
|
+
營業現金流利潤率 = 營業活動現金流 ÷ 營業收入
|
322
|
+
"""
|
323
|
+
try:
|
324
|
+
finance_dict["operating_cash_flow_profit_rate"] = (
|
325
|
+
finance_dict["operating_cash_flow"] / finance_dict["revenue"])
|
326
|
+
except KeyError:
|
327
|
+
finance_dict["operating_cash_flow_profit_rate"] = None
|
328
|
+
|
329
|
+
print(
|
330
|
+
f"operating_cash_flow_profit_rate failed because of {str(e)}")
|
331
|
+
|
332
|
+
|
333
|
+
# 成長動能
|
334
|
+
|
335
|
+
"""
|
336
|
+
前四個已經有了 revenue_YoY, gross_prof_YoY, operating_income_YoY, net_income_YoY:
|
337
|
+
後四個在金流,還需要處理
|
338
|
+
"""
|
339
|
+
# 營運指標
|
340
|
+
|
341
|
+
@classmethod
|
342
|
+
def cal_dso(cls, finance_dict):
|
343
|
+
"""
|
344
|
+
計算應收帳款收現天數(DSO)
|
345
|
+
DSO = 365 × (營業收入 ÷ 應收帳款平均餘額)
|
346
|
+
"""
|
347
|
+
finance_dict['dso'] = (
|
348
|
+
365 * (finance_dict['revenue'] / finance_dict['account_pay']))
|
349
|
+
|
350
|
+
@classmethod
|
351
|
+
def cal_account_receive_over_revenue(cls, finance_dict):
|
352
|
+
"""
|
353
|
+
計算應收帳款佔營收比率
|
354
|
+
= 應收帳款平均餘額 ÷ 營業收入
|
355
|
+
"""
|
356
|
+
finance_dict["account_receive_over_revenue"] = (
|
357
|
+
finance_dict['account_receive'] / finance_dict['revenue'])
|
358
|
+
|
359
|
+
@classmethod
|
360
|
+
def cal_dpo(cls, finance_dict):
|
361
|
+
"""
|
362
|
+
計算應付帳款週轉天數
|
363
|
+
DPO = 365天 ÷ (銷貨成本÷平均應付帳款)
|
364
|
+
"""
|
365
|
+
finance_dict["dpo"] = (
|
366
|
+
365 *
|
367
|
+
(finance_dict['operating_cost'] / finance_dict['account_pay']))
|
368
|
+
|
369
|
+
@classmethod
|
370
|
+
def cal_inventories_cycle_ratio(cls, finance_dict):
|
371
|
+
"""
|
372
|
+
計算存貨周轉率
|
373
|
+
= 銷貨成本 ÷ 存貨
|
374
|
+
"""
|
375
|
+
|
376
|
+
finance_dict["inventories_cycle_ratio"] = (
|
377
|
+
finance_dict['operating_cost'] / finance_dict['inventories'])
|
378
|
+
|
379
|
+
@classmethod
|
380
|
+
def cal_dio(cls, finance_dict):
|
381
|
+
"""
|
382
|
+
計算 存貨週轉天數
|
383
|
+
DIO = 365天 ÷ (銷貨成本 ÷ 存貨)
|
384
|
+
MUDA MUDA MUDA MUDA !!!
|
385
|
+
"""
|
386
|
+
finance_dict["dio"] = (finance_dict["operating_cost"] /
|
387
|
+
finance_dict["inventories"])
|
388
|
+
|
389
|
+
@classmethod
|
390
|
+
def cal_inventories_revenue_ratio(cls, finance_dict):
|
391
|
+
"""
|
392
|
+
計算存貨佔營收比率
|
393
|
+
存貨佔營收比= 存貨 ÷ 營業收入
|
394
|
+
"""
|
395
|
+
finance_dict["inventories_revenue_ratio"] = (
|
396
|
+
finance_dict['inventories'] / finance_dict['revenue'])
|
397
|
+
|
398
|
+
@classmethod
|
399
|
+
def cal_cash_of_conversion_cycle(cls, finance_dict):
|
400
|
+
"""
|
401
|
+
計算現金循環週期
|
402
|
+
存貨週轉天數 + 應收帳款週轉天數 - 應付帳款週轉天數
|
403
|
+
"""
|
404
|
+
finance_dict["cash_of_conversion_cycle"] = (finance_dict["dio"] +
|
405
|
+
finance_dict["dso"] -
|
406
|
+
finance_dict['dpo'])
|
407
|
+
|
408
|
+
@classmethod
|
409
|
+
def cal_asset_turnover(cls, finance_dict):
|
410
|
+
finance_dict["asset_turnover"] = (finance_dict["revenue"] /
|
411
|
+
finance_dict["inventories"])
|
412
|
+
|
413
|
+
@classmethod
|
414
|
+
def cal_application_turnover(cls, finance_dict):
|
415
|
+
finance_dict['applcation_turnover'] = (finance_dict['revenue'] /
|
416
|
+
finance_dict["application"])
|
417
|
+
|
418
|
+
@classmethod
|
419
|
+
def cal_current_ratio(cls, finance_dict):
|
420
|
+
"""
|
421
|
+
計算流動比率 = 流動資產 / 流動負債
|
422
|
+
"""
|
423
|
+
try:
|
424
|
+
finance_dict['current_ratio'] = finance_dict[
|
425
|
+
'current_assets'] / finance_dict['current_liabilities']
|
426
|
+
except (KeyError, ZeroDivisionError, TypeError) as e:
|
427
|
+
finance_dict['current_ratio'] = None
|
428
|
+
print(f"Error calculating current ratio: {e}")
|
429
|
+
|
430
|
+
@classmethod
|
431
|
+
def cal_quick_ratio(cls, finance_dict):
|
432
|
+
try:
|
433
|
+
# 速動比率 = (流動資產 - 存貨) / 流動負債
|
434
|
+
finance_dict['quick_ratio'] = (
|
435
|
+
finance_dict['current_assets'] - finance_dict['inventories']
|
436
|
+
) / finance_dict['current_liabilities']
|
437
|
+
except (KeyError, ZeroDivisionError, TypeError) as e:
|
438
|
+
finance_dict['quick_ratio'] = None
|
439
|
+
print(f"Error calculating quick ratio: {e}")
|
440
|
+
|
441
|
+
@classmethod
|
442
|
+
def cal_debt_to_equity_ratio(cls, finance_dict):
|
443
|
+
try:
|
444
|
+
# 負債權益比率 = 總負債 / 股東權益
|
445
|
+
finance_dict['debt_to_equity_ratio'] = finance_dict[
|
446
|
+
'total_liabilities'] / finance_dict['equity']
|
447
|
+
except (KeyError, ZeroDivisionError, TypeError) as e:
|
448
|
+
finance_dict['debt_to_equity_ratio'] = None
|
449
|
+
print(f"Error calculating debt to equity ratio: {e}")
|
450
|
+
|
451
|
+
@classmethod
|
452
|
+
def cal_net_debt_to_equity_ratio(cls, finance_dict):
|
453
|
+
try:
|
454
|
+
# 淨負債權益比率 = (總負債 - 現金及約當現金) / 股東權益
|
455
|
+
finance_dict['net_debt_to_equity_ratio'] = (
|
456
|
+
finance_dict['total_liabilities'] -
|
457
|
+
finance_dict['cash_and_cash_equivalents']
|
458
|
+
) / finance_dict['equity']
|
459
|
+
except (KeyError, ZeroDivisionError, TypeError) as e:
|
460
|
+
finance_dict['net_debt_to_equity_ratio'] = None
|
461
|
+
print(f"Error calculating net debt to equity ratio: {e}")
|
462
|
+
|
463
|
+
@classmethod
|
464
|
+
def cal_interest_coverage_ratio(cls, finance_dict):
|
465
|
+
try:
|
466
|
+
# 利息保障倍數 = EBIT / 利息費用
|
467
|
+
finance_dict['interest_coverage_ratio'] = finance_dict[
|
468
|
+
'EBIT'] / finance_dict['interest_expense']
|
469
|
+
except (KeyError, ZeroDivisionError, TypeError) as e:
|
470
|
+
finance_dict['interest_coverage_ratio'] = None
|
471
|
+
print(f"Error calculating interest coverage ratio: {e}")
|
472
|
+
|
473
|
+
@classmethod
|
474
|
+
def cal_debt_to_operating_cash_flow(cls, finance_dict):
|
475
|
+
try:
|
476
|
+
# 有息負債 / 營業活動現金流
|
477
|
+
finance_dict['debt_to_operating_cash_flow'] = finance_dict[
|
478
|
+
'interest_bearing_debt'] / finance_dict['operating_cash_flow']
|
479
|
+
except (KeyError, ZeroDivisionError, TypeError) as e:
|
480
|
+
finance_dict['debt_to_operating_cash_flow'] = None
|
481
|
+
print(f"Error calculating debt to operating cash flow: {e}")
|
482
|
+
|
483
|
+
@classmethod
|
484
|
+
def cal_debt_to_free_cash_flow(cls, finance_dict):
|
485
|
+
try:
|
486
|
+
# 有息負債 / 自由現金流
|
487
|
+
finance_dict['debt_to_free_cash_flow'] = finance_dict[
|
488
|
+
'interest_bearing_debt'] / finance_dict['fcf']
|
489
|
+
except (KeyError, ZeroDivisionError, TypeError) as e:
|
490
|
+
finance_dict['debt_to_free_cash_flow'] = None
|
491
|
+
print(f"Error calculating debt to free cash flow: {e}")
|
492
|
+
|
493
|
+
@classmethod
|
494
|
+
def cal_cash_flow_ratio(cls, finance_dict):
|
495
|
+
try:
|
496
|
+
# 現金流量比率 = 營業活動現金流 / 流動負債
|
497
|
+
finance_dict['cash_flow_ratio'] = finance_dict[
|
498
|
+
'operating_cash_flow'] / finance_dict['current_liabilities']
|
499
|
+
except (KeyError, ZeroDivisionError, TypeError) as e:
|
500
|
+
finance_dict['cash_flow_ratio'] = None
|
501
|
+
print(f"Error calculating cash flow ratio: {e}")
|
File without changes
|
File without changes
|