neurostats-API 0.0.25__py3-none-any.whl → 1.0.0__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 +15 -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/__init__.py +2 -0
- neurostats_API/async_mode/fetchers/tej/seasonal_data.py +88 -0
- neurostats_API/async_mode/fetchers/tej/tech.py +34 -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 +22 -2
- 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 +81 -0
- neurostats_API/transformers/balance_sheet/us.py +38 -0
- neurostats_API/transformers/base.py +158 -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 +70 -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 +79 -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 +134 -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 +26 -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.0.dist-info/METADATA +102 -0
- neurostats_API-1.0.0.dist-info/RECORD +121 -0
- neurostats_API-0.0.25.dist-info/METADATA +0 -858
- neurostats_API-0.0.25.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.25.dist-info → neurostats_API-1.0.0.dist-info}/WHEEL +0 -0
- {neurostats_API-0.0.25.dist-info → neurostats_API-1.0.0.dist-info}/top_level.txt +0 -0
neurostats_API/__init__.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
__version__='0.0
|
1
|
+
__version__='1.0.0'
|
2
2
|
|
3
3
|
from .fetchers import (
|
4
4
|
AgentFinanceOverviewFetcher,
|
@@ -12,4 +12,18 @@ from .fetchers import (
|
|
12
12
|
TechFetcher,
|
13
13
|
TEJStockPriceFetcher,
|
14
14
|
ProfitLoseFetcher,
|
15
|
+
)
|
16
|
+
|
17
|
+
from .async_mode import (
|
18
|
+
AsyncAgentOverviewFetcher,
|
19
|
+
AsyncBalanceSheetFetcher,
|
20
|
+
AsyncCashFlowFetcher,
|
21
|
+
AsyncFinanceOverviewFetcher,
|
22
|
+
AsyncMonthlyRevenueFetcher,
|
23
|
+
AsyncProfitLoseFetcher,
|
24
|
+
AsyncTechFetcher,
|
25
|
+
AsyncTEJSeasonalFetcher,
|
26
|
+
AsyncTWSEInstitutionFetcher,
|
27
|
+
AsyncTWSEMarginFetcher,
|
28
|
+
AsyncTWSEStatsValueFetcher
|
15
29
|
)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from .fetchers import (
|
2
|
+
AsyncAgentOverviewFetcher,
|
3
|
+
AsyncBalanceSheetFetcher,
|
4
|
+
AsyncCashFlowFetcher,
|
5
|
+
AsyncFinanceOverviewFetcher,
|
6
|
+
AsyncMonthlyRevenueFetcher,
|
7
|
+
AsyncProfitLoseFetcher,
|
8
|
+
AsyncTechFetcher,
|
9
|
+
AsyncTEJSeasonalFetcher,
|
10
|
+
AsyncTWSEInstitutionFetcher,
|
11
|
+
AsyncTWSEMarginFetcher,
|
12
|
+
AsyncTWSEStatsValueFetcher
|
13
|
+
)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from pymongo import AsyncMongoClient
|
2
|
+
|
3
|
+
class BaseDBClient:
|
4
|
+
def __init__(self, client: AsyncMongoClient, zone= None):
|
5
|
+
self.client = client
|
6
|
+
self.zone = zone or "tw" # 預設台灣
|
7
|
+
self.db = self._select_db()
|
8
|
+
|
9
|
+
def _select_db(self):
|
10
|
+
"""
|
11
|
+
根據 zone 選擇對應的資料庫
|
12
|
+
"""
|
13
|
+
zone_db_map = {
|
14
|
+
"tw": "company_test",
|
15
|
+
"us": "company_us"
|
16
|
+
}
|
17
|
+
db_name = zone_db_map.get(self.zone, "company_test")
|
18
|
+
return self.client[db_name]
|
19
|
+
|
20
|
+
def get_collection(self, collection_name: str):
|
21
|
+
"""
|
22
|
+
傳回指定 collection
|
23
|
+
"""
|
24
|
+
return self.db[collection_name]
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from .base import BaseDBClient
|
2
|
+
import os
|
3
|
+
from pymongo import AsyncMongoClient
|
4
|
+
from dotenv import load_dotenv
|
5
|
+
|
6
|
+
class TEJDBClient(BaseDBClient):
|
7
|
+
def __init__(self, mongo_uri):
|
8
|
+
"""初始化時接收 MongoDB 連接 URI"""
|
9
|
+
super().__init__(mongo_uri, zone = 'tw')
|
10
|
+
self.db = self.client["company_test"]
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from .daily import (
|
2
|
+
AsyncTEJDailyTechDBExtractor,
|
3
|
+
AsyncYFDailyTechDBExtractor,
|
4
|
+
AsyncDailyValueDBExtractor,
|
5
|
+
AsyncTEJDailyValueDBExtractor,
|
6
|
+
AsyncTWSEChipDBExtractor,
|
7
|
+
|
8
|
+
)
|
9
|
+
|
10
|
+
from .month_revenue import (
|
11
|
+
AsyncTWSEMonthlyRevenueExtractor
|
12
|
+
)
|
13
|
+
|
14
|
+
from .seasonal import (
|
15
|
+
AsyncBalanceSheetExtractor,
|
16
|
+
AsyncProfitLoseExtractor,
|
17
|
+
AsyncCashFlowExtractor,
|
18
|
+
AsyncTEJFinanceStatementDBExtractor,
|
19
|
+
AsyncTEJSelfSettlementDBExtractor
|
20
|
+
)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import abc
|
2
|
+
from datetime import datetime
|
3
|
+
from ..db import TWSEDBClient, USDBClient
|
4
|
+
import json
|
5
|
+
import pytz
|
6
|
+
from neurostats_API.utils import StatsDateTime, StatsProcessor, YoY_Calculator, NoCompanyError
|
7
|
+
|
8
|
+
class BaseDBExtractor(abc.ABC):
|
9
|
+
def __init__(self, ticker, client):
|
10
|
+
self.ticker = ticker
|
11
|
+
self.client = client
|
12
|
+
self.timezone = pytz.timezone("Asia/Taipei")
|
13
|
+
self.tw_company_list = StatsProcessor.load_json("company_list/tw.json")
|
14
|
+
self.us_company_list = StatsProcessor.load_json("company_list/us.json")
|
15
|
+
|
16
|
+
self.company_name, self.zone = self._set_company_name(self.ticker)
|
17
|
+
|
18
|
+
self.db_client = self._set_db_connection()
|
19
|
+
|
20
|
+
def _set_company_name(self, ticker):
|
21
|
+
company_lists = [
|
22
|
+
(self.tw_company_list, 'tw'),
|
23
|
+
(self.us_company_list, 'us')
|
24
|
+
]
|
25
|
+
|
26
|
+
for company_list, zone in company_lists:
|
27
|
+
company_name = company_list.get(ticker, None)
|
28
|
+
if (company_name):
|
29
|
+
return company_name, zone
|
30
|
+
# 沒找到公司名稱
|
31
|
+
raise NoCompanyError(f"Ticker-{ticker} not found in any company lists")
|
32
|
+
|
33
|
+
def _set_db_connection(self):
|
34
|
+
db_map = {
|
35
|
+
'tw': TWSEDBClient,
|
36
|
+
'us': USDBClient
|
37
|
+
}
|
38
|
+
db_client = db_map.get(self.zone, TWSEDBClient)
|
39
|
+
return db_client(self.client)
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
async def query_data(self):
|
44
|
+
"""
|
45
|
+
user使用的接口
|
46
|
+
"""
|
47
|
+
return NotImplementedError()
|
48
|
+
|
49
|
+
@abc.abstractmethod
|
50
|
+
def _prepare_query(self):
|
51
|
+
pass
|
52
|
+
|
53
|
+
def get_company_name(self):
|
54
|
+
"""
|
55
|
+
回傳公司
|
56
|
+
"""
|
57
|
+
return self.company_name
|
58
|
+
|
59
|
+
def get_zone(self):
|
60
|
+
"""
|
61
|
+
公司區域(TW, US)
|
62
|
+
"""
|
63
|
+
return self.zone
|
64
|
+
|
65
|
+
def _get_today(self):
|
66
|
+
return datetime.today()
|
@@ -0,0 +1,89 @@
|
|
1
|
+
from ..base import BaseDBExtractor
|
2
|
+
from datetime import datetime
|
3
|
+
from neurostats_API.utils import NotSupportedError
|
4
|
+
from pymongo import ASCENDING, DESCENDING
|
5
|
+
|
6
|
+
|
7
|
+
class BaseDailyTechDBExtractor(BaseDBExtractor):
|
8
|
+
|
9
|
+
def __init__(self, ticker, client):
|
10
|
+
super().__init__(ticker, client)
|
11
|
+
|
12
|
+
self.collection_name_map = None
|
13
|
+
self.collection_name = self._get_collection_name()
|
14
|
+
|
15
|
+
if (self.collection_name is None):
|
16
|
+
raise NotSupportedError(
|
17
|
+
f"{self.__class__.__name__} only supports {list(self.collection_name_map.keys())}, got {self.zone} with ticker = \"{self.ticker}\""
|
18
|
+
)
|
19
|
+
|
20
|
+
self.collection = self.db_client.get_collection(self.collection_name)
|
21
|
+
|
22
|
+
async def query_data(self, start_date=None, end_date=None, get_latest = False):
|
23
|
+
if (self.collection is None):
|
24
|
+
return []
|
25
|
+
|
26
|
+
if (start_date is None):
|
27
|
+
start_date = "1991-01-01"
|
28
|
+
if (end_date is None):
|
29
|
+
end_date = self._get_today()
|
30
|
+
|
31
|
+
query, projection, sort = self._prepare_query(
|
32
|
+
start_date=start_date, end_date=end_date, get_latest=get_latest
|
33
|
+
)
|
34
|
+
|
35
|
+
if (get_latest):
|
36
|
+
cursor = self.collection.find(query, projection).sort(sort).limit(1)
|
37
|
+
else:
|
38
|
+
cursor = self.collection.find(query, projection).sort(sort)
|
39
|
+
|
40
|
+
fetched_data = [data async for data in cursor]
|
41
|
+
|
42
|
+
return fetched_data
|
43
|
+
|
44
|
+
def _get_collection_name(self):
|
45
|
+
self.collection_name_map = {"tw": "twse_daily_share_price", "us": "us_technical"}
|
46
|
+
collection_name = self.collection_name_map.get(self.zone, None)
|
47
|
+
|
48
|
+
return collection_name
|
49
|
+
|
50
|
+
def _prepare_query(self, start_date=None, end_date=None, get_latest = False):
|
51
|
+
query = {"ticker": self.ticker}
|
52
|
+
|
53
|
+
query = self._update_query_with_date(query, start_date, end_date)
|
54
|
+
|
55
|
+
projection = {
|
56
|
+
"_id": 0,
|
57
|
+
}
|
58
|
+
|
59
|
+
if (get_latest):
|
60
|
+
sort = [("date", DESCENDING)]
|
61
|
+
else:
|
62
|
+
sort = [("date", ASCENDING)]
|
63
|
+
|
64
|
+
return query, projection, sort
|
65
|
+
|
66
|
+
def _update_query_with_date(self, query, start_date, end_date):
|
67
|
+
start_date = self._transform_date(start_date)
|
68
|
+
end_date = self._transform_date(end_date)
|
69
|
+
|
70
|
+
date_range = {"$gte": start_date, "$lte": end_date}
|
71
|
+
|
72
|
+
query.update({"date": date_range})
|
73
|
+
|
74
|
+
return query
|
75
|
+
|
76
|
+
def _transform_date(self, date):
|
77
|
+
if (isinstance(date, str)):
|
78
|
+
try:
|
79
|
+
return datetime.strptime(date, "%Y-%m-%d")
|
80
|
+
except Exception as e:
|
81
|
+
raise ValueError(
|
82
|
+
"date string format imcompatible, should be \"YYYY-MM-DD\""
|
83
|
+
)
|
84
|
+
elif (isinstance(date, datetime)):
|
85
|
+
return date
|
86
|
+
else:
|
87
|
+
raise ValueError(
|
88
|
+
"date type imcompatible, should be string(\"YYYY-MM-DD\") or datetime.datetime"
|
89
|
+
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from .base import BaseDailyTechDBExtractor
|
2
|
+
from datetime import datetime
|
3
|
+
from pymongo import ASCENDING, DESCENDING
|
4
|
+
|
5
|
+
|
6
|
+
class AsyncTEJDailyChipDBExtractor(BaseDailyTechDBExtractor):
|
7
|
+
|
8
|
+
def __init__(self, ticker, client):
|
9
|
+
super().__init__(ticker, client)
|
10
|
+
|
11
|
+
def _get_collection_name(self):
|
12
|
+
self.collection_name_map = {"tw": "TEJ_chip"}
|
13
|
+
|
14
|
+
return self.collection_name_map.get(self.zone, None)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from .base import BaseDailyTechDBExtractor
|
2
|
+
|
3
|
+
|
4
|
+
class AsyncTEJDailyTechDBExtractor(BaseDailyTechDBExtractor):
|
5
|
+
|
6
|
+
def __init__(self, ticker, client):
|
7
|
+
super().__init__(ticker, client)
|
8
|
+
|
9
|
+
def _get_collection_name(self):
|
10
|
+
self.collection_name_map = {"tw": "TEJ_share_price"}
|
11
|
+
|
12
|
+
return self.collection_name_map.get(self.zone, None)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
from .base import BaseDailyTechDBExtractor
|
2
|
+
from datetime import datetime
|
3
|
+
from pymongo import ASCENDING, DESCENDING
|
4
|
+
from neurostats_API.utils import NotSupportedError
|
5
|
+
|
6
|
+
class AsyncTWSEChipDBExtractor(BaseDailyTechDBExtractor):
|
7
|
+
|
8
|
+
def __init__(self, ticker, client, fetch_type = 'I'):
|
9
|
+
|
10
|
+
"""
|
11
|
+
擷取台股每日籌碼資料。
|
12
|
+
|
13
|
+
fetch_type:
|
14
|
+
- 'I' (INSTITUTION): 法人買賣
|
15
|
+
- 'M' (MARGIN): 融資融券餘額變化
|
16
|
+
- 'S' (SECURITY_LENDING): 借券債券
|
17
|
+
|
18
|
+
"""
|
19
|
+
super().__init__(ticker, client)
|
20
|
+
|
21
|
+
column_name_map = {
|
22
|
+
'tw': {
|
23
|
+
"I" : "institution_trading",
|
24
|
+
"M" : "margin_trading",
|
25
|
+
"S" : "security_lending"
|
26
|
+
},
|
27
|
+
}
|
28
|
+
self.target_column = column_name_map[self.zone][fetch_type]
|
29
|
+
|
30
|
+
def _get_collection_name(self):
|
31
|
+
self.collection_name_map = {"tw": "twse_chip"}
|
32
|
+
collection_name = self.collection_name_map.get(self.zone, None)
|
33
|
+
return collection_name
|
34
|
+
|
35
|
+
def _prepare_query(self, start_date=None, end_date=None, get_latest = False):
|
36
|
+
query, projection, sort = super()._prepare_query(start_date, end_date, get_latest)
|
37
|
+
query.update(
|
38
|
+
{
|
39
|
+
self.target_column: {"$exists" : True}
|
40
|
+
}
|
41
|
+
)
|
42
|
+
projection.update(
|
43
|
+
{
|
44
|
+
'date': 1,
|
45
|
+
self.target_column: 1
|
46
|
+
}
|
47
|
+
)
|
48
|
+
|
49
|
+
return query, projection, sort
|
@@ -0,0 +1,93 @@
|
|
1
|
+
from .base import BaseDailyTechDBExtractor
|
2
|
+
from pymongo import ASCENDING, DESCENDING
|
3
|
+
from neurostats_API.utils import NotSupportedError
|
4
|
+
|
5
|
+
class AsyncDailyValueDBExtractor(BaseDailyTechDBExtractor):
|
6
|
+
|
7
|
+
def __init__(self, ticker, client, fetch_type='D'):
|
8
|
+
"""
|
9
|
+
fetch_type:
|
10
|
+
'D': daily
|
11
|
+
'Y': yearly
|
12
|
+
"""
|
13
|
+
self.fetch_type = fetch_type
|
14
|
+
super().__init__(ticker, client)
|
15
|
+
|
16
|
+
def _get_collection_name(self):
|
17
|
+
self.collection_name_map = {
|
18
|
+
"tw": {
|
19
|
+
"D":"twse_daily_share_price",
|
20
|
+
"Y": "twse_yearly_value"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
collection_map = self.collection_name_map.get(self.zone)
|
24
|
+
if (collection_map is None):
|
25
|
+
raise NotSupportedError(f"AsyncDailyValueDBExtractor only supports {self.collection_name_map.keys}, but get {self.zone}")
|
26
|
+
collection_name = collection_map.get(self.fetch_type)
|
27
|
+
if (collection_name is None):
|
28
|
+
raise ValueError(f"AsyncDailyValueDBExtractor: Invalid fetch type : {self.fetch_type}")
|
29
|
+
return collection_name
|
30
|
+
|
31
|
+
def _prepare_query(self, start_date = None, end_date = None, get_latest = False):
|
32
|
+
query_fn_map = {
|
33
|
+
'D': super()._prepare_query,
|
34
|
+
'Y': self._prepare_annual_query
|
35
|
+
}
|
36
|
+
|
37
|
+
query_fn = query_fn_map.get(self.fetch_type)
|
38
|
+
query, projection, sorting = query_fn(start_date=start_date, end_date=end_date, get_latest=get_latest)
|
39
|
+
|
40
|
+
projection.update(
|
41
|
+
{
|
42
|
+
"date": 1,
|
43
|
+
"year": 1,
|
44
|
+
"close": 1,
|
45
|
+
"P_E": 1,
|
46
|
+
"P_B": 1,
|
47
|
+
"P_FCF": 1,
|
48
|
+
"P_S": 1,
|
49
|
+
"EV_S": 1,
|
50
|
+
"EV_OPI": 1,
|
51
|
+
"EV_EBIT": 1,
|
52
|
+
"EV_EBITDA": 1
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
return query, projection, sorting
|
57
|
+
|
58
|
+
|
59
|
+
def _prepare_annual_query(self, start_date, end_date, get_latest = False):
|
60
|
+
start_date = self._transform_date(start_date)
|
61
|
+
end_date = self._transform_date(end_date)
|
62
|
+
|
63
|
+
start_year = start_date.year - 1911
|
64
|
+
end_year = end_date.year - 1911
|
65
|
+
query = {
|
66
|
+
"ticker": self.ticker,
|
67
|
+
"year": {
|
68
|
+
"$gte": start_year,
|
69
|
+
"$lte": end_year
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
projection = {
|
74
|
+
"_id": 0,
|
75
|
+
}
|
76
|
+
|
77
|
+
if (get_latest):
|
78
|
+
sort = [("year", DESCENDING)]
|
79
|
+
else:
|
80
|
+
sort = [("year", ASCENDING)]
|
81
|
+
|
82
|
+
return query, projection, sort
|
83
|
+
|
84
|
+
|
85
|
+
class AsyncTEJDailyValueDBExtractor(BaseDailyTechDBExtractor):
|
86
|
+
|
87
|
+
def __init__(self, ticker, mongo_uri):
|
88
|
+
super().__init__(ticker, mongo_uri)
|
89
|
+
|
90
|
+
def _get_collection_name(self):
|
91
|
+
self.collection_name_map = {"tw": "TEJ_share_price"}
|
92
|
+
|
93
|
+
return self.collection_name_map.get(self.zone, None)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from .base import BaseDailyTechDBExtractor
|
2
|
+
|
3
|
+
|
4
|
+
class AsyncYFDailyTechDBExtractor(BaseDailyTechDBExtractor):
|
5
|
+
|
6
|
+
def __init__(self, ticker, client):
|
7
|
+
super().__init__(ticker, client)
|
8
|
+
|
9
|
+
def _get_collection_name(self):
|
10
|
+
self.collection_name_map = {"tw": "twse_daily_share_price", "us": "us_technical"}
|
11
|
+
|
12
|
+
return self.collection_name_map.get(self.zone, None)
|
@@ -0,0 +1 @@
|
|
1
|
+
from .twse import AsyncTWSEMonthlyRevenueExtractor
|
@@ -0,0 +1,140 @@
|
|
1
|
+
from ..base import BaseDBExtractor
|
2
|
+
from datetime import datetime
|
3
|
+
import json
|
4
|
+
import pandas as pd
|
5
|
+
from pymongo import ASCENDING, DESCENDING
|
6
|
+
from neurostats_API.async_mode.db import TWSEDBClient, USDBClient
|
7
|
+
from neurostats_API.utils import StatsDateTime, StatsProcessor, NotSupportedError
|
8
|
+
import yaml
|
9
|
+
|
10
|
+
|
11
|
+
class AsyncBaseMonthlyDBExtractor(BaseDBExtractor):
|
12
|
+
|
13
|
+
def __init__(self, ticker, client):
|
14
|
+
super().__init__(ticker, client)
|
15
|
+
|
16
|
+
self.collection_name_map = None
|
17
|
+
self.collection_name = self._get_collection_name()
|
18
|
+
|
19
|
+
if (self.collection_name is None):
|
20
|
+
raise NotSupportedError(
|
21
|
+
f"{self.__class__.__name__} only supports {list(self.collection_name_map.keys())}, got {self.zone} with ticker = \"{self.ticker}\""
|
22
|
+
)
|
23
|
+
|
24
|
+
self.collection = self.db_client.get_collection(self.collection_name)
|
25
|
+
|
26
|
+
async def query_data(
|
27
|
+
self, start_date=None, end_date=None, get_latest=False
|
28
|
+
):
|
29
|
+
if (start_date is None):
|
30
|
+
start_date = "1991-01-01"
|
31
|
+
if (end_date is None):
|
32
|
+
end_date = self._get_today()
|
33
|
+
|
34
|
+
start_year, start_month = self._get_year_and_month(date=start_date)
|
35
|
+
end_year, end_month = self._get_year_and_month(date=end_date)
|
36
|
+
|
37
|
+
query, projection, sort = self._prepare_query(
|
38
|
+
start_year, start_month, end_year, end_month, get_latest
|
39
|
+
)
|
40
|
+
cursor = self.collection.find(query, projection).sort(sort)
|
41
|
+
if (get_latest):
|
42
|
+
cursor = cursor.limit(1)
|
43
|
+
|
44
|
+
fetched_data = [data async for data in cursor]
|
45
|
+
|
46
|
+
return fetched_data
|
47
|
+
|
48
|
+
def _get_collection_name(self):
|
49
|
+
self.collection_name_map = {
|
50
|
+
"tw": "twse_monthly_revenue",
|
51
|
+
}
|
52
|
+
|
53
|
+
collection_name = self.collection_name_map.get(self.zone, None)
|
54
|
+
|
55
|
+
return collection_name
|
56
|
+
|
57
|
+
def _prepare_query(
|
58
|
+
self,
|
59
|
+
start_year=None,
|
60
|
+
start_month=None,
|
61
|
+
end_year=None,
|
62
|
+
end_month=None,
|
63
|
+
get_latest=False
|
64
|
+
):
|
65
|
+
|
66
|
+
query = {"ticker": self.ticker}
|
67
|
+
|
68
|
+
query = self._update_query_with_year_month(
|
69
|
+
query, start_year, start_month, end_year, end_month
|
70
|
+
)
|
71
|
+
|
72
|
+
projection = {
|
73
|
+
"_id": 0,
|
74
|
+
}
|
75
|
+
|
76
|
+
if (get_latest):
|
77
|
+
sort = [("year", DESCENDING), ("month", DESCENDING)]
|
78
|
+
else:
|
79
|
+
sort = [("year", ASCENDING), ("month", ASCENDING)]
|
80
|
+
|
81
|
+
return query, projection, sort
|
82
|
+
|
83
|
+
def _get_year_and_month(self, date):
|
84
|
+
if (isinstance(date, str)):
|
85
|
+
date = datetime.strptime(date, "%Y-%m-%d")
|
86
|
+
|
87
|
+
year = date.year
|
88
|
+
month = date.month
|
89
|
+
|
90
|
+
return year, month
|
91
|
+
|
92
|
+
def _update_query_with_year_month(
|
93
|
+
self, query, start_year, start_month, end_year, end_month
|
94
|
+
):
|
95
|
+
if all(v is not None for v in [start_year, start_month]):
|
96
|
+
# 大於start_year條件
|
97
|
+
query.update(
|
98
|
+
{
|
99
|
+
"$or": [
|
100
|
+
{
|
101
|
+
"year": {
|
102
|
+
"$gt": start_year
|
103
|
+
}
|
104
|
+
},
|
105
|
+
{
|
106
|
+
"year": start_year,
|
107
|
+
"month": {
|
108
|
+
"$gte": start_month
|
109
|
+
}
|
110
|
+
},
|
111
|
+
]
|
112
|
+
}
|
113
|
+
)
|
114
|
+
|
115
|
+
if all(v is not None for v in [end_year, end_month]):
|
116
|
+
# 小於end_year條件
|
117
|
+
query.update(
|
118
|
+
{
|
119
|
+
"$and":
|
120
|
+
query.get("$and", []) + [
|
121
|
+
{
|
122
|
+
"$or": [
|
123
|
+
{
|
124
|
+
"year": {
|
125
|
+
"$lt": end_year
|
126
|
+
}
|
127
|
+
},
|
128
|
+
{
|
129
|
+
"year": end_year,
|
130
|
+
"month": {
|
131
|
+
"$lte": end_month
|
132
|
+
}
|
133
|
+
},
|
134
|
+
]
|
135
|
+
}
|
136
|
+
]
|
137
|
+
}
|
138
|
+
)
|
139
|
+
|
140
|
+
return query
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from .base import AsyncBaseSeasonalDBExtractor
|
2
|
+
from datetime import datetime
|
3
|
+
import json
|
4
|
+
import pandas as pd
|
5
|
+
from pymongo import ASCENDING, DESCENDING
|
6
|
+
from neurostats_API.async_mode.db import TWSEDBClient, USDBClient
|
7
|
+
from neurostats_API.utils import StatsDateTime, StatsProcessor
|
8
|
+
import yaml
|
9
|
+
|
10
|
+
class AsyncBalanceSheetExtractor(AsyncBaseSeasonalDBExtractor):
|
11
|
+
|
12
|
+
def __init__(self, ticker, client):
|
13
|
+
|
14
|
+
super().__init__(ticker, client)
|
15
|
+
|
16
|
+
self.column_name_map = {
|
17
|
+
'tw': "balance_sheet",
|
18
|
+
'us': "balance_sheet"
|
19
|
+
}
|