neurostats-API 0.0.18__tar.gz → 0.0.20__tar.gz

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.
Files changed (39) hide show
  1. {neurostats_api-0.0.18/neurostats_API.egg-info → neurostats_api-0.0.20}/PKG-INFO +39 -12
  2. neurostats_api-0.0.18/PKG-INFO → neurostats_api-0.0.20/README.md +37 -26
  3. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/__init__.py +3 -2
  4. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/__init__.py +1 -1
  5. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/base.py +41 -13
  6. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/tech.py +107 -80
  7. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/tej_finance_report.py +180 -78
  8. neurostats_api-0.0.18/README.md → neurostats_api-0.0.20/neurostats_API.egg-info/PKG-INFO +54 -11
  9. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API.egg-info/requires.txt +1 -1
  10. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/setup.py +2 -2
  11. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/test/test_fetchers.py +9 -0
  12. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/test/test_tej.py +12 -8
  13. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/MANIFEST.in +0 -0
  14. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/cli.py +0 -0
  15. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/balance_sheet.py +0 -0
  16. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/cash_flow.py +0 -0
  17. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/finance_overview.py +0 -0
  18. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/institution.py +0 -0
  19. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/margin_trading.py +0 -0
  20. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/month_revenue.py +0 -0
  21. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/profit_lose.py +0 -0
  22. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/fetchers/value_invest.py +0 -0
  23. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/main.py +0 -0
  24. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/tools/balance_sheet.yaml +0 -0
  25. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/tools/cash_flow_percentage.yaml +0 -0
  26. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/tools/finance_overview_dict.yaml +0 -0
  27. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/tools/profit_lose.yaml +0 -0
  28. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/tools/seasonal_data_field_dict.txt +0 -0
  29. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/tools/tej_db_index.yaml +0 -0
  30. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/utils/__init__.py +0 -0
  31. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/utils/calculate_value.py +0 -0
  32. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/utils/data_process.py +0 -0
  33. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/utils/datetime.py +0 -0
  34. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/utils/db_client.py +0 -0
  35. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API/utils/fetcher.py +0 -0
  36. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API.egg-info/SOURCES.txt +0 -0
  37. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API.egg-info/dependency_links.txt +0 -0
  38. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/neurostats_API.egg-info/top_level.txt +0 -0
  39. {neurostats_api-0.0.18 → neurostats_api-0.0.20}/setup.cfg +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: neurostats_API
3
- Version: 0.0.18
3
+ Version: 0.0.20
4
4
  Summary: The service of NeuroStats website
5
5
  Home-page: https://github.com/NeurowattStats/NeuroStats_API.git
6
6
  Author: JasonWang@Neurowatt
7
7
  Author-email: jason@neurowatt.ai
8
8
  Requires-Python: >=3.6
9
9
  Description-Content-Type: text/markdown
10
- Requires-Dist: numpy>=2.1.0
10
+ Requires-Dist: numpy
11
11
  Requires-Dist: pandas>=2.2.0
12
12
  Requires-Dist: pymongo
13
13
  Requires-Dist: pytz
@@ -728,6 +728,7 @@ data = fetcher.get(
728
728
  #### 回傳資料
729
729
  ##### `YOY_NOCAL` 與 `QOQ_NOCAL`
730
730
  為回傳`pd.DataFrame`,column名稱為<年份>Q<季>, row名稱為指定財報項目
731
+
731
732
  ```Python
732
733
  # fetch_mode = fetcher.FetchMode.QOQ_NOCAL
733
734
  2024Q3 2024Q2 2024Q1
@@ -742,17 +743,18 @@ bp51 3.111298e+09 3.173919e+09 2.453840e+09
742
743
 
743
744
  ##### `YOY` 與 `QOQ`
744
745
  回傳為`Dict[pd.DataFrame]`, key 為指定的index, DataFrame中則是該index歷年的數值與成長率
746
+ 成長率單位為`%`
745
747
  ```Python
746
748
  # fetch_mode = fetcher.FetchMode.QOQ
747
749
  {
748
750
  'bp41':
749
751
  2024Q3 2024Q2 2024Q1
750
752
  value 7.082005e+07 6.394707e+07 5.761001e+07
751
- growth 1.074791e-01 1.099994e-01 5.532101e-03,
753
+ growth 1.074791e+01 1.099994e+01 5.532101e-01,
752
754
  'bp51':
753
755
  2024Q3 2024Q2 2024Q1
754
756
  value 3.111298e+09 3.145373e+09 3.091985e+09
755
- growth -1.083335e-02 1.726663e-02 -4.159542e-03
757
+ growth -1.083335e+00 1.726663e+00 -4.159542e-01
756
758
  }
757
759
 
758
760
  # fetch_mode = fetcher.FetchMode.YOY
@@ -760,17 +762,17 @@ growth -1.083335e-02 1.726663e-02 -4.159542e-03
760
762
  'bp41':
761
763
  2024Q3 2023Q3 2022Q3
762
764
  value 7.082005e+07 5.377231e+07 6.201822e+07
763
- YoY_1 NaN NaN 4.130744e-01
764
- YoY_3 1.729171e-01 9.556684e-02 1.883274e-01
765
- YoY_5 1.389090e-01 1.215242e-01 1.642914e-01
766
- YoY_10 1.255138e-01 1.356297e-01 1.559702e-01,
765
+ YoY_1 3.170357e+01 -1.329596e+01 4.130744e+01
766
+ YoY_3 1.729171e+01 9.556684e+00 1.883274e+01
767
+ YoY_5 1.389090e+01 1.215242e+01 1.642914e+01
768
+ YoY_10 1.255138e+01 1.356297e+01 1.559702e+01
767
769
  'bp51':
768
770
  2024Q3 2023Q3 2022Q3
769
771
  value 3.111298e+09 3.173919e+09 2.453840e+09
770
- YoY_1 NaN NaN 3.179539e-01
771
- YoY_3 1.866752e-01 2.766851e-01 2.638677e-01
772
- YoY_5 2.068132e-01 2.479698e-01 1.815106e-01
773
- YoY_10 1.420500e-01 1.586797e-01 1.551364e-01
772
+ YoY_1 -1.972987e+00 2.934499e+01 3.179539e+01
773
+ YoY_3 1.866752e+01 2.766851e+01 2.638677e+01
774
+ YoY_5 2.068132e+01 2.479698e+01 1.815106e+01
775
+ YoY_10 1.420500e+01 1.586797e+01 1.551364e+01
774
776
  }
