neurostats-API 0.0.25rc1__py3-none-any.whl → 1.0.0rc2__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 -1
- neurostats_API/async_mode/__init__.py +13 -0
- neurostats_API/async_mode/db/__init__.py +3 -0
- neurostats_API/async_mode/db/base.py +24 -0
- neurostats_API/async_mode/db/tej.py +10 -0
- neurostats_API/async_mode/db/twse.py +8 -0
- neurostats_API/async_mode/db/us.py +9 -0
- neurostats_API/async_mode/db_extractors/__init__.py +20 -0
- neurostats_API/async_mode/db_extractors/base.py +66 -0
- neurostats_API/async_mode/db_extractors/daily/__init__.py +7 -0
- neurostats_API/async_mode/db_extractors/daily/base.py +89 -0
- neurostats_API/async_mode/db_extractors/daily/tej_chip.py +14 -0
- neurostats_API/async_mode/db_extractors/daily/tej_tech.py +12 -0
- neurostats_API/async_mode/db_extractors/daily/twse_chip.py +49 -0
- neurostats_API/async_mode/db_extractors/daily/value.py +93 -0
- neurostats_API/async_mode/db_extractors/daily/yf.py +12 -0
- neurostats_API/async_mode/db_extractors/month_revenue/__init__.py +1 -0
- neurostats_API/async_mode/db_extractors/month_revenue/base.py +140 -0
- neurostats_API/async_mode/db_extractors/month_revenue/twse.py +5 -0
- neurostats_API/async_mode/db_extractors/seasonal/__init__.py +4 -0
- neurostats_API/async_mode/db_extractors/seasonal/balance_sheet.py +19 -0
- neurostats_API/async_mode/db_extractors/seasonal/base.py +152 -0
- neurostats_API/async_mode/db_extractors/seasonal/cashflow.py +10 -0
- neurostats_API/async_mode/db_extractors/seasonal/profit_lose.py +17 -0
- neurostats_API/async_mode/db_extractors/seasonal/tej.py +87 -0
- neurostats_API/async_mode/factory/__init__.py +1 -0
- neurostats_API/async_mode/factory/extractor_factory.py +168 -0
- neurostats_API/async_mode/factory/transformer_factory.py +164 -0
- neurostats_API/async_mode/fetchers/__init__.py +10 -0
- neurostats_API/async_mode/fetchers/balance_sheet.py +31 -0
- neurostats_API/async_mode/fetchers/base.py +48 -0
- neurostats_API/async_mode/fetchers/cash_flow.py +56 -0
- neurostats_API/async_mode/fetchers/finance_overview.py +134 -0
- neurostats_API/async_mode/fetchers/month_revenue.py +35 -0
- neurostats_API/async_mode/fetchers/profit_lose.py +46 -0
- neurostats_API/async_mode/fetchers/tech.py +205 -0
- neurostats_API/async_mode/fetchers/tej.py +88 -0
- neurostats_API/async_mode/fetchers/twse_institution.py +62 -0
- neurostats_API/async_mode/fetchers/twse_margin.py +100 -0
- neurostats_API/async_mode/fetchers/value.py +76 -0
- neurostats_API/config/company_list/ticker_index_industry_map.json +7946 -0
- neurostats_API/config/company_list/us.json +9986 -0
- neurostats_API/{tools → config}/tej_db/tej_db_skip_index.yaml +0 -2
- neurostats_API/{tools → config}/twse/profit_lose.yaml +0 -6
- neurostats_API/fetchers/finance_overview.py +27 -5
- neurostats_API/transformers/__init__.py +40 -0
- neurostats_API/transformers/balance_sheet/__init__.py +2 -0
- neurostats_API/transformers/balance_sheet/base.py +51 -0
- neurostats_API/transformers/balance_sheet/twse.py +76 -0
- neurostats_API/transformers/balance_sheet/us.py +30 -0
- neurostats_API/transformers/base.py +110 -0
- neurostats_API/transformers/cash_flow/__init__.py +2 -0
- neurostats_API/transformers/cash_flow/base.py +114 -0
- neurostats_API/transformers/cash_flow/twse.py +68 -0
- neurostats_API/transformers/cash_flow/us.py +38 -0
- neurostats_API/transformers/daily_chip/__init__.py +1 -0
- neurostats_API/transformers/daily_chip/base.py +5 -0
- neurostats_API/transformers/daily_chip/tej.py +0 -0
- neurostats_API/transformers/daily_chip/twse_chip.py +415 -0
- neurostats_API/transformers/daily_chip/utils/__init__.py +0 -0
- neurostats_API/transformers/daily_chip/utils/institution.py +90 -0
- neurostats_API/transformers/daily_chip/utils/margin_trading.py +2 -0
- neurostats_API/transformers/daily_chip/utils/security_lending.py +0 -0
- neurostats_API/transformers/daily_tech/__init__.py +1 -0
- neurostats_API/transformers/daily_tech/base.py +5 -0
- neurostats_API/transformers/daily_tech/tech.py +84 -0
- neurostats_API/transformers/daily_tech/utils/__init__.py +1 -0
- neurostats_API/transformers/daily_tech/utils/processor.py +251 -0
- neurostats_API/transformers/finance_overview/__init__.py +2 -0
- neurostats_API/transformers/finance_overview/agent_overview.py +55 -0
- neurostats_API/transformers/finance_overview/base.py +824 -0
- neurostats_API/transformers/finance_overview/stats_overview.py +64 -0
- neurostats_API/transformers/month_revenue/__init__.py +1 -0
- neurostats_API/transformers/month_revenue/base.py +60 -0
- neurostats_API/transformers/month_revenue/twse.py +129 -0
- neurostats_API/transformers/profit_lose/__init__.py +2 -0
- neurostats_API/transformers/profit_lose/base.py +82 -0
- neurostats_API/transformers/profit_lose/twse.py +133 -0
- neurostats_API/transformers/profit_lose/us.py +25 -0
- neurostats_API/transformers/tej/__init__.py +1 -0
- neurostats_API/transformers/tej/base.py +149 -0
- neurostats_API/transformers/tej/finance_statement.py +80 -0
- neurostats_API/transformers/value/__init__.py +1 -0
- neurostats_API/transformers/value/base.py +5 -0
- neurostats_API/transformers/value/tej.py +8 -0
- neurostats_API/transformers/value/twse.py +48 -0
- neurostats_API/utils/__init__.py +1 -1
- neurostats_API/utils/data_process.py +10 -6
- neurostats_API/utils/exception.py +8 -0
- neurostats_API/utils/logger.py +21 -0
- neurostats_API-1.0.0rc2.dist-info/METADATA +102 -0
- neurostats_API-1.0.0rc2.dist-info/RECORD +119 -0
- neurostats_API-0.0.25rc1.dist-info/METADATA +0 -858
- neurostats_API-0.0.25rc1.dist-info/RECORD +0 -36
- /neurostats_API/{tools → config}/company_list/tw.json +0 -0
- /neurostats_API/{tools → config}/company_list/us_TradingView_list.json +0 -0
- /neurostats_API/{tools → config}/tej_db/tej_db_index.yaml +0 -0
- /neurostats_API/{tools → config}/tej_db/tej_db_percent_index.yaml +0 -0
- /neurostats_API/{tools → config}/tej_db/tej_db_thousand_index.yaml +0 -0
- /neurostats_API/{tools → config}/twse/balance_sheet.yaml +0 -0
- /neurostats_API/{tools → config}/twse/cash_flow_percentage.yaml +0 -0
- /neurostats_API/{tools → config}/twse/finance_overview_dict.yaml +0 -0
- /neurostats_API/{tools → config}/twse/seasonal_data_field_dict.txt +0 -0
- {neurostats_API-0.0.25rc1.dist-info → neurostats_API-1.0.0rc2.dist-info}/WHEEL +0 -0
- {neurostats_API-0.0.25rc1.dist-info → neurostats_API-1.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
from .base import AsyncBaseFetcher
|
2
|
+
from ..db_extractors import AsyncCashFlowExtractor
|
3
|
+
from neurostats_API.async_mode.factory import get_extractor
|
4
|
+
from neurostats_API.transformers import (
|
5
|
+
TWSECashFlowTransformer, USCashFlowTransformer
|
6
|
+
)
|
7
|
+
from neurostats_API.utils import NoCompanyError, NotSupportedError
|
8
|
+
|
9
|
+
|
10
|
+
class AsyncCashFlowFetcher(AsyncBaseFetcher):
|
11
|
+
|
12
|
+
def __init__(self, ticker, client):
|
13
|
+
|
14
|
+
super().__init__()
|
15
|
+
self.transformer_map = {
|
16
|
+
"tw": TWSECashFlowTransformer,
|
17
|
+
"us": USCashFlowTransformer
|
18
|
+
}
|
19
|
+
self.ticker = ticker
|
20
|
+
try:
|
21
|
+
self.extractor = get_extractor("DB_cash_flow", self.ticker, client)
|
22
|
+
self.company_name = self.extractor.get_company_name()
|
23
|
+
self.zone = self.extractor.get_zone()
|
24
|
+
|
25
|
+
transformer = self.get_transformer(self.zone)
|
26
|
+
self.transformer = transformer(ticker, self.company_name, self.zone)
|
27
|
+
|
28
|
+
except NotSupportedError as e:
|
29
|
+
raise NotSupportedError(
|
30
|
+
f"{self.__class__.__name__} only support {list(self.transformer_map.keys())} companies now, {ticker} is not available"
|
31
|
+
) from e
|
32
|
+
except NoCompanyError:
|
33
|
+
raise
|
34
|
+
|
35
|
+
async def query_data(self, start_date=None, end_date=None):
|
36
|
+
fetched_data = await self.extractor.query_data(start_date, end_date)
|
37
|
+
|
38
|
+
transformed_data = self.transformer.process_transform(fetched_data)
|
39
|
+
|
40
|
+
return transformed_data
|
41
|
+
|
42
|
+
async def query_data_stats_only(self, start_date=None, end_date=None):
|
43
|
+
fetched_data = await self.extractor.query_data(start_date, end_date)
|
44
|
+
|
45
|
+
transformed_data = self.transformer.process_stats_page(fetched_data)
|
46
|
+
|
47
|
+
return transformed_data
|
48
|
+
|
49
|
+
async def query_data_QoQ_and_YoY_only(self, start_date=None, end_date=None):
|
50
|
+
if (self.zone != 'tw'):
|
51
|
+
raise NotSupportedError("query_data_QoQ_and_YoY_only() only supports for TW companies")
|
52
|
+
fetched_data = await self.extractor.query_data(start_date, end_date)
|
53
|
+
|
54
|
+
transformed_data = self.transformer.process_QoQ(fetched_data)
|
55
|
+
|
56
|
+
return transformed_data
|
@@ -0,0 +1,134 @@
|
|
1
|
+
from .base import AsyncBaseFetcher
|
2
|
+
from datetime import datetime
|
3
|
+
import importlib.resources as pkg_resources
|
4
|
+
|
5
|
+
from neurostats_API.async_mode.db_extractors import (
|
6
|
+
AsyncBalanceSheetExtractor, AsyncCashFlowExtractor, AsyncProfitLoseExtractor
|
7
|
+
)
|
8
|
+
from neurostats_API.async_mode.factory import get_extractor
|
9
|
+
from neurostats_API.transformers import AgentOverviewTransformer, FinanceOverviewTransformer
|
10
|
+
from neurostats_API.utils import StatsDateTime, StatsProcessor, NoCompanyError, NotSupportedError
|
11
|
+
import numpy as np
|
12
|
+
import pandas as pd
|
13
|
+
import pytz
|
14
|
+
import holidays
|
15
|
+
import warnings
|
16
|
+
|
17
|
+
warnings.filterwarnings(
|
18
|
+
"ignore",
|
19
|
+
category=holidays.deprecations.v1_incompatibility.
|
20
|
+
FutureIncompatibilityWarning
|
21
|
+
)
|
22
|
+
import yaml
|
23
|
+
|
24
|
+
|
25
|
+
class AsyncFinanceOverviewFetcher(AsyncBaseFetcher):
|
26
|
+
"""
|
27
|
+
對應iFa.ai -> 財務分析 -> 重要指標(finance_overview)
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self, ticker, db_client):
|
31
|
+
super().__init__()
|
32
|
+
|
33
|
+
self.target_fields = StatsProcessor.load_yaml(
|
34
|
+
"twse/finance_overview_dict.yaml"
|
35
|
+
)
|
36
|
+
self.inverse_dict = StatsProcessor.load_txt(
|
37
|
+
"twse/seasonal_data_field_dict.txt", json_load=True
|
38
|
+
)
|
39
|
+
try:
|
40
|
+
self.balance_sheet_extractor = get_extractor(
|
41
|
+
"DB_balance_sheet", ticker, db_client
|
42
|
+
)
|
43
|
+
self.profit_lose_extractor = get_extractor(
|
44
|
+
"DB_profit_lose", ticker, db_client
|
45
|
+
)
|
46
|
+
self.cashflow_extractor = get_extractor(
|
47
|
+
"DB_cash_flow", ticker, db_client
|
48
|
+
)
|
49
|
+
|
50
|
+
company_name = self.balance_sheet_extractor.get_company_name()
|
51
|
+
zone = self.balance_sheet_extractor.get_zone()
|
52
|
+
|
53
|
+
if (zone != 'tw'):
|
54
|
+
raise NotSupportedError(None)
|
55
|
+
|
56
|
+
self.transformer = FinanceOverviewTransformer(
|
57
|
+
ticker, company_name, zone
|
58
|
+
)
|
59
|
+
except NotSupportedError as e:
|
60
|
+
raise NotSupportedError("FinanceOverviewFetcher only supports TW corporation now") from e
|
61
|
+
|
62
|
+
async def query_data(self, date=None):
|
63
|
+
|
64
|
+
balance_sheet = await self.balance_sheet_extractor.query_data(
|
65
|
+
end_date=date, get_latest=True
|
66
|
+
)
|
67
|
+
profit_lose = await self.profit_lose_extractor.query_data(
|
68
|
+
end_date=date, get_latest=True
|
69
|
+
)
|
70
|
+
cash_flow = await self.cashflow_extractor.query_data(
|
71
|
+
end_date=date, get_latest=True
|
72
|
+
)
|
73
|
+
|
74
|
+
transformed_data = self.transformer.process_transform(
|
75
|
+
balance_sheet, profit_lose, cash_flow
|
76
|
+
)
|
77
|
+
|
78
|
+
return transformed_data
|
79
|
+
|
80
|
+
|
81
|
+
class AsyncAgentOverviewFetcher(AsyncBaseFetcher):
|
82
|
+
"""
|
83
|
+
用於P1-Agent 的 公司股市經濟概況頁面
|
84
|
+
"""
|
85
|
+
|
86
|
+
def __init__(self, ticker, db_client):
|
87
|
+
from neurostats_API.async_mode.fetchers import AsyncTechFetcher
|
88
|
+
self.ticker = ticker
|
89
|
+
self.tech_fetcher = AsyncTechFetcher(
|
90
|
+
ticker=self.ticker, client=db_client
|
91
|
+
)
|
92
|
+
try:
|
93
|
+
self.balance_sheet_extractor = AsyncBalanceSheetExtractor(
|
94
|
+
ticker=self.ticker, client=db_client
|
95
|
+
)
|
96
|
+
self.profit_lose_extractor = AsyncProfitLoseExtractor(
|
97
|
+
ticker=self.ticker, client=db_client
|
98
|
+
)
|
99
|
+
self.cash_flow_extractor = AsyncCashFlowExtractor(
|
100
|
+
ticker=self.ticker, client=db_client
|
101
|
+
)
|
102
|
+
|
103
|
+
company_name = self.balance_sheet_extractor.get_company_name()
|
104
|
+
zone = self.balance_sheet_extractor.get_zone()
|
105
|
+
|
106
|
+
self.transformer_map = {'us': AgentOverviewTransformer}
|
107
|
+
|
108
|
+
transformer = self.get_transformer(zone)
|
109
|
+
self.transformer = transformer(ticker, company_name, zone)
|
110
|
+
|
111
|
+
except NotSupportedError as e:
|
112
|
+
raise NotSupportedError(
|
113
|
+
f'Agent Overview only supports US corporation now, Got zone={zone}, ticker={ticker}'
|
114
|
+
) from e
|
115
|
+
|
116
|
+
async def query_data(self, date=None):
|
117
|
+
|
118
|
+
tech_data = await self.tech_fetcher.get_daily(
|
119
|
+
start_date=None, end_date=date
|
120
|
+
)
|
121
|
+
balance_sheet = await self.balance_sheet_extractor.query_data(
|
122
|
+
end_date=date
|
123
|
+
)
|
124
|
+
profit_lose = await self.profit_lose_extractor.query_data(end_date=date)
|
125
|
+
cash_flow = await self.cash_flow_extractor.query_data(end_date=date)
|
126
|
+
|
127
|
+
return_data = self.transformer.process_transform(
|
128
|
+
tech_data=tech_data,
|
129
|
+
balance_sheet_list=balance_sheet,
|
130
|
+
profit_lose_list=profit_lose,
|
131
|
+
cash_flow_list=cash_flow
|
132
|
+
)
|
133
|
+
|
134
|
+
return return_data
|
@@ -0,0 +1,35 @@
|
|
1
|
+
from .base import AsyncBaseFetcher
|
2
|
+
from ..db_extractors import AsyncTWSEMonthlyRevenueExtractor
|
3
|
+
from neurostats_API.async_mode.factory import get_extractor
|
4
|
+
from neurostats_API.transformers import TWSEMonthlyRevenueTransformer
|
5
|
+
from neurostats_API.utils import NoCompanyError, NotSupportedError
|
6
|
+
|
7
|
+
|
8
|
+
class AsyncMonthlyRevenueFetcher(AsyncBaseFetcher):
|
9
|
+
|
10
|
+
def __init__(self, ticker, client):
|
11
|
+
self.ticker = ticker
|
12
|
+
self.transformer_map = {
|
13
|
+
"tw": TWSEMonthlyRevenueTransformer,
|
14
|
+
}
|
15
|
+
|
16
|
+
try:
|
17
|
+
self.extractor = get_extractor("TWSE_month_revenue", ticker, client)
|
18
|
+
company_name = self.extractor.get_company_name()
|
19
|
+
zone = self.extractor.get_zone()
|
20
|
+
|
21
|
+
transformer = self.get_transformer(zone)
|
22
|
+
|
23
|
+
self.transformer = transformer(ticker, company_name, zone)
|
24
|
+
except NotSupportedError as e:
|
25
|
+
raise NotSupportedError(
|
26
|
+
"AsyncMonthRevenueFetcher only supports tw company now"
|
27
|
+
) from e
|
28
|
+
|
29
|
+
|
30
|
+
async def query_data(self, start_date=None, end_date=None):
|
31
|
+
fetched_data = await self.extractor.query_data(start_date, end_date)
|
32
|
+
|
33
|
+
transformed_data = self.transformer.process_transform(fetched_data)
|
34
|
+
|
35
|
+
return transformed_data
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from neurostats_API.async_mode.db_extractors import AsyncProfitLoseExtractor
|
2
|
+
from neurostats_API.async_mode.factory import get_extractor
|
3
|
+
from neurostats_API.transformers import (
|
4
|
+
TWSEProfitLoseTransformer,
|
5
|
+
USProfitLoseTransformer
|
6
|
+
)
|
7
|
+
from neurostats_API.utils import (
|
8
|
+
NoCompanyError,
|
9
|
+
NotSupportedError
|
10
|
+
)
|
11
|
+
|
12
|
+
from .base import AsyncBaseFetcher
|
13
|
+
|
14
|
+
class AsyncProfitLoseFetcher(AsyncBaseFetcher):
|
15
|
+
def __init__(
|
16
|
+
self,
|
17
|
+
ticker,
|
18
|
+
client
|
19
|
+
):
|
20
|
+
self.ticker = ticker
|
21
|
+
|
22
|
+
self.transformer_map = {
|
23
|
+
"tw": TWSEProfitLoseTransformer,
|
24
|
+
"us": USProfitLoseTransformer
|
25
|
+
}
|
26
|
+
try:
|
27
|
+
self.extractor = get_extractor("DB_profit_lose", ticker, client)
|
28
|
+
company_name = self.extractor.get_company_name()
|
29
|
+
zone = self.extractor.get_zone()
|
30
|
+
|
31
|
+
transformer = self.get_transformer(zone)
|
32
|
+
self.transformer = transformer(ticker, company_name, zone)
|
33
|
+
|
34
|
+
except NotSupportedError as e:
|
35
|
+
raise NotSupportedError(
|
36
|
+
f"{self.__class__.__name__} only support {list(self.transformer_map.keys())} companies now, {ticker} is not available"
|
37
|
+
) from e
|
38
|
+
|
39
|
+
async def query_data(self, start_date = None, end_date = None):
|
40
|
+
fetched_data = await self.extractor.query_data(start_date, end_date)
|
41
|
+
|
42
|
+
transformed_data = self.transformer.process_transform(
|
43
|
+
fetched_data
|
44
|
+
)
|
45
|
+
|
46
|
+
return transformed_data
|
@@ -0,0 +1,205 @@
|
|
1
|
+
from .base import AsyncBaseFetcher
|
2
|
+
from datetime import datetime
|
3
|
+
from ..db_extractors import (
|
4
|
+
AsyncYFDailyTechDBExtractor, AsyncTEJDailyTechDBExtractor
|
5
|
+
)
|
6
|
+
from neurostats_API.async_mode.factory import get_extractor
|
7
|
+
from neurostats_API.transformers import DailyTechTransformer
|
8
|
+
from neurostats_API.utils import NotSupportedError
|
9
|
+
import pandas as pd
|
10
|
+
import yfinance as yf
|
11
|
+
|
12
|
+
|
13
|
+
class AsyncTechFetcher(AsyncBaseFetcher):
|
14
|
+
|
15
|
+
def __init__(self, ticker, client):
|
16
|
+
self.ticker = ticker
|
17
|
+
self.transformer_map = {
|
18
|
+
"tw": DailyTechTransformer,
|
19
|
+
"us": DailyTechTransformer
|
20
|
+
}
|
21
|
+
try:
|
22
|
+
self.yf_db_extractor = get_extractor("YF_tech", ticker, client)
|
23
|
+
|
24
|
+
self.tej_db_extractor = None
|
25
|
+
if (self.yf_db_extractor.get_zone() == 'tw'):
|
26
|
+
self.tej_db_extractor = get_extractor("TEJ_tech", ticker, client)
|
27
|
+
|
28
|
+
self.company_name = self.yf_db_extractor.get_company_name()
|
29
|
+
self.zone = self.yf_db_extractor.get_zone()
|
30
|
+
|
31
|
+
self.required_cols = [
|
32
|
+
'date', 'open', 'high', 'low', 'close', 'volume'
|
33
|
+
]
|
34
|
+
|
35
|
+
transformer = self.get_transformer(self.zone)
|
36
|
+
self.transformer = transformer(ticker, self.company_name, self.zone)
|
37
|
+
|
38
|
+
except NotSupportedError as e:
|
39
|
+
raise NotSupportedError(
|
40
|
+
f"{self.__class__.__name__} only supports {list(self.transformer_map.keys())}, got {self.zone} with ticker = \"{self.ticker}\""
|
41
|
+
) from e
|
42
|
+
|
43
|
+
async def query_data(self, start_date=None, end_date=None):
|
44
|
+
if (start_date is None):
|
45
|
+
start_date = datetime.strptime("1991-01-01", "%Y-%m-%d")
|
46
|
+
if (end_date is None):
|
47
|
+
end_date = datetime.today()
|
48
|
+
|
49
|
+
fetched_data = await self._fetch_data(start_date, end_date)
|
50
|
+
|
51
|
+
transformed_data = self.transformer.process_transform(fetched_data)
|
52
|
+
|
53
|
+
return transformed_data
|
54
|
+
|
55
|
+
async def get_daily(self, start_date=None, end_date=None):
|
56
|
+
await self.query_data(start_date, end_date)
|
57
|
+
|
58
|
+
return self.transformer.get_daily()
|
59
|
+
|
60
|
+
async def get_weekly(self, start_date=None, end_date=None):
|
61
|
+
await self.query_data(start_date, end_date)
|
62
|
+
|
63
|
+
return self.transformer.get_weekly()
|
64
|
+
|
65
|
+
async def get_monthly(self, start_date=None, end_date=None):
|
66
|
+
await self.query_data(start_date, end_date)
|
67
|
+
|
68
|
+
return self.transformer.get_monthly()
|
69
|
+
|
70
|
+
async def get_quarterly(self, start_date=None, end_date=None):
|
71
|
+
await self.query_data(start_date, end_date)
|
72
|
+
|
73
|
+
return self.transformer.get_quarterly()
|
74
|
+
|
75
|
+
async def get_yearly(self, start_date=None, end_date=None):
|
76
|
+
await self.query_data(start_date, end_date)
|
77
|
+
|
78
|
+
return self.transformer.get_yearly()
|
79
|
+
|
80
|
+
async def _fetch_data(self, start_date, end_date):
|
81
|
+
|
82
|
+
if (self.ticker in ['GSPC', 'IXIC', 'DJI', 'TWII']):
|
83
|
+
full_tick = f'^{self.ticker}'
|
84
|
+
df = self._conduct_yf_search(full_tick, start_date, end_date)
|
85
|
+
|
86
|
+
return df[self.required_cols]
|
87
|
+
|
88
|
+
else:
|
89
|
+
search_fn_map = {
|
90
|
+
'tw': self._conduct_tw_db_search,
|
91
|
+
'us': self._conduct_us_db_search
|
92
|
+
}
|
93
|
+
search_fn = search_fn_map.get(self.zone)
|
94
|
+
df = await search_fn(start_date, end_date)
|
95
|
+
|
96
|
+
return df
|
97
|
+
|
98
|
+
def _conduct_yf_search(self, ticker, start_date, end_date):
|
99
|
+
yf_ticker = yf.Ticker(ticker)
|
100
|
+
origin_df = yf_ticker.history(period="10y")
|
101
|
+
|
102
|
+
if origin_df.empty:
|
103
|
+
return origin_df
|
104
|
+
|
105
|
+
origin_df = origin_df.reset_index()
|
106
|
+
origin_df["Date"] = pd.to_datetime(origin_df["Date"])
|
107
|
+
df = origin_df.rename(
|
108
|
+
columns={
|
109
|
+
"Date": "date",
|
110
|
+
"Open": "open",
|
111
|
+
"High": "high",
|
112
|
+
"Low": "low",
|
113
|
+
"Close": "close",
|
114
|
+
"Volume": "volume"
|
115
|
+
}
|
116
|
+
)
|
117
|
+
|
118
|
+
df = df.loc[(df['date'] >= start_date) & (df['date'] <= end_date)]
|
119
|
+
return df
|
120
|
+
|
121
|
+
async def _conduct_tw_db_search(self, start_date, end_date):
|
122
|
+
search_fns = [
|
123
|
+
self._conduct_tw_db_search_yf,
|
124
|
+
self._conduct_tw_db_search_tej,
|
125
|
+
]
|
126
|
+
|
127
|
+
sync_search_fns = [
|
128
|
+
lambda s_date, e_date: self.
|
129
|
+
_conduct_yf_search(f'{self.ticker}.tw', s_date, e_date),
|
130
|
+
lambda s_date, e_date: self.
|
131
|
+
_conduct_yf_search(f'{self.ticker}.two', s_date, e_date),
|
132
|
+
]
|
133
|
+
|
134
|
+
for search_method in search_fns:
|
135
|
+
try:
|
136
|
+
fetched_data = await search_method(start_date, end_date)
|
137
|
+
if not fetched_data.empty:
|
138
|
+
break
|
139
|
+
except (KeyError, ValueError, TypeError) as e:
|
140
|
+
print(e)
|
141
|
+
continue
|
142
|
+
else: # 找不到資料
|
143
|
+
for search_method in sync_search_fns:
|
144
|
+
fetched_data = search_method(start_date, end_date)
|
145
|
+
if not fetched_data.empty:
|
146
|
+
break
|
147
|
+
else:
|
148
|
+
return pd.DataFrame(self.required_cols)
|
149
|
+
|
150
|
+
return fetched_data[self.required_cols]
|
151
|
+
|
152
|
+
async def _conduct_tw_db_search_yf(self, start_date, end_date):
|
153
|
+
"""
|
154
|
+
尋找從yf與twse爬下來的台股資料
|
155
|
+
"""
|
156
|
+
fetched_data = await self.yf_db_extractor.query_data(
|
157
|
+
start_date, end_date
|
158
|
+
)
|
159
|
+
|
160
|
+
if (len(fetched_data) > 0):
|
161
|
+
df = pd.DataFrame(fetched_data)
|
162
|
+
return df
|
163
|
+
else:
|
164
|
+
return None
|
165
|
+
|
166
|
+
async def _conduct_tw_db_search_tej(self, start_date, end_date):
|
167
|
+
"""
|
168
|
+
尋找從TEJ爬下來的台股資料
|
169
|
+
"""
|
170
|
+
fetched_data = await self.tej_db_extractor.query_data(
|
171
|
+
start_date, end_date
|
172
|
+
)
|
173
|
+
|
174
|
+
if (len(fetched_data) > 0):
|
175
|
+
df = pd.DataFrame(fetched_data)
|
176
|
+
df = df.rename(
|
177
|
+
columns={
|
178
|
+
"open_d": "open",
|
179
|
+
"high_d": "high",
|
180
|
+
"low_d": "low",
|
181
|
+
"close_d": "close",
|
182
|
+
"vol": "volume"
|
183
|
+
}
|
184
|
+
)
|
185
|
+
|
186
|
+
return df
|
187
|
+
else:
|
188
|
+
return None
|
189
|
+
|
190
|
+
def _conduct_us_db_search(self, start_date, end_date):
|
191
|
+
search_fns = [
|
192
|
+
self.yf_db_extractor.query_data, lambda s_date, e_date: self.
|
193
|
+
_conduct_yf_search(f"{self.ticker}", s_date, e_date)
|
194
|
+
]
|
195
|
+
|
196
|
+
for search_method in search_fns:
|
197
|
+
try:
|
198
|
+
df = search_method(start_date, end_date)
|
199
|
+
break
|
200
|
+
except (KeyError, ValueError, TypeError):
|
201
|
+
continue
|
202
|
+
else: # 回傳空df
|
203
|
+
df = pd.DataFrame(columns=self.required_cols)
|
204
|
+
|
205
|
+
return df
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from .base import AsyncBaseFetcher
|
2
|
+
from datetime import datetime
|
3
|
+
from neurostats_API.async_mode.db_extractors import (
|
4
|
+
AsyncTEJFinanceStatementDBExtractor, AsyncTEJSelfSettlementDBExtractor
|
5
|
+
)
|
6
|
+
from neurostats_API.async_mode.factory import get_extractor
|
7
|
+
from neurostats_API.transformers import TEJFinanceStatementTransformer
|
8
|
+
from neurostats_API.utils import NotSupportedError
|
9
|
+
|
10
|
+
|
11
|
+
class AsyncTEJSeasonalFetcher(AsyncBaseFetcher):
|
12
|
+
|
13
|
+
def __init__(
|
14
|
+
self,
|
15
|
+
ticker,
|
16
|
+
client,
|
17
|
+
collection='TEJ_finance_statement',
|
18
|
+
fetch_type='Q'
|
19
|
+
):
|
20
|
+
self.ticker = ticker
|
21
|
+
self.fetch_type = fetch_type
|
22
|
+
self.transformer_map = {"tw": TEJFinanceStatementTransformer}
|
23
|
+
try:
|
24
|
+
self.db_extractor = get_extractor(
|
25
|
+
collection=collection,
|
26
|
+
ticker=ticker,
|
27
|
+
client=client,
|
28
|
+
fetch_type=fetch_type
|
29
|
+
)
|
30
|
+
|
31
|
+
self.company_name = self.db_extractor.get_company_name()
|
32
|
+
self.zone = self.db_extractor.get_zone()
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
transformer = self.get_transformer(self.zone)
|
37
|
+
|
38
|
+
self.transformer = transformer(
|
39
|
+
ticker=ticker,
|
40
|
+
company_name=self.company_name,
|
41
|
+
zone=self.zone,
|
42
|
+
target_column=fetch_type
|
43
|
+
)
|
44
|
+
|
45
|
+
except NotSupportedError as e:
|
46
|
+
raise NotSupportedError(
|
47
|
+
f"{self.__class__.__name__} only support {list(self.transformer_map.keys())} companies now, {ticker} is not available"
|
48
|
+
) from e
|
49
|
+
|
50
|
+
async def query_data(
|
51
|
+
self,
|
52
|
+
start_date=None,
|
53
|
+
end_date=None,
|
54
|
+
fetch_mode='QoQ',
|
55
|
+
use_cal=True,
|
56
|
+
indexes=None
|
57
|
+
):
|
58
|
+
fetched_data = await self.db_extractor.query_data(
|
59
|
+
start_date=None, end_date=end_date
|
60
|
+
) # 先索取目標日期之前的之後再截斷
|
61
|
+
|
62
|
+
if (start_date is None):
|
63
|
+
start_date = datetime.strptime("1991-01-01", "%Y-%m-%d")
|
64
|
+
|
65
|
+
transformed_data = self.transformer.process_transform(
|
66
|
+
fetched_data=fetched_data,
|
67
|
+
fetch_mode=fetch_mode,
|
68
|
+
start_date=start_date,
|
69
|
+
use_cal=use_cal,
|
70
|
+
indexes=indexes
|
71
|
+
) # 轉換並根據開始日期截斷
|
72
|
+
|
73
|
+
return transformed_data
|
74
|
+
|
75
|
+
def _get_extractor(self, collection):
|
76
|
+
EXTRACTOR_MAP = {
|
77
|
+
"TEJ_finance_statement": AsyncTEJFinanceStatementDBExtractor,
|
78
|
+
"TEJ_self_settlement": AsyncTEJSelfSettlementDBExtractor,
|
79
|
+
}
|
80
|
+
|
81
|
+
extractor = EXTRACTOR_MAP.get(collection, None)
|
82
|
+
|
83
|
+
if (extractor is None):
|
84
|
+
raise ValueError(
|
85
|
+
f"{collection} not a regular argument for collection, only {list(EXTRACTOR_MAP.keys())} available"
|
86
|
+
)
|
87
|
+
|
88
|
+
return extractor
|
@@ -0,0 +1,62 @@
|
|
1
|
+
from .base import AsyncBaseFetcher
|
2
|
+
from datetime import datetime, timedelta
|
3
|
+
from ..db_extractors import (
|
4
|
+
AsyncTWSEChipDBExtractor,
|
5
|
+
AsyncYFDailyTechDBExtractor,
|
6
|
+
)
|
7
|
+
from neurostats_API.async_mode.factory import get_extractor
|
8
|
+
from neurostats_API.utils import NotSupportedError
|
9
|
+
from neurostats_API.transformers import TWSEChipTransformer
|
10
|
+
|
11
|
+
|
12
|
+
class AsyncTWSEInstitutionFetcher(AsyncBaseFetcher):
|
13
|
+
|
14
|
+
def __init__(self, ticker, client):
|
15
|
+
self.ticker = ticker
|
16
|
+
self.transformer_map = {"tw": TWSEChipTransformer}
|
17
|
+
try:
|
18
|
+
self.extractor = get_extractor(
|
19
|
+
"TWSE_chip", ticker, client, fetch_type='I'
|
20
|
+
)
|
21
|
+
self.daily_extractor = get_extractor("YF_tech", ticker, client)
|
22
|
+
|
23
|
+
company_name = self.extractor.get_company_name()
|
24
|
+
zone = self.extractor.get_zone()
|
25
|
+
|
26
|
+
transformer = self.get_transformer(zone)
|
27
|
+
self.transformer = transformer(ticker, company_name, zone)
|
28
|
+
|
29
|
+
except NotSupportedError as e:
|
30
|
+
raise NotSupportedError(
|
31
|
+
f"{self.__class__.__name__} only support {list(self.transformer_map.keys())} companies now, {ticker} is not available"
|
32
|
+
) from e
|
33
|
+
|
34
|
+
async def query_data(
|
35
|
+
self, start_date=None, end_date=None, get_latest=False
|
36
|
+
):
|
37
|
+
fetched_data = await self.extractor.query_data(
|
38
|
+
start_date, end_date, get_latest
|
39
|
+
)
|
40
|
+
tech_data = await self.daily_extractor.query_data(
|
41
|
+
start_date, end_date, get_latest
|
42
|
+
)
|
43
|
+
|
44
|
+
fetched_data = {"institution_trading": fetched_data}
|
45
|
+
transformed_data = self.transformer.process_transform(
|
46
|
+
tech_data=tech_data, **fetched_data
|
47
|
+
)
|
48
|
+
|
49
|
+
return transformed_data
|
50
|
+
|
51
|
+
async def query_data_annual(self):
|
52
|
+
"""
|
53
|
+
取一年內的資料
|
54
|
+
"""
|
55
|
+
end_date = datetime.today()
|
56
|
+
start_date = end_date - timedelta(days=365)
|
57
|
+
|
58
|
+
fetched_data = await self.query_data(
|
59
|
+
start_date, end_date, get_latest=False
|
60
|
+
)
|
61
|
+
|
62
|
+
return fetched_data
|