neurostats-API 0.0.3__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.
- 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
|