akshare-one 0.3.1__py3-none-any.whl → 0.3.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. akshare_one/__init__.py +214 -31
  2. akshare_one/indicators.py +395 -395
  3. akshare_one/modules/cache.py +10 -9
  4. akshare_one/modules/eastmoney/client.py +88 -88
  5. akshare_one/modules/eastmoney/utils.py +104 -104
  6. akshare_one/modules/financial/base.py +27 -22
  7. akshare_one/modules/financial/eastmoney.py +184 -0
  8. akshare_one/modules/financial/factory.py +46 -44
  9. akshare_one/modules/financial/sina.py +298 -273
  10. akshare_one/modules/historical/base.py +47 -47
  11. akshare_one/modules/historical/eastmoney.py +241 -241
  12. akshare_one/modules/historical/eastmoney_direct.py +79 -79
  13. akshare_one/modules/historical/factory.py +48 -48
  14. akshare_one/modules/historical/sina.py +254 -254
  15. akshare_one/modules/indicators/base.py +158 -158
  16. akshare_one/modules/indicators/factory.py +33 -33
  17. akshare_one/modules/indicators/simple.py +230 -230
  18. akshare_one/modules/indicators/talib.py +263 -263
  19. akshare_one/modules/info/base.py +25 -0
  20. akshare_one/modules/info/eastmoney.py +52 -0
  21. akshare_one/modules/info/factory.py +44 -0
  22. akshare_one/modules/insider/base.py +28 -28
  23. akshare_one/modules/insider/factory.py +44 -44
  24. akshare_one/modules/insider/xueqiu.py +115 -115
  25. akshare_one/modules/news/base.py +22 -22
  26. akshare_one/modules/news/eastmoney.py +47 -47
  27. akshare_one/modules/news/factory.py +44 -44
  28. akshare_one/modules/realtime/base.py +27 -27
  29. akshare_one/modules/realtime/eastmoney.py +57 -57
  30. akshare_one/modules/realtime/eastmoney_direct.py +37 -37
  31. akshare_one/modules/realtime/factory.py +48 -48
  32. akshare_one/modules/realtime/xueqiu.py +60 -60
  33. akshare_one/modules/utils.py +10 -10
  34. {akshare_one-0.3.1.dist-info → akshare_one-0.3.3.dist-info}/METADATA +70 -70
  35. akshare_one-0.3.3.dist-info/RECORD +39 -0
  36. {akshare_one-0.3.1.dist-info → akshare_one-0.3.3.dist-info}/licenses/LICENSE +21 -21
  37. akshare_one/financial.py +0 -46
  38. akshare_one/insider.py +0 -33
  39. akshare_one/news.py +0 -27
  40. akshare_one/stock.py +0 -78
  41. akshare_one-0.3.1.dist-info/RECORD +0 -39
  42. {akshare_one-0.3.1.dist-info → akshare_one-0.3.3.dist-info}/WHEEL +0 -0
  43. {akshare_one-0.3.1.dist-info → akshare_one-0.3.3.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,10 @@
1
- from cachetools import TTLCache
2
-
3
- # 缓存配置
4
- CACHE_CONFIG = {
5
- "hist_data_cache": TTLCache(maxsize=1000, ttl=3600), # 历史数据缓存1小时
6
- "realtime_cache": TTLCache(maxsize=500, ttl=60), # 实时数据缓存1分钟
7
- "news_cache": TTLCache(maxsize=500, ttl=3600),
8
- "financial_cache": TTLCache(maxsize=500, ttl=86400), # 财务数据缓存24小时
9
- }
1
+ from cachetools import TTLCache
2
+
3
+ # 缓存配置
4
+ CACHE_CONFIG = {
5
+ "hist_data_cache": TTLCache(maxsize=1000, ttl=3600), # 历史数据缓存1小时
6
+ "realtime_cache": TTLCache(maxsize=500, ttl=60), # 实时数据缓存1分钟
7
+ "news_cache": TTLCache(maxsize=500, ttl=3600),
8
+ "financial_cache": TTLCache(maxsize=500, ttl=86400), # 财务数据缓存24小时
9
+ "info_cache": TTLCache(maxsize=500, ttl=86400), # 信息数据缓存24小时
10
+ }
@@ -1,88 +1,88 @@
1
- import requests
2
- from typing import Dict, Any
3
-
4
-
5
- class EastMoneyClient:
6
- """
7
- A client for interacting directly with EastMoney's data APIs.
8
- This class handles session management, request signing, and API calls.
9
- """
10
-
11
- def __init__(self):
12
- self.session = requests.Session()
13
- self.session.headers.update(
14
- {
15
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
16
- "Referer": "https://quote.eastmoney.com/",
17
- "Accept": "application/json, text/plain, */*",
18
- "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
19
- }
20
- )
21
-
22
- def _get_security_id(self, symbol: str) -> str:
23
- """
24
- Converts a stock symbol to EastMoney's internal secid format.
25
- e.g., '600519' -> '1.600519', '000001' -> '0.000001'
26
- """
27
- symbol = symbol.upper()
28
- if symbol.startswith("SZ"):
29
- market = "0"
30
- code = symbol[2:]
31
- elif symbol.startswith("SH"):
32
- market = "1"
33
- code = symbol[2:]
34
- elif symbol.startswith("HK"):
35
- market = "116"
36
- code = symbol[2:]
37
- elif len(symbol) == 6:
38
- if symbol.startswith(("000", "001", "002", "003", "300", "200")):
39
- market = "0"
40
- elif symbol.startswith(("600", "601", "603", "605", "688", "900")):
41
- market = "1"
42
- else:
43
- market = "0" # Default to SZ for ambiguity
44
- code = symbol
45
- elif len(symbol) == 5: # HK Market
46
- market = "116"
47
- code = symbol
48
- else:
49
- market = "0"
50
- code = symbol
51
- return f"{market}.{code}"
52
-
53
- def fetch_historical_klines(
54
- self, symbol: str, klt: str, fqt: str, start_date: str, end_date: str
55
- ) -> Dict[str, Any]:
56
- """
57
- Fetches historical K-line (candlestick) data.
58
- """
59
- url = "https://push2his.eastmoney.com/api/qt/stock/kline/get"
60
- secid = self._get_security_id(symbol)
61
- params = {
62
- "fields1": "f1,f2,f3,f4,f5,f6",
63
- "fields2": "f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61",
64
- "klt": klt,
65
- "fqt": fqt,
66
- "secid": secid,
67
- "beg": start_date,
68
- "end": end_date,
69
- }
70
- response = self.session.get(url, params=params)
71
- response.raise_for_status()
72
- return response.json()
73
-
74
- def fetch_realtime_quote(self, symbol: str) -> Dict[str, Any]:
75
- """
76
- Fetches real-time quote data for a single stock.
77
- """
78
- url = "https://push2.eastmoney.com/api/qt/stock/get"
79
- secid = self._get_security_id(symbol)
80
- params = {
81
- "invt": "2",
82
- "fltt": "2",
83
- "fields": "f43,f57,f58,f169,f170,f46,f60,f44,f51,f168,f47,f164,f163,f116,f60,f45,f52,f50,f48,f167,f117,f71,f161,f49,f530",
84
- "secid": secid,
85
- }
86
- response = self.session.get(url, params=params)
87
- response.raise_for_status()
88
- return response.json()
1
+ import requests
2
+ from typing import Dict, Any
3
+
4
+
5
+ class EastMoneyClient:
6
+ """
7
+ A client for interacting directly with EastMoney's data APIs.
8
+ This class handles session management, request signing, and API calls.
9
+ """
10
+
11
+ def __init__(self):
12
+ self.session = requests.Session()
13
+ self.session.headers.update(
14
+ {
15
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
16
+ "Referer": "https://quote.eastmoney.com/",
17
+ "Accept": "application/json, text/plain, */*",
18
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
19
+ }
20
+ )
21
+
22
+ def _get_security_id(self, symbol: str) -> str:
23
+ """
24
+ Converts a stock symbol to EastMoney's internal secid format.
25
+ e.g., '600519' -> '1.600519', '000001' -> '0.000001'
26
+ """
27
+ symbol = symbol.upper()
28
+ if symbol.startswith("SZ"):
29
+ market = "0"
30
+ code = symbol[2:]
31
+ elif symbol.startswith("SH"):
32
+ market = "1"
33
+ code = symbol[2:]
34
+ elif symbol.startswith("HK"):
35
+ market = "116"
36
+ code = symbol[2:]
37
+ elif len(symbol) == 6:
38
+ if symbol.startswith(("000", "001", "002", "003", "300", "200")):
39
+ market = "0"
40
+ elif symbol.startswith(("600", "601", "603", "605", "688", "900")):
41
+ market = "1"
42
+ else:
43
+ market = "0" # Default to SZ for ambiguity
44
+ code = symbol
45
+ elif len(symbol) == 5: # HK Market
46
+ market = "116"
47
+ code = symbol
48
+ else:
49
+ market = "0"
50
+ code = symbol
51
+ return f"{market}.{code}"
52
+
53
+ def fetch_historical_klines(
54
+ self, symbol: str, klt: str, fqt: str, start_date: str, end_date: str
55
+ ) -> Dict[str, Any]:
56
+ """
57
+ Fetches historical K-line (candlestick) data.
58
+ """
59
+ url = "https://push2his.eastmoney.com/api/qt/stock/kline/get"
60
+ secid = self._get_security_id(symbol)
61
+ params = {
62
+ "fields1": "f1,f2,f3,f4,f5,f6",
63
+ "fields2": "f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61",
64
+ "klt": klt,
65
+ "fqt": fqt,
66
+ "secid": secid,
67
+ "beg": start_date,
68
+ "end": end_date,
69
+ }
70
+ response = self.session.get(url, params=params)
71
+ response.raise_for_status()
72
+ return response.json()
73
+
74
+ def fetch_realtime_quote(self, symbol: str) -> Dict[str, Any]:
75
+ """
76
+ Fetches real-time quote data for a single stock.
77
+ """
78
+ url = "https://push2.eastmoney.com/api/qt/stock/get"
79
+ secid = self._get_security_id(symbol)
80
+ params = {
81
+ "invt": "2",
82
+ "fltt": "2",
83
+ "fields": "f43,f57,f58,f169,f170,f46,f60,f44,f51,f168,f47,f164,f163,f116,f60,f45,f52,f50,f48,f167,f117,f71,f161,f49,f530",
84
+ "secid": secid,
85
+ }
86
+ response = self.session.get(url, params=params)
87
+ response.raise_for_status()
88
+ return response.json()
@@ -1,104 +1,104 @@
1
- import pandas as pd
2
- from typing import Dict, Any
3
-
4
-
5
- def parse_kline_data(data: Dict[str, Any]) -> pd.DataFrame:
6
- """
7
- Parses K-line data from the API response into a pandas DataFrame.
8
- """
9
- klines = data.get("data", {}).get("klines", [])
10
- if not klines:
11
- return pd.DataFrame(
12
- columns=["timestamp", "open", "high", "low", "close", "volume"]
13
- )
14
-
15
- records = []
16
- for kline in klines:
17
- parts = kline.split(",")
18
- if len(parts) >= 6:
19
- records.append(
20
- {
21
- "timestamp": parts[0],
22
- "open": float(parts[1]),
23
- "close": float(parts[2]),
24
- "high": float(parts[3]),
25
- "low": float(parts[4]),
26
- "volume": int(parts[5]),
27
- }
28
- )
29
-
30
- df = pd.DataFrame(records)
31
- if not df.empty:
32
- df["timestamp"] = pd.to_datetime(df["timestamp"])
33
- df["timestamp"] = (
34
- df["timestamp"].dt.tz_localize("Asia/Shanghai").dt.tz_convert("UTC")
35
- )
36
- df = df[["timestamp", "open", "high", "low", "close", "volume"]]
37
- return df
38
-
39
-
40
- def parse_realtime_data(data: Dict[str, Any]) -> pd.DataFrame:
41
- """
42
- Parses real-time quote data from the API response into a pandas DataFrame.
43
- """
44
- stock_data = data.get("data")
45
- if not stock_data:
46
- return pd.DataFrame()
47
-
48
- df = pd.DataFrame(
49
- [
50
- {
51
- "symbol": stock_data.get("f57"),
52
- "price": stock_data.get("f43"),
53
- "change": stock_data.get("f169"),
54
- "pct_change": stock_data.get("f170"),
55
- "volume": stock_data.get("f47"),
56
- "amount": stock_data.get("f48"),
57
- "open": stock_data.get("f46"),
58
- "high": stock_data.get("f44"),
59
- "low": stock_data.get("f45"),
60
- "prev_close": stock_data.get("f60"),
61
- }
62
- ]
63
- )
64
- df["timestamp"] = pd.Timestamp.now(tz="Asia/Shanghai").tz_convert("UTC")
65
- return df
66
-
67
-
68
- def resample_historical_data(
69
- df: pd.DataFrame, interval: str, multiplier: int
70
- ) -> pd.DataFrame:
71
- """
72
- Resamples historical data to a specified frequency.
73
- """
74
- if df.empty or multiplier <= 1:
75
- return df
76
-
77
- df = df.set_index("timestamp")
78
-
79
- freq_map = {
80
- "day": f"{multiplier}D",
81
- "week": f"{multiplier}W-MON",
82
- "month": f"{multiplier}MS",
83
- "year": f"{multiplier * 12}MS",
84
- }
85
- freq = freq_map.get(interval)
86
-
87
- if not freq:
88
- return df.reset_index()
89
-
90
- resampled = (
91
- df.resample(freq)
92
- .agg(
93
- {
94
- "open": "first",
95
- "high": "max",
96
- "low": "min",
97
- "close": "last",
98
- "volume": "sum",
99
- }
100
- )
101
- .dropna()
102
- )
103
-
104
- return resampled.reset_index()
1
+ import pandas as pd
2
+ from typing import Dict, Any
3
+
4
+
5
+ def parse_kline_data(data: Dict[str, Any]) -> pd.DataFrame:
6
+ """
7
+ Parses K-line data from the API response into a pandas DataFrame.
8
+ """
9
+ klines = data.get("data", {}).get("klines", [])
10
+ if not klines:
11
+ return pd.DataFrame(
12
+ columns=["timestamp", "open", "high", "low", "close", "volume"]
13
+ )
14
+
15
+ records = []
16
+ for kline in klines:
17
+ parts = kline.split(",")
18
+ if len(parts) >= 6:
19
+ records.append(
20
+ {
21
+ "timestamp": parts[0],
22
+ "open": float(parts[1]),
23
+ "close": float(parts[2]),
24
+ "high": float(parts[3]),
25
+ "low": float(parts[4]),
26
+ "volume": int(parts[5]),
27
+ }
28
+ )
29
+
30
+ df = pd.DataFrame(records)
31
+ if not df.empty:
32
+ df["timestamp"] = pd.to_datetime(df["timestamp"])
33
+ df["timestamp"] = (
34
+ df["timestamp"].dt.tz_localize("Asia/Shanghai").dt.tz_convert("UTC")
35
+ )
36
+ df = df[["timestamp", "open", "high", "low", "close", "volume"]]
37
+ return df
38
+
39
+
40
+ def parse_realtime_data(data: Dict[str, Any]) -> pd.DataFrame:
41
+ """
42
+ Parses real-time quote data from the API response into a pandas DataFrame.
43
+ """
44
+ stock_data = data.get("data")
45
+ if not stock_data:
46
+ return pd.DataFrame()
47
+
48
+ df = pd.DataFrame(
49
+ [
50
+ {
51
+ "symbol": stock_data.get("f57"),
52
+ "price": stock_data.get("f43"),
53
+ "change": stock_data.get("f169"),
54
+ "pct_change": stock_data.get("f170"),
55
+ "volume": stock_data.get("f47"),
56
+ "amount": stock_data.get("f48"),
57
+ "open": stock_data.get("f46"),
58
+ "high": stock_data.get("f44"),
59
+ "low": stock_data.get("f45"),
60
+ "prev_close": stock_data.get("f60"),
61
+ }
62
+ ]
63
+ )
64
+ df["timestamp"] = pd.Timestamp.now(tz="Asia/Shanghai").tz_convert("UTC")
65
+ return df
66
+
67
+
68
+ def resample_historical_data(
69
+ df: pd.DataFrame, interval: str, multiplier: int
70
+ ) -> pd.DataFrame:
71
+ """
72
+ Resamples historical data to a specified frequency.
73
+ """
74
+ if df.empty or multiplier <= 1:
75
+ return df
76
+
77
+ df = df.set_index("timestamp")
78
+
79
+ freq_map = {
80
+ "day": f"{multiplier}D",
81
+ "week": f"{multiplier}W-MON",
82
+ "month": f"{multiplier}MS",
83
+ "year": f"{multiplier * 12}MS",
84
+ }
85
+ freq = freq_map.get(interval)
86
+
87
+ if not freq:
88
+ return df.reset_index()
89
+
90
+ resampled = (
91
+ df.resample(freq)
92
+ .agg(
93
+ {
94
+ "open": "first",
95
+ "high": "max",
96
+ "low": "min",
97
+ "close": "last",
98
+ "volume": "sum",
99
+ }
100
+ )
101
+ .dropna()
102
+ )
103
+
104
+ return resampled.reset_index()
@@ -1,22 +1,27 @@
1
- from abc import ABC, abstractmethod
2
- import pandas as pd
3
-
4
-
5
- class FinancialDataProvider(ABC):
6
- def __init__(self, symbol: str) -> None:
7
- self.symbol = symbol
8
-
9
- @abstractmethod
10
- def get_balance_sheet(self) -> pd.DataFrame:
11
- """Fetches balance sheet data"""
12
- pass
13
-
14
- @abstractmethod
15
- def get_income_statement(self) -> pd.DataFrame:
16
- """Fetches income statement data"""
17
- pass
18
-
19
- @abstractmethod
20
- def get_cash_flow(self) -> pd.DataFrame:
21
- """Fetches cash flow data"""
22
- pass
1
+ from abc import ABC, abstractmethod
2
+ import pandas as pd
3
+
4
+
5
+ class FinancialDataProvider(ABC):
6
+ def __init__(self, symbol: str) -> None:
7
+ self.symbol = symbol
8
+
9
+ @abstractmethod
10
+ def get_balance_sheet(self) -> pd.DataFrame:
11
+ """Fetches balance sheet data"""
12
+ pass
13
+
14
+ @abstractmethod
15
+ def get_income_statement(self) -> pd.DataFrame:
16
+ """Fetches income statement data"""
17
+ pass
18
+
19
+ @abstractmethod
20
+ def get_cash_flow(self) -> pd.DataFrame:
21
+ """Fetches cash flow data"""
22
+ pass
23
+
24
+ @abstractmethod
25
+ def get_financial_metrics(self) -> pd.DataFrame:
26
+ """Fetch financial metrics"""
27
+ pass
@@ -0,0 +1,184 @@
1
+ from cachetools import cached
2
+ import pandas as pd
3
+ import requests
4
+
5
+ from akshare_one.modules.cache import CACHE_CONFIG
6
+ from .base import FinancialDataProvider
7
+
8
+
9
+ class EastMoneyFinancialReport(FinancialDataProvider):
10
+ _balance_sheet_rename_map = {
11
+ "REPORT_DATE": "report_date",
12
+ "TOTAL_ASSETS": "total_assets",
13
+ "FIXED_ASSET": "fixed_assets_net",
14
+ "MONETARYFUNDS": "cash_and_equivalents",
15
+ "ACCOUNTS_RECE": "accounts_receivable",
16
+ "INVENTORY": "inventory",
17
+ "TOTAL_LIABILITIES": "total_liabilities",
18
+ "ACCOUNTS_PAYABLE": "trade_and_non_trade_payables",
19
+ "ADVANCE_RECEIVABLES": "deferred_revenue",
20
+ "TOTAL_EQUITY": "shareholders_equity",
21
+ }
22
+
23
+ _income_statement_rename_map = {
24
+ "REPORT_DATE": "report_date",
25
+ "TOTAL_OPERATE_INCOME": "revenue",
26
+ "TOTAL_OPERATE_COST": "total_operating_costs",
27
+ "OPERATE_PROFIT": "operating_profit",
28
+ "PARENT_NETPROFIT": "net_income_common_stock",
29
+ }
30
+
31
+ _cash_flow_rename_map = {
32
+ "REPORT_DATE": "report_date",
33
+ "NETCASH_OPERATE": "net_cash_flow_from_operations",
34
+ "NETCASH_INVEST": "net_cash_flow_from_investing",
35
+ "NETCASH_FINANCE": "net_cash_flow_from_financing",
36
+ "CCE_ADD": "change_in_cash_and_equivalents",
37
+ }
38
+
39
+ def __init__(self, symbol):
40
+ super().__init__(symbol)
41
+
42
+ def get_income_statement(self):
43
+ pass
44
+
45
+ def get_balance_sheet(self):
46
+ pass
47
+
48
+ def get_cash_flow(self):
49
+ pass
50
+
51
+ @cached(
52
+ CACHE_CONFIG["financial_cache"],
53
+ key=lambda self, symbol=None: f"financial_metrics_{self.symbol}",
54
+ )
55
+ def get_financial_metrics(self) -> pd.DataFrame:
56
+ """获取三大财务报表关键指标"""
57
+ balance_sheet = self._fetch_balance_sheet()
58
+ income_statement = self._fetch_income_statement()
59
+ cash_flow = self._fetch_cash_flow()
60
+
61
+ if balance_sheet.empty and income_statement.empty and cash_flow.empty:
62
+ return pd.DataFrame()
63
+
64
+ merged = pd.merge(
65
+ balance_sheet, income_statement, on="report_date", how="outer"
66
+ )
67
+ merged = pd.merge(merged, cash_flow, on="report_date", how="outer")
68
+
69
+ # Convert report_date to datetime and format as YYYY-MM-DD
70
+ merged["report_date"] = pd.to_datetime(merged["report_date"]).dt.strftime(
71
+ "%Y-%m-%d"
72
+ )
73
+
74
+ # Sort by report_date in descending order (most recent first)
75
+ merged = merged.sort_values("report_date", ascending=False).reset_index(
76
+ drop=True
77
+ )
78
+
79
+ return merged
80
+
81
+ def _fetch_balance_sheet(self) -> pd.DataFrame:
82
+ """
83
+ Get stock balance sheet data from East Money API
84
+ """
85
+ try:
86
+ # API endpoint and parameters
87
+ api_url = "https://datacenter-web.eastmoney.com/api/data/v1/get"
88
+ params = {
89
+ "reportName": "RPT_DMSK_FN_BALANCE",
90
+ "filter": f'(SECURITY_CODE="{self.symbol}")',
91
+ "pageNumber": "1",
92
+ "pageSize": "1000",
93
+ "sortColumns": "REPORT_DATE",
94
+ "sortTypes": "-1",
95
+ "columns": ",".join(self._balance_sheet_rename_map.keys()),
96
+ }
97
+
98
+ # Fetch data from API
99
+ response = requests.get(api_url, params=params)
100
+ response.raise_for_status()
101
+ data = response.json()
102
+
103
+ # Extract the actual data
104
+ if data.get("result") and data["result"].get("data"):
105
+ df = pd.DataFrame(data["result"]["data"])
106
+ df.rename(columns=self._balance_sheet_rename_map, inplace=True)
107
+ return df
108
+ else:
109
+ print("No balance sheet data found in API response")
110
+ return pd.DataFrame()
111
+
112
+ except Exception as e:
113
+ print(f"Error occurred: {str(e)}")
114
+ return pd.DataFrame()
115
+
116
+ def _fetch_income_statement(self) -> pd.DataFrame:
117
+ """
118
+ Get stock income statement data from East Money API
119
+ """
120
+ try:
121
+ # API endpoint and parameters
122
+ api_url = "https://datacenter-web.eastmoney.com/api/data/v1/get"
123
+ params = {
124
+ "reportName": "RPT_DMSK_FN_INCOME",
125
+ "filter": f'(SECURITY_CODE="{self.symbol}")',
126
+ "pageNumber": "1",
127
+ "pageSize": "1000",
128
+ "sortColumns": "REPORT_DATE",
129
+ "sortTypes": "-1",
130
+ "columns": ",".join(self._income_statement_rename_map.keys()),
131
+ }
132
+
133
+ # Fetch data from API
134
+ response = requests.get(api_url, params=params)
135
+ response.raise_for_status()
136
+ data = response.json()
137
+
138
+ # Extract the actual data
139
+ if data.get("result") and data["result"].get("data"):
140
+ df = pd.DataFrame(data["result"]["data"])
141
+ df.rename(columns=self._income_statement_rename_map, inplace=True)
142
+ return df
143
+ else:
144
+ print("No income statement data found in API response")
145
+ return pd.DataFrame()
146
+
147
+ except Exception as e:
148
+ print(f"Error occurred: {str(e)}")
149
+ return pd.DataFrame()
150
+
151
+ def _fetch_cash_flow(self) -> pd.DataFrame:
152
+ """
153
+ Get stock cash flow statement data from East Money API
154
+ """
155
+ try:
156
+ # API endpoint and parameters
157
+ api_url = "https://datacenter-web.eastmoney.com/api/data/v1/get"
158
+ params = {
159
+ "reportName": "RPT_DMSK_FN_CASHFLOW",
160
+ "filter": f'(SECURITY_CODE="{self.symbol}")',
161
+ "pageNumber": "1",
162
+ "pageSize": "1000",
163
+ "sortColumns": "REPORT_DATE",
164
+ "sortTypes": "-1",
165
+ "columns": ",".join(self._cash_flow_rename_map.keys()),
166
+ }
167
+
168
+ # Fetch data from API
169
+ response = requests.get(api_url, params=params)
170
+ response.raise_for_status()
171
+ data = response.json()
172
+
173
+ # Extract the actual data
174
+ if data.get("result") and data["result"].get("data"):
175
+ df = pd.DataFrame(data["result"]["data"])
176
+ df.rename(columns=self._cash_flow_rename_map, inplace=True)
177
+ return df
178
+ else:
179
+ print("No cash flow statement data found in API response")
180
+ return pd.DataFrame()
181
+
182
+ except Exception as e:
183
+ print(f"Error occurred: {str(e)}")
184
+ return pd.DataFrame()