775
777
  ```
776
778
 
@@ -819,3 +821,28 @@ data = fetcher.get(
819
821
 
820
822
  [TEJ資料集連結](https://tquant.tejwin.com/%E8%B3%87%E6%96%99%E9%9B%86/)
821
823
  請看 `公司自結數`
824
+
825
+ ### 開高低收
826
+ ```Python
827
+ mongo_uri = <MongoDB 的 URI>
828
+ db_name = 'company' # 連接的DB名稱
829
+ collection_name = "TWN/APIPRCD" # 連接的collection對象
830
+ from neurostats_API import TEJStockPriceFetcher
831
+
832
+ fetcher = TEJStockPriceFetcher(
833
+ mongo_uri = mongo_uri,
834
+ db_name = db_name,
835
+ collection_name = collection_name
836
+ )
837
+
838
+ data = fetcher.get(
839
+ ticker = "2330" # 任意的股票代碼
840
+ start_date = "2005-01-01",
841
+ period = "3m"
842
+ ) # -> pd.DataFrame
843
+ ```
844
+ - `ticker`: 股票代碼
845
+ - `start_date`: 搜尋範圍的開始日期
846
+ - `period`: 搜尋的時間範圍長度
847
+
848
+ `period`與`start_date`同時存在時以period優先
@@ -1,19 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: neurostats_API
3
- Version: 0.0.18
4
- Summary: The service of NeuroStats website
5
- Home-page: https://github.com/NeurowattStats/NeuroStats_API.git
6
- Author: JasonWang@Neurowatt
7
- Author-email: jason@neurowatt.ai
8
- Requires-Python: >=3.6
9
- Description-Content-Type: text/markdown
10
- Requires-Dist: numpy>=2.1.0
11
- Requires-Dist: pandas>=2.2.0
12
- Requires-Dist: pymongo
13
- Requires-Dist: pytz
14
- Requires-Dist: python-dotenv
15
- Requires-Dist: yfinance
16
-
17
1
  # neurostats_API
18
2
 
19
3
  - [檔案架構](#檔案架構)
@@ -728,6 +712,7 @@ data = fetcher.get(
728
712
  #### 回傳資料
729
713
  ##### `YOY_NOCAL` 與 `QOQ_NOCAL`
730
714
  為回傳`pd.DataFrame`,column名稱為<年份>Q<季>, row名稱為指定財報項目
715
+
731
716
  ```Python
732
717
  # fetch_mode = fetcher.FetchMode.QOQ_NOCAL
733
718
  2024Q3 2024Q2 2024Q1
@@ -742,17 +727,18 @@ bp51 3.111298e+09 3.173919e+09 2.453840e+09
742
727
 
743
728
  ##### `YOY` 與 `QOQ`
744
729
  回傳為`Dict[pd.DataFrame]`, key 為指定的index, DataFrame中則是該index歷年的數值與成長率
730
+ 成長率單位為`%`
745
731
  ```Python
746
732
  # fetch_mode = fetcher.FetchMode.QOQ
747
733
  {
748
734
  'bp41':
749
735
  2024Q3 2024Q2 2024Q1
750
736
  value 7.082005e+07 6.394707e+07 5.761001e+07
751
- growth 1.074791e-01 1.099994e-01 5.532101e-03,
737
+ growth 1.074791e+01 1.099994e+01 5.532101e-01,
752
738
  'bp51':
753
739
  2024Q3 2024Q2 2024Q1
754
740
  value 3.111298e+09 3.145373e+09 3.091985e+09
755
- growth -1.083335e-02 1.726663e-02 -4.159542e-03
741
+ growth -1.083335e+00 1.726663e+00 -4.159542e-01
756
742
  }
757
743
 
758
744
  # fetch_mode = fetcher.FetchMode.YOY
@@ -760,17 +746,17 @@ growth -1.083335e-02 1.726663e-02 -4.159542e-03
760
746
  'bp41':
761
747
  2024Q3 2023Q3 2022Q3
762
748
  value 7.082005e+07 5.377231e+07 6.201822e+07
763
- YoY_1 NaN NaN 4.130744e-01
764
- YoY_3 1.729171e-01 9.556684e-02 1.883274e-01
765
- YoY_5 1.389090e-01 1.215242e-01 1.642914e-01
766
- YoY_10 1.255138e-01 1.356297e-01 1.559702e-01,
749
+ YoY_1 3.170357e+01 -1.329596e+01 4.130744e+01
750
+ YoY_3 1.729171e+01 9.556684e+00 1.883274e+01
751
+ YoY_5 1.389090e+01 1.215242e+01 1.642914e+01
752
+ YoY_10 1.255138e+01 1.356297e+01 1.559702e+01
767
753
  'bp51':
768
754
  2024Q3 2023Q3 2022Q3
769
755
  value 3.111298e+09 3.173919e+09 2.453840e+09
770
- YoY_1 NaN NaN 3.179539e-01
771
- YoY_3 1.866752e-01 2.766851e-01 2.638677e-01
772
- YoY_5 2.068132e-01 2.479698e-01 1.815106e-01
773
- YoY_10 1.420500e-01 1.586797e-01 1.551364e-01
756
+ YoY_1 -1.972987e+00 2.934499e+01 3.179539e+01
757
+ YoY_3 1.866752e+01 2.766851e+01 2.638677e+01
758
+ YoY_5 2.068132e+01 2.479698e+01 1.815106e+01
759
+ YoY_10 1.420500e+01 1.586797e+01 1.551364e+01
774
760
  }
775
761
  ```
776
762
 
@@ -819,3 +805,28 @@ data = fetcher.get(
819
805
 
820
806
  [TEJ資料集連結](https://tquant.tejwin.com/%E8%B3%87%E6%96%99%E9%9B%86/)
821
807
  請看 `公司自結數`
808
+
809
+ ### 開高低收
810
+ ```Python
811
+ mongo_uri = <MongoDB 的 URI>
812
+ db_name = 'company' # 連接的DB名稱
813
+ collection_name = "TWN/APIPRCD" # 連接的collection對象
814
+ from neurostats_API import TEJStockPriceFetcher
815
+
816
+ fetcher = TEJStockPriceFetcher(
817
+ mongo_uri = mongo_uri,
818
+ db_name = db_name,
819
+ collection_name = collection_name
820
+ )
821
+
822
+ data = fetcher.get(
823
+ ticker = "2330" # 任意的股票代碼
824
+ start_date = "2005-01-01",
825
+ period = "3m"
826
+ ) # -> pd.DataFrame
827
+ ```
828
+ - `ticker`: 股票代碼
829
+ - `start_date`: 搜尋範圍的開始日期
830
+ - `period`: 搜尋的時間範圍長度
831
+
832
+ `period`與`start_date`同時存在時以period優先
@@ -1,4 +1,4 @@
1
- __version__='0.0.18'
1
+ __version__='0.0.20'
2
2
 
3
3
  from .fetchers import (
4
4
  BalanceSheetFetcher,
@@ -9,5 +9,6 @@ from .fetchers import (
9
9
  MarginTradingFetcher,
10
10
  MonthRevenueFetcher,
11
11
  TechFetcher,
12
- ProfitLoseFetcher
12
+ TEJStockPriceFetcher,
13
+ ProfitLoseFetcher,
13
14
  )
@@ -2,7 +2,7 @@ from .base import StatsDateTime, StatsFetcher
2
2
  from .balance_sheet import BalanceSheetFetcher
3
3
  from .cash_flow import CashFlowFetcher
4
4
  from .finance_overview import FinanceOverviewFetcher
5
- from .tej_finance_report import FinanceReportFetcher
5
+ from .tej_finance_report import FinanceReportFetcher, TEJStockPriceFetcher
6
6
  from .tech import TechFetcher
7
7
  from .institution import InstitutionFetcher
8
8
  from .margin_trading import MarginTradingFetcher
@@ -1,4 +1,5 @@
1
1
  import abc
2
+ from typing import Union
2
3
  from pymongo import MongoClient
3
4
  import pandas as pd
4
5
  import json
@@ -53,8 +54,8 @@ class StatsFetcher:
53
54
  season = (month - 1) // 3 + 1
54
55
 
55
56
  return StatsDateTime(date, year, month, day, season)
56
-
57
- def has_required_columns(self, df:pd.DataFrame, required_cols=None):
57
+
58
+ def has_required_columns(self, df: pd.DataFrame, required_cols=None):
58
59
  """
59
60
  Check if the required columns are present in the DataFrame.
60
61
 
@@ -68,23 +69,26 @@ class StatsFetcher:
68
69
  """
69
70
  if required_cols is None:
70
71
  required_cols = ['date', 'open', 'high', 'low', 'close', 'volume']
71
-
72
+
72
73
  return all(col in df.columns for col in required_cols)
73
74
 
74
75
 
75
76
  class BaseTEJFetcher(abc.ABC):
76
77
 
77
- def __init__(self):
78
- self.client = None
79
- self.db = None
80
- self.collection = None
81
-
82
78
  @abc.abstractmethod
83
79
  def get(self):
84
80
  pass
85
81
 
86
82
  def get_latest_data_time(self, ticker):
87
- latest_data = self.collection.find_one({"ticker": ticker}, {"last_update": 1, "_id": 0})
83
+ latest_data = self.collection.find_one(
84
+ {
85
+ "ticker": ticker
86
+ },
87
+ {
88
+ "last_update": 1,
89
+ "_id": 0
90
+ }
91
+ )
88
92
 
89
93
  try:
90
94
  latest_date = latest_data['last_update']["latest_data_date"]
@@ -93,7 +97,8 @@ class BaseTEJFetcher(abc.ABC):
93
97
 
94
98
  return latest_date
95
99
 
96
- def cal_YoY(self, data_dict: dict, start_year: int, end_year: int, season: int):
100
+ def cal_YoY(
101
+ self, data_dict: dict, start_year: int, end_year: int, season: int):
97
102
  year_shifts = [1, 3, 5, 10]
98
103
  return_dict = {}
99
104
  for year in range(start_year, end_year + 1):
@@ -115,7 +120,8 @@ class BaseTEJFetcher(abc.ABC):
115
120
  try:
116
121
  past_year = year - shift
117
122
  last_value = data_dict[f"{past_year}Q{season}"][key]
118
- temp_dict[f"YoY_{shift}"] = YoY_Calculator.cal_growth(this_value, last_value, delta=shift)
123
+ temp_dict[f"YoY_{shift}"] = YoY_Calculator.cal_growth(
124
+ this_value, last_value, delta=shift) * 100
119
125
  except Exception as e:
120
126
  temp_dict[f"YoY_{shift}"] = None
121
127
 
@@ -153,9 +159,11 @@ class BaseTEJFetcher(abc.ABC):
153
159
  temp_dict = {"value": this_value}
154
160
 
155
161
  try:
156
- last_value = data_dict[f"{last_year}Q{last_season}"][key]['value']
162
+ last_value = data_dict[f"{last_year}Q{last_season}"][
163
+ key]['value']
157
164
 
158
- temp_dict['growth'] = YoY_Calculator.cal_growth(this_value, last_value, delta=1)
165
+ temp_dict['growth'] = YoY_Calculator.cal_growth(
166
+ this_value, last_value, delta=1) * 100
159
167
  except Exception as e:
160
168
  temp_dict['growth'] = None
161
169
 
@@ -173,3 +181,23 @@ class BaseTEJFetcher(abc.ABC):
173
181
  for key in data_dict.keys():
174
182
  data_dict[key] = pd.DataFrame.from_dict(data_dict[key])
175
183
  return data_dict
184
+
185
+ def set_time_shift(self, date: Union[str, datetime], period: str):
186
+ if isinstance(date, str):
187
+ date = datetime.strptime(date, "%Y-%m-%d")
188
+
189
+ period_mapping = {
190
+ "1d": timedelta(days=1),
191
+ "7d": timedelta(days=7),
192
+ "1m": timedelta(days=30),
193
+ "3m": timedelta(days=90),
194
+ "1y": timedelta(days=365),
195
+ "3y": timedelta(days=365 * 3),
196
+ "5y": timedelta(days=365 * 5),
197
+ "10y": timedelta(days=365 * 10),
198
+ }
199
+
200
+ if period == "all":
201
+ return datetime.strptime("1991-01-01", "%Y-%m-%d")
202
+
203
+ return date - period_mapping.get(period, timedelta(days=0)) # 預設為不變
@@ -2,48 +2,49 @@ from .base import StatsFetcher
2
2
  import pandas as pd
3
3
  import yfinance as yf
4
4
 
5
+
5
6
  class TechFetcher(StatsFetcher):
6
-
7
- def __init__(self, ticker:str, db_client):
8
7
 
8
+ def __init__(self, ticker: str, db_client):
9
9
  """
10
10
  The Capitalization-Weighted Index includes the following tickers:
11
11
  ['GSPC', 'IXIC', 'DJI', 'TWII']
12
12
  """
13
-
13
+
14
14
  super().__init__(ticker, db_client)
15
+ self.collection = self.db["TWN/APIPRCD"]
15
16
  self.full_ohlcv = self._get_ohlcv()
16
- self.basic_indexes = ['SMA5', 'SMA20', 'SMA60', 'EMA5', 'EMA20',
17
- 'EMA40', 'EMA12', 'EMA26', 'RSI7', 'RSI14',
18
- 'RSI21', 'MACD', 'Signal Line', 'Middle Band',
19
- 'Upper Band', 'Lower Band', '%b', 'ATR',
20
- 'BBW','EMA Cycle','EMA Cycle Instructions',
21
- 'Day Trading Signal']
22
-
17
+ self.basic_indexes = [
18
+ 'SMA5', 'SMA20', 'SMA60', 'EMA5', 'EMA20', 'EMA40', 'EMA12',
19
+ 'EMA26', 'RSI7', 'RSI14', 'RSI21', 'MACD', 'Signal Line',
20
+ 'Middle Band', 'Upper Band', 'Lower Band', '%b', 'ATR', 'BBW',
21
+ 'EMA Cycle', 'EMA Cycle Instructions', 'Day Trading Signal'
22
+ ]
23
+
23
24
  self.daily_index = TechProcessor.cal_basic_index(self.full_ohlcv)
24
25
 
25
26
  self.weekly_index = TechProcessor.resample(
26
- self.daily_index,
27
- period= 'W',
28
- technical_indicators = self.basic_indexes
27
+ self.daily_index,
28
+ period='W',
29
+ technical_indicators=self.basic_indexes
29
30
  )
30
31
 
31
32
  self.monthly_index = TechProcessor.resample(
32
- self.daily_index,
33
- period= 'ME',
34
- technical_indicators = self.basic_indexes
33
+ self.daily_index,
34
+ period='ME',
35
+ technical_indicators=self.basic_indexes
35
36
  )
36
37
 
37
38
  self.quarterly_index = TechProcessor.resample(
38
- self.daily_index,
39
- period= 'QE',
40
- technical_indicators = self.basic_indexes
39
+ self.daily_index,
40
+ period='QE',
41
+ technical_indicators=self.basic_indexes
41
42
  )
42
43
 
43
44
  self.yearly_index = TechProcessor.resample(
44
- self.daily_index,
45
- period= 'YE',
46
- technical_indicators = self.basic_indexes
45
+ self.daily_index,
46
+ period='YE',
47
+ technical_indicators=self.basic_indexes
47
48
  )
48
49
 
49
50
  def _get_ohlcv(self):
@@ -51,25 +52,7 @@ class TechFetcher(StatsFetcher):
51
52
  required_cols = ['date', 'open', 'high', 'low', 'close', 'volume']
52
53
 
53
54
  try:
54
- query = {'ticker': self.ticker}
55
- ticker_full = self.collection.find_one(query)
56
-
57
- if not ticker_full:
58
- raise ValueError(f"No data found for ticker: {self.ticker}")
59
-
60
- daily_data = ticker_full.get("daily_data", [])
61
- if not isinstance(daily_data, list):
62
- raise TypeError("Expected 'daily_data' to be a list.")
63
-
64
- df = pd.DataFrame(daily_data)
65
-
66
- if not self.has_required_columns(df, required_cols):
67
- raise KeyError(f"Missing required columns")
68
-
69
- except (KeyError, ValueError, TypeError) as e:
70
-
71
- print(f"Conduct yf searching")
72
-
55
+ # 先對yf search
73
56
  if self.ticker in ['GSPC', 'IXIC', 'DJI', 'TWII']:
74
57
  full_tick = f'^{self.ticker}'
75
58
  else:
@@ -80,13 +63,41 @@ class TechFetcher(StatsFetcher):
80
63
  if not self.has_required_columns(df, required_cols):
81
64
 
82
65
  print(f".tw failed, try .two")
83
-
66
+
84
67
  full_tick = f'{self.ticker}.two'
85
68
 
86
69
  df = self.conduct_yf_search(full_tick)
87
-
88
- return df[required_cols]
89
70
 
71
+ if (df.empty):
72
+ raise ValueError(f"No data found for ticker: {self.ticker}")
73
+
74
+ except (KeyError, ValueError, TypeError) as e:
75
+ # 再對TEJ search
76
+ tej_required_cols = [
77
+ "mdate", "open_d", 'high_d', 'low_d', 'close_d', 'vol'
78
+ ]
79
+ tej_name_proj = {
80
+ tej_name: org_name
81
+ for tej_name, org_name in zip(tej_required_cols, required_cols)
82
+ }
83
+
84
+ query = {'ticker': self.ticker}
85
+ ticker_full = self.collection.find_one(query)
86
+
87
+ if not ticker_full:
88
+ raise ValueError("No ticker found in database")
89
+
90
+ daily_data = ticker_full.get("data", [])
91
+ if not isinstance(daily_data, list):
92
+ raise TypeError("Expected 'daily_data' to be a list.")
93
+
94
+ df = pd.DataFrame(daily_data)
95
+
96
+ if not self.has_required_columns(df, tej_required_cols):
97
+ raise KeyError(f"Missing required columns")
98
+ df = df.rename(columns=tej_name_proj)
99
+
100
+ return df[required_cols]
90
101
 
91
102
  def get_daily(self):
92
103
 
@@ -95,7 +106,7 @@ class TechFetcher(StatsFetcher):
95
106
  def get_weekly(self):
96
107
 
97
108
  return self.weekly_index
98
-
109
+
99
110
  def get_monthly(self):
100
111
 
101
112
  return self.monthly_index
@@ -103,19 +114,19 @@ class TechFetcher(StatsFetcher):
103
114
  def get_quarterly(self):
104
115
 
105
116
  return self.quarterly_index
106
-
117
+
107
118
  def get_yearly(self):
108
119
 
109
120
  return self.yearly_index
110
-
111
- def conduct_yf_search(self, ticker:str):
121
+
122
+ def conduct_yf_search(self, ticker: str):
112
123
 
113
124
  yf_ticker = yf.Ticker(ticker)
114
125
  origin_df = yf_ticker.history(period="10y")
115
-
126
+
116
127
  if origin_df.empty:
117
128
  return origin_df
118
-
129
+
119
130
  origin_df = origin_df.reset_index()
120
131
  origin_df["Date"] = pd.to_datetime(origin_df["Date"])
121
132
  df = origin_df.rename(
@@ -131,8 +142,9 @@ class TechFetcher(StatsFetcher):
131
142
 
132
143
  return df
133
144
 
145
+
134
146
  class TechProcessor:
135
-
147
+
136
148
  @staticmethod
137
149
  def cal_sma(closes: pd.Series, n_days: int) -> pd.Series:
138
150
  return closes.rolling(window=n_days).mean()
@@ -158,7 +170,9 @@ class TechProcessor:
158
170
  return macds.ewm(span=n_days, adjust=False).mean()
159
171
 
160
172
  @staticmethod
161
- def cal_bollinger_bands(closes: pd.Series, n_days: int = 20) -> pd.DataFrame:
173
+ def cal_bollinger_bands(
174
+ closes: pd.Series, n_days: int = 20
175
+ ) -> pd.DataFrame:
162
176
  middle = closes.rolling(window=n_days).mean()
163
177
  upper = middle + 2 * closes.rolling(window=n_days).std()
164
178
  lower = middle - 2 * closes.rolling(window=n_days).std()
@@ -174,12 +188,15 @@ class TechProcessor:
174
188
  )
175
189
 
176
190
  @staticmethod
177
- def cal_atr(highes: pd.Series, lows: pd.Series, closes: pd.Series, n_days: int) -> pd.Series:
191
+ def cal_atr(
192
+ highes: pd.Series, lows: pd.Series, closes: pd.Series, n_days: int
193
+ ) -> pd.Series:
178
194
  high_low = highes - lows
179
195
  high_close = (highes - closes.shift(1)).abs()
180
196
  low_close = (lows - closes.shift(1)).abs()
181
197
 
182
- true_range = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
198
+ true_range = pd.concat([high_low, high_close, low_close],
199
+ axis=1).max(axis=1)
183
200
  atr = true_range.rolling(window=n_days, min_periods=1).mean()
184
201
 
185
202
  return atr
@@ -233,15 +250,15 @@ class TechProcessor:
233
250
  return '今日此股票為好的當沖標的'
234
251
  else:
235
252
  return f'今日此股票並非好的當沖標的, 原因: {", ".join(reasons)}'
236
-
253
+
237
254
  @staticmethod
238
- def cal_basic_index(ohlcvs:pd.DataFrame):
239
-
255
+ def cal_basic_index(ohlcvs: pd.DataFrame):
256
+
240
257
  # SMA
241
- ohlcvs['SMA5'] = TechProcessor.cal_sma(ohlcvs['close'], 5)
258
+ ohlcvs['SMA5'] = TechProcessor.cal_sma(ohlcvs['close'], 5)
242
259
  ohlcvs['SMA20'] = TechProcessor.cal_sma(ohlcvs['close'], 20)
243
260
  ohlcvs['SMA60'] = TechProcessor.cal_sma(ohlcvs['close'], 40)
244
-
261
+
245
262
  # EMA
246
263
  ohlcvs['EMA5'] = TechProcessor.cal_ema(ohlcvs['close'], 5)
247
264
  ohlcvs['EMA20'] = TechProcessor.cal_ema(ohlcvs['close'], 20)
@@ -256,34 +273,42 @@ class TechProcessor:
256
273
  ohlcvs['RSI21'] = TechProcessor.cal_rsi(ohlcvs['close'], 21)
257
274
 
258
275
  # MACD
259
- ohlcvs['MACD'] = TechProcessor.cal_macd(ohlcvs['EMA12'], ohlcvs['EMA26'])
276
+ ohlcvs['MACD'] = TechProcessor.cal_macd(
277
+ ohlcvs['EMA12'], ohlcvs['EMA26']
278
+ )
260
279
  ohlcvs['Signal Line'] = TechProcessor.cal_single_line(ohlcvs['MACD'], 9)
261
-
280
+
262
281
  # BANDS
263
282
  bands = TechProcessor.cal_bollinger_bands(ohlcvs['close'], 20)
264
283
  ohlcvs['Middle Band'] = bands['middle']
265
284
  ohlcvs['Upper Band'] = bands['upper']
266
285
  ohlcvs['Lower Band'] = bands['lower']
267
286
  ohlcvs['%b'] = bands['%b']
268
- ohlcvs['BBW'] = (ohlcvs["Upper Band"] - ohlcvs["Lower Band"]) / ohlcvs["Middle Band"]
287
+ ohlcvs['BBW'] = (ohlcvs["Upper Band"] -
288
+ ohlcvs["Lower Band"]) / ohlcvs["Middle Band"]
269
289
 
270
290
  # ATR
271
- ohlcvs['ATR'] = TechProcessor.cal_atr(ohlcvs['high'],ohlcvs['low'],ohlcvs['close'],14)
291
+ ohlcvs['ATR'] = TechProcessor.cal_atr(
292
+ ohlcvs['high'], ohlcvs['low'], ohlcvs['close'], 14
293
+ )
272
294
 
273
295
  # EMA CYCLE
274
296
  ohlcvs['EMA Cycle'] = ohlcvs.apply(
275
- lambda row: TechProcessor.check_tech_trend(row['EMA5'], row['EMA20'], row['EMA40']),
297
+ lambda row: TechProcessor.
298
+ check_tech_trend(row['EMA5'], row['EMA20'], row['EMA40']),
276
299
  axis=1
277
300
  )
278
301
  guidance_map = {
279
302
  '穩定上升期': "三條移動平均線都左下右上, 買方優勢, 三線間隔越來越遠時, 進一步強攻",
280
- '牛市結束期': "ema20 & 40 左下右上, ema5 緩慢下滑, 行情仍強, 賣出條件為 ema5 持續下跌, ema20 停止上漲",
303
+ '牛市結束期':
304
+ "ema20 & 40 左下右上, ema5 緩慢下滑, 行情仍強, 賣出條件為 ema5 持續下跌, ema20 停止上漲",
281
305
  '熊市入口期': "全數出清穩定上升期布局的多頭部位, 考慮提早佈局建立空頭部位",
282
306
  '穩定下跌期': "三條移動平均線都是左上右下, 賣方優勢, 三線間隔越來越遠時, 進一步強攻",
283
- '熊市結束期': "ema20 & 40 左上右下, ema5 緩慢上升, 行情仍走弱, 布局買進的條件是 ema 持續上漲, ema20 停止下降, 幾乎持平",
307
+ '熊市結束期':
308
+ "ema20 & 40 左上右下, ema5 緩慢上升, 行情仍走弱, 布局買進的條件是 ema 持續上漲, ema20 停止下降, 幾乎持平",
284
309
  '牛市入口期': "全數出清穩定下跌期布局的空頭部位, 考慮提早佈局多頭部位",
285
310
  '未定義': "無對應指導"
286
- }
311
+ }
287
312
 
288
313
  ohlcvs['EMA Cycle Instructions'] = ohlcvs['EMA Cycle'].map(guidance_map)
289
314
 
@@ -292,7 +317,7 @@ class TechProcessor:
292
317
  ohlcvs['Day Trading Signal'] = ohlcvs.apply(
293
318
  lambda row: TechProcessor.check_day_trading(
294
319
  close_today=row['close'],
295
- close_yesterday=row['close_yesterday'], # 使用前一天的收盤價
320
+ close_yesterday=row['close_yesterday'], # 使用前一天的收盤價
296
321
  today_atr=row['ATR'],
297
322
  today_rsi7=row['RSI7']
298
323
  ),
@@ -302,7 +327,12 @@ class TechProcessor:
302
327
  return ohlcvs
303
328
 
304
329
  @staticmethod
305
- def resample(df: pd.DataFrame, period='W', technical_indicators=None, date_col='date'):
330
+ def resample(
331
+ df: pd.DataFrame,
332
+ period='W',
333
+ technical_indicators=None,
334
+ date_col='date'
335
+ ):
306
336
  """
307
337
  將 DataFrame 中的技術指標數據重新取樣為指定的時間週期。
308
338
  參數:
@@ -344,11 +374,16 @@ class TechProcessor:
344
374
  agg_dict[indicator] = 'mean'
345
375
 
346
376
  # 過濾出存在於 DataFrame 中的列
347
- existing_cols = {col: agg_dict[col] for col in agg_dict if col in numeric_df.columns}
348
-
377
+ existing_cols = {
378
+ col: agg_dict[col]
379
+ for col in agg_dict if col in numeric_df.columns
380
+ }
381
+
349
382
  # 確保索引為 DatetimeIndex,進行重新取樣
350
383
  if not isinstance(df.index, pd.DatetimeIndex):
351
- raise TypeError("The DataFrame index must be a DatetimeIndex for resampling.")
384
+ raise TypeError(
385
+ "The DataFrame index must be a DatetimeIndex for resampling."
386
+ )
352
387
 
353
388
  resampled_df = numeric_df.resample(period).agg(existing_cols)
354
389
 
@@ -357,11 +392,3 @@ class TechProcessor:
357
392
  resampled_df.reset_index(inplace=True)
358
393
 
359
394
  return resampled_df
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
-
@@ -3,6 +3,7 @@ from datetime import datetime
3
3
  from enum import Enum
4
4
  import pandas as pd
5
5
  from pymongo import MongoClient
6
+ from .tech import TechProcessor
6
7
  from ..utils import StatsProcessor, YoY_Calculator
7
8
  import warnings
8
9
  import yaml
@@ -16,7 +17,12 @@ class FinanceReportFetcher(BaseTEJFetcher):
16
17
  YOY_NOCAL = 3
17
18
  QOQ_NOCAL = 4
18
19
 
19
- def __init__(self, mongo_uri, db_name="company", collection_name="TWN/AINVFQ1"):
20
+ def __init__(
21
+ self,
22
+ mongo_uri,
23
+ db_name="company",
24
+ collection_name="TWN/AINVFQ1"
25
+ ):
20
26
  self.client = MongoClient(mongo_uri)
21
27
  self.db = self.client[db_name]
22
28
  self.collection = self.db[collection_name]
@@ -48,7 +54,9 @@ class FinanceReportFetcher(BaseTEJFetcher):
48
54
  indexes = set(indexes)
49
55
  difference = indexes - self.check_index
50
56
  if (difference):
51
- warnings.warn(f"{list(difference)} 沒有出現在資料表中,請確認column名稱是否正確", UserWarning)
57
+ warnings.warn(
58
+ f"{list(difference)} 沒有出現在資料表中,請確認column名稱是否正確",
59
+ UserWarning)
52
60
 
53
61
  if (not start_date):
54
62
  start_date = datetime.strptime("2005-01-01", "%Y-%m-%d")
@@ -112,7 +120,15 @@ class FinanceReportFetcher(BaseTEJFetcher):
112
120
  return data_df
113
121
 
114
122
  def get_QoQ_data(
115
- self, ticker, start_year, start_season, end_year, end_season, report_type="Q", indexes=[], use_cal=False):
123
+ self,
124
+ ticker,
125
+ start_year,
126
+ start_season,
127
+ end_year,
128
+ end_season,
129
+ report_type="Q",
130
+ indexes=[],
131
+ use_cal=False):
116
132
  """
117
133
  取得時間範圍內每季資料
118
134
  """
@@ -138,31 +154,29 @@ class FinanceReportFetcher(BaseTEJFetcher):
138
154
  }, {
139
155
  "$unwind": "$data"
140
156
  }, {
141
- "$match":
142
- {
143
- "$or":
144
- [
145
- {
146
- "data.year": {
147
- "$gt": start_year,
148
- "$lt": end_year
149
- }
150
- }, {
151
- "data.year": start_year,
152
- "data.season": {
153
- "$gte": start_season
154
- }
155
- }, {
156
- "data.year": end_year,
157
- "data.season": {
158
- "$lte": end_season
159
- }
160
- }, {
161
- "data.year": lower_bound_year,
162
- "data.season": lower_bound_season
163
- }
164
- ]
165
- }
157
+ "$match": {
158
+ "$or": [
159
+ {
160
+ "data.year": {
161
+ "$gt": start_year,
162
+ "$lt": end_year
163
+ }
164
+ }, {
165
+ "data.year": start_year,
166
+ "data.season": {
167
+ "$gte": start_season
168
+ }
169
+ }, {
170
+ "data.year": end_year,
171
+ "data.season": {
172
+ "$lte": end_season
173
+ }
174
+ }, {
175
+ "data.year": lower_bound_year,
176
+ "data.season": lower_bound_season
177
+ }
178
+ ]
179
+ }
166
180
  }, {
167
181
  "$project": {
168
182
  "data.year": 1,
@@ -186,31 +200,29 @@ class FinanceReportFetcher(BaseTEJFetcher):
186
200
  }, {
187
201
  "$unwind": "$data"
188
202
  }, {
189
- "$match":
190
- {
191
- "$or":
192
- [
193
- {
194
- "data.year": {
195
- "$gt": start_year,
196
- "$lt": end_year
197
- }
198
- }, {
199
- "data.year": start_year,
200
- "data.season": {
201
- "$gte": start_season
202
- }
203
- }, {
204
- "data.year": end_year,
205
- "data.season": {
206
- "$lte": end_season
207
- }
208
- }, {
209
- "data.year": lower_bound_year,
210
- "data.season": lower_bound_season
211
- }
212
- ]
213
- }
203
+ "$match": {
204
+ "$or": [
205
+ {
206
+ "data.year": {
207
+ "$gt": start_year,
208
+ "$lt": end_year
209
+ }
210
+ }, {
211
+ "data.year": start_year,
212
+ "data.season": {
213
+ "$gte": start_season
214
+ }
215
+ }, {
216
+ "data.year": end_year,
217
+ "data.season": {
218
+ "$lte": end_season
219
+ }
220
+ }, {
221
+ "data.year": lower_bound_year,
222
+ "data.season": lower_bound_season
223
+ }
224
+ ]
225
+ }
214
226
  }, {
215
227
  "$project": project_stage
216
228
  }
@@ -218,7 +230,10 @@ class FinanceReportFetcher(BaseTEJFetcher):
218
230
 
219
231
  fetched_data = self.collection.aggregate(pipeline).to_list()
220
232
  data_dict = StatsProcessor.list_of_dict_to_dict(
221
- fetched_data, keys=["year", "season"], delimeter="Q", data_key=report_type)
233
+ fetched_data,
234
+ keys=["year", "season"],
235
+ delimeter="Q",
236
+ data_key=report_type)
222
237
 
223
238
  if (use_cal):
224
239
  data_with_QoQ = self.cal_QoQ(data_dict)
@@ -233,7 +248,15 @@ class FinanceReportFetcher(BaseTEJFetcher):
233
248
  data_df = data_df.iloc[:, ::-1]
234
249
  return data_df
235
250
 
236
- def get_YoY_data(self, ticker, start_year, end_year, season, report_type="Q", indexes=[], use_cal=False):
251
+ def get_YoY_data(
252
+ self,
253
+ ticker,
254
+ start_year,
255
+ end_year,
256
+ season,
257
+ report_type="Q",
258
+ indexes=[],
259
+ use_cal=False):
237
260
  """
238
261
  取得某季歷年資料
239
262
  """
@@ -258,20 +281,23 @@ class FinanceReportFetcher(BaseTEJFetcher):
258
281
  }, {
259
282
  "$unwind": "$data"
260
283
  }, {
261
- "$match":
262
- {
263
- "$or": [{
264
- "$and": [{
265
- "data.year": {
266
- "$in": select_year
267
- }
268
- }, {
269
- "data.season": {
270
- "$eq": season
284
+ "$match": {
285
+ "$or": [
286
+ {
287
+ "$and": [
288
+ {
289
+ "data.year": {
290
+ "$in": select_year
291
+ }
292
+ }, {
293
+ "data.season": {
294
+ "$eq": season
295
+ }
271
296
  }
272
- }]
273
- },]
274
- }
297
+ ]
298
+ },
299
+ ]
300
+ }
275
301
  }, {
276
302
  "$project": {
277
303
  "data.year": 1,
@@ -296,15 +322,17 @@ class FinanceReportFetcher(BaseTEJFetcher):
296
322
  "$unwind": "$data"
297
323
  }, {
298
324
  "$match": {
299
- "$and": [{
300
- "data.year": {
301
- "$in": select_year
302
- }
303
- }, {
304
- "data.season": {
305
- "$eq": season
325
+ "$and": [
326
+ {
327
+ "data.year": {
328
+ "$in": select_year
329
+ }
330
+ }, {
331
+ "data.season": {
332
+ "$eq": season
333
+ }
306
334
  }
307
- }]
335
+ ]
308
336
  }
309
337
  }, {
310
338
  "$project": project_stage
@@ -315,10 +343,14 @@ class FinanceReportFetcher(BaseTEJFetcher):
315
343
 
316
344
  # 處理計算YoY
317
345
  data_dict = StatsProcessor.list_of_dict_to_dict(
318
- fetched_data, keys=['year', 'season'], data_key=report_type, delimeter='Q')
346
+ fetched_data,
347
+ keys=['year', 'season'],
348
+ data_key=report_type,
349
+ delimeter='Q')
319
350
 
320
351
  if (use_cal):
321
- data_with_YoY = self.cal_YoY(data_dict, start_year, end_year, season)
352
+ data_with_YoY = self.cal_YoY(
353
+ data_dict, start_year, end_year, season)
322
354
  data_df = pd.DataFrame.from_dict(data_with_YoY)
323
355
  data_df = data_df.iloc[:, ::-1].T
324
356
  data_dict = data_df.to_dict()
@@ -328,3 +360,73 @@ class FinanceReportFetcher(BaseTEJFetcher):
328
360
  data_df = pd.DataFrame.from_dict(data_dict)
329
361
  data_df = data_df.iloc[:, ::-1]
330
362
  return data_df
363
+
364
+
365
+ class TEJStockPriceFetcher(BaseTEJFetcher):
366
+
367
+ def __init__(
368
+ self,
369
+ mongo_uri,
370
+ db_name: str = "company",
371
+ collection_name: str = None):
372
+ self.mongo_uri = mongo_uri
373
+ self.db_name = db_name
374
+ self.collection_name = collection_name
375
+
376
+ self.client = MongoClient(self.mongo_uri)
377
+ self.db = self.client[self.db_name]
378
+ self.collection = self.db[self.collection_name]
379
+
380
+ self.check_period = ['1d', '7d', '1m', '3m', '1y', '3y', '5y', '10y', 'all']
381
+
382
+ def get(
383
+ self,
384
+ ticker: str = "2330",
385
+ start_date: str = "2024-10-01",
386
+ period: str = None
387
+ ):
388
+ """
389
+ 取得開高低收資料
390
+ start_date: str: 起始的日期
391
+ period: 指定日期範圍(E.g. 1天, 7天...etc)
392
+ 如果宣告period, 以period為優先
393
+ """
394
+
395
+ assert (
396
+ period is None or period in self.check_period
397
+ ), f"period should be None or {','.join(self.check_period)}"
398
+
399
+ if (period is not None):
400
+ latest_date = self.get_latest_data_time(ticker)
401
+ start_date = self.set_time_shift(date=latest_date, period=period)
402
+ else:
403
+ start_date = datetime.strptime(start_date, "%Y-%m-%d")
404
+
405
+ pipeline = [
406
+ {
407
+ "$match": {
408
+ "ticker": ticker
409
+ }
410
+ }, {
411
+ "$unwind": "$data"
412
+ }, {
413
+ "$match": {
414
+ "data.mdate": {
415
+ "$gt": start_date
416
+ }
417
+ }
418
+ }, {
419
+ "$project": {
420
+ "ticker": 1,
421
+ "data": 1,
422
+ "_id": 0
423
+ }
424
+ }
425
+ ]
426
+ datas = self.collection.aggregate(pipeline).to_list()
427
+
428
+ elements = [element['data'] for element in datas]
429
+
430
+ data_df = pd.DataFrame(elements).set_index('mdate')
431
+
432
+ return data_df
@@ -1,3 +1,19 @@
1
+ Metadata-Version: 2.1
2
+ Name: neurostats_API
3
+ Version: 0.0.20
4
+ Summary: The service of NeuroStats website
5
+ Home-page: https://github.com/NeurowattStats/NeuroStats_API.git
6
+ Author: JasonWang@Neurowatt
7
+ Author-email: jason@neurowatt.ai
8
+ Requires-Python: >=3.6
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: numpy
11
+ Requires-Dist: pandas>=2.2.0
12
+ Requires-Dist: pymongo
13
+ Requires-Dist: pytz
14
+ Requires-Dist: python-dotenv
15
+ Requires-Dist: yfinance
16
+
1
17
  # neurostats_API
2
18
 
3
19
  - [檔案架構](#檔案架構)
@@ -712,6 +728,7 @@ data = fetcher.get(
712
728
  #### 回傳資料
713
729
  ##### `YOY_NOCAL` 與 `QOQ_NOCAL`
714
730
  為回傳`pd.DataFrame`,column名稱為<年份>Q<季>, row名稱為指定財報項目
731
+
715
732
  ```Python
716
733
  # fetch_mode = fetcher.FetchMode.QOQ_NOCAL
717
734
  2024Q3 2024Q2 2024Q1
@@ -726,17 +743,18 @@ bp51 3.111298e+09 3.173919e+09 2.453840e+09
726
743
 
727
744
  ##### `YOY` 與 `QOQ`
728
745
  回傳為`Dict[pd.DataFrame]`, key 為指定的index, DataFrame中則是該index歷年的數值與成長率
746
+ 成長率單位為`%`
729
747
  ```Python
730
748
  # fetch_mode = fetcher.FetchMode.QOQ
731
749
  {
732
750
  'bp41':
733
751
  2024Q3 2024Q2 2024Q1
734
752
  value 7.082005e+07 6.394707e+07 5.761001e+07
735
- growth 1.074791e-01 1.099994e-01 5.532101e-03,
753
+ growth 1.074791e+01 1.099994e+01 5.532101e-01,
736
754
  'bp51':
737
755
  2024Q3 2024Q2 2024Q1
738
756
  value 3.111298e+09 3.145373e+09 3.091985e+09
739
- growth -1.083335e-02 1.726663e-02 -4.159542e-03
757
+ growth -1.083335e+00 1.726663e+00 -4.159542e-01
740
758
  }
741
759
 
742
760
  # fetch_mode = fetcher.FetchMode.YOY
@@ -744,17 +762,17 @@ growth -1.083335e-02 1.726663e-02 -4.159542e-03
744
762
  'bp41':
745
763
  2024Q3 2023Q3 2022Q3
746
764
  value 7.082005e+07 5.377231e+07 6.201822e+07
747
- YoY_1 NaN NaN 4.130744e-01
748
- YoY_3 1.729171e-01 9.556684e-02 1.883274e-01
749
- YoY_5 1.389090e-01 1.215242e-01 1.642914e-01
750
- YoY_10 1.255138e-01 1.356297e-01 1.559702e-01,
765
+ YoY_1 3.170357e+01 -1.329596e+01 4.130744e+01
766
+ YoY_3 1.729171e+01 9.556684e+00 1.883274e+01
767
+ YoY_5 1.389090e+01 1.215242e+01 1.642914e+01
768
+ YoY_10 1.255138e+01 1.356297e+01 1.559702e+01
751
769
  'bp51':
752
770
  2024Q3 2023Q3 2022Q3
753
771
  value 3.111298e+09 3.173919e+09 2.453840e+09
754
- YoY_1 NaN NaN 3.179539e-01
755
- YoY_3 1.866752e-01 2.766851e-01 2.638677e-01
756
- YoY_5 2.068132e-01 2.479698e-01 1.815106e-01
757
- YoY_10 1.420500e-01 1.586797e-01 1.551364e-01
772
+ YoY_1 -1.972987e+00 2.934499e+01 3.179539e+01
773
+ YoY_3 1.866752e+01 2.766851e+01 2.638677e+01
774
+ YoY_5 2.068132e+01 2.479698e+01 1.815106e+01
775
+ YoY_10 1.420500e+01 1.586797e+01 1.551364e+01
758
776
  }
759
777
  ```
760
778
 
@@ -802,4 +820,29 @@ data = fetcher.get(
802
820
  - 範例輸入: `['bp41', 'bp51']`
803
821
 
804
822
  [TEJ資料集連結](https://tquant.tejwin.com/%E8%B3%87%E6%96%99%E9%9B%86/)
805
- 請看 `公司自結數`
823
+ 請看 `公司自結數`
824
+
825
+ ### 開高低收
826
+ ```Python
827
+ mongo_uri = <MongoDB 的 URI>
828
+ db_name = 'company' # 連接的DB名稱
829
+ collection_name = "TWN/APIPRCD" # 連接的collection對象
830
+ from neurostats_API import TEJStockPriceFetcher
831
+
832
+ fetcher = TEJStockPriceFetcher(
833
+ mongo_uri = mongo_uri,
834
+ db_name = db_name,
835
+ collection_name = collection_name
836
+ )
837
+
838
+ data = fetcher.get(
839
+ ticker = "2330" # 任意的股票代碼
840
+ start_date = "2005-01-01",
841
+ period = "3m"
842
+ ) # -> pd.DataFrame
843
+ ```
844
+ - `ticker`: 股票代碼
845
+ - `start_date`: 搜尋範圍的開始日期
846
+ - `period`: 搜尋的時間範圍長度
847
+
848
+ `period`與`start_date`同時存在時以period優先
@@ -1,4 +1,4 @@
1
- numpy>=2.1.0
1
+ numpy
2
2
  pandas>=2.2.0
3
3
  pymongo
4
4
  pytz
@@ -2,11 +2,11 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='neurostats_API',
5
- version='0.0.18',
5
+ version='0.0.20',
6
6
  long_description=open('README.md', 'r', encoding='utf-8').read(),
7
7
  long_description_content_type='text/markdown',
8
8
  install_requires=[
9
- "numpy>=2.1.0",
9
+ "numpy",
10
10
  "pandas>=2.2.0",
11
11
  "pymongo",
12
12
  "pytz",
@@ -212,4 +212,13 @@ def test_margin_trading():
212
212
 
213
213
  fetched_data = fetcher.query_data()
214
214
 
215
+ pp.pprint(fetched_data)
216
+
217
+ def test_tech():
218
+ from neurostats_API.fetchers import TechFetcher
219
+
220
+ fetcher = TechFetcher(ticker = '1260', db_client=db_client)
221
+
222
+ fetched_data = fetcher.get_quarterly()
223
+
215
224
  pp.pprint(fetched_data)
@@ -23,8 +23,8 @@ def test_QoQ():
23
23
  data = fetcher.get(
24
24
  ticker,
25
25
  fetch_mode = mode,
26
- # start_date="2024-01-01",
27
- # end_date="2024-12-31",
26
+ start_date="2024-01-01",
27
+ end_date="2024-12-31",
28
28
  indexes = []
29
29
  )
30
30
 
@@ -40,10 +40,14 @@ def test_YoY():
40
40
  ]
41
41
 
42
42
  for mode in modes:
43
- data = fetcher.get(
44
- ticker,
45
- mode,
46
- start_date="2024-01-01",
47
- end_date="2024-12-31",
48
- indexes = ["ip12"])
43
+ data = fetcher.get(ticker, mode, indexes = ["r405"])
49
44
  print (data)
45
+
46
+ def test_stock_price():
47
+ from neurostats_API import TEJStockPriceFetcher
48
+
49
+ fetcher = TEJStockPriceFetcher(mongo_uri, collection_name="TWN/APIPRCD")
50
+
51
+ data = fetcher.get(ticker, period='3y')
52
+
53
+ print(data)