akshare-one 0.1.3__py3-none-any.whl → 0.2.1__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 (32) hide show
  1. akshare_one/__init__.py +2 -3
  2. akshare_one/financial.py +7 -4
  3. akshare_one/insider.py +5 -9
  4. akshare_one/modules/financial/base.py +22 -0
  5. akshare_one/modules/financial/factory.py +44 -0
  6. akshare_one/{adapters → modules/financial}/sina.py +19 -203
  7. akshare_one/modules/historical/base.py +39 -0
  8. akshare_one/modules/historical/eastmoney.py +241 -0
  9. akshare_one/modules/historical/factory.py +46 -0
  10. akshare_one/modules/historical/sina.py +218 -0
  11. akshare_one/modules/insider/base.py +28 -0
  12. akshare_one/modules/insider/factory.py +44 -0
  13. akshare_one/{adapters → modules/insider}/xueqiu.py +16 -76
  14. akshare_one/modules/news/base.py +22 -0
  15. akshare_one/modules/news/eastmoney.py +47 -0
  16. akshare_one/modules/news/factory.py +44 -0
  17. akshare_one/modules/realtime/base.py +27 -0
  18. akshare_one/modules/realtime/eastmoney.py +57 -0
  19. akshare_one/modules/realtime/factory.py +46 -0
  20. akshare_one/modules/realtime/xueqiu.py +60 -0
  21. akshare_one/modules/utils.py +10 -0
  22. akshare_one/news.py +3 -4
  23. akshare_one/stock.py +17 -31
  24. {akshare_one-0.1.3.dist-info → akshare_one-0.2.1.dist-info}/METADATA +9 -9
  25. akshare_one-0.2.1.dist-info/RECORD +29 -0
  26. {akshare_one-0.1.3.dist-info → akshare_one-0.2.1.dist-info}/WHEEL +1 -1
  27. akshare_one/adapters/__init__.py +0 -7
  28. akshare_one/adapters/eastmoney.py +0 -353
  29. akshare_one-0.1.3.dist-info/RECORD +0 -15
  30. /akshare_one/{adapters/cache → modules}/cache.py +0 -0
  31. {akshare_one-0.1.3.dist-info → akshare_one-0.2.1.dist-info}/licenses/LICENSE +0 -0
  32. {akshare_one-0.1.3.dist-info → akshare_one-0.2.1.dist-info}/top_level.txt +0 -0
akshare_one/__init__.py CHANGED
@@ -7,12 +7,11 @@ Provides standardized access to various financial data sources with:
7
7
 
8
8
  Example:
9
9
  >>> from akshare_one import get_hist_data, get_realtime_data
10
+ >>> # 获取股票历史数据
10
11
  >>> df = get_hist_data("600000", interval="day")
11
12
  >>> print(df.head())
12
- >>> # 获取单只股票实时数据
13
+ >>> # 获取股票实时数据
13
14
  >>> df = get_realtime_data(symbol="600000")
14
- >>> # 获取所有股票实时数据
15
- >>> df = get_realtime_data()
16
15
  """
17
16
 
18
17
  from .stock import get_hist_data, get_realtime_data
akshare_one/financial.py CHANGED
@@ -4,7 +4,7 @@
4
4
  """
5
5
 
6
6
  import pandas as pd
7
- from .adapters import SinaAdapter
7
+ from akshare_one.modules.financial.factory import FinancialDataFactory
8
8
 
9
9
 
10
10
  def get_balance_sheet(symbol: str, source: str = "sina") -> "pd.DataFrame":
@@ -15,7 +15,8 @@ def get_balance_sheet(symbol: str, source: str = "sina") -> "pd.DataFrame":
15
15
  source: 数据源 ("sina")
16
16
  """
17
17
  if source == "sina":
18
- return SinaAdapter().get_balance_sheet(symbol=symbol)
18
+ provider = FinancialDataFactory.get_provider(source, symbol=symbol)
19
+ return provider.get_balance_sheet()
19
20
  raise ValueError(f"Unsupported data source: {source}")
20
21
 
21
22
 
@@ -27,7 +28,8 @@ def get_income_statement(symbol: str, source: str = "sina") -> "pd.DataFrame":
27
28
  source: 数据源 ("sina")
28
29
  """
29
30
  if source == "sina":
30
- return SinaAdapter().get_income_statement(symbol=symbol)
31
+ provider = FinancialDataFactory.get_provider(source, symbol=symbol)
32
+ return provider.get_income_statement()
31
33
  raise ValueError(f"Unsupported data source: {source}")
32
34
 
33
35
 
@@ -39,5 +41,6 @@ def get_cash_flow(symbol: str, source: str = "sina") -> "pd.DataFrame":
39
41
  source: 数据源 ("sina")
40
42
  """
41
43
  if source == "sina":
42
- return SinaAdapter().get_cash_flow(symbol=symbol)
44
+ provider = FinancialDataFactory.get_provider(source, symbol=symbol)
45
+ return provider.get_cash_flow()
43
46
  raise ValueError(f"Unsupported data source: {source}")
akshare_one/insider.py CHANGED
@@ -3,19 +3,16 @@
3
3
  包含上市公司内部交易相关功能
4
4
  """
5
5
 
6
- from typing import Optional
7
6
  import pandas as pd
8
- from .adapters import XueQiuAdapter
7
+ from .modules.insider.factory import InsiderDataFactory
9
8
 
10
9
 
11
- def get_inner_trade_data(
12
- source: str = "xueqiu", symbol: Optional[str] = None
13
- ) -> "pd.DataFrame":
10
+ def get_inner_trade_data(symbol: str, source: str = "xueqiu") -> "pd.DataFrame":
14
11
  """获取雪球内部交易数据
15
12
 
16
13
  Args:
17
14
  source: 数据源 (目前支持 "xueqiu")
18
- symbol: 可选股票代码,如"600000",不传则返回所有数据
15
+ symbol: 股票代码,如"600000"
19
16
 
20
17
  Returns:
21
18
  pd.DataFrame:
@@ -32,6 +29,5 @@ def get_inner_trade_data(
32
29
  - transaction_value: 交易金额(变动股数*成交均价)
33
30
  - shares_owned_before_transaction: 变动前持股数
34
31
  """
35
- if source == "xueqiu":
36
- return XueQiuAdapter().get_inner_trade_data(symbol=symbol)
37
- raise ValueError(f"Unsupported data source: {source}")
32
+ provider = InsiderDataFactory.get_provider(source, symbol=symbol)
33
+ return provider.get_inner_trade_data()
@@ -0,0 +1,22 @@
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
@@ -0,0 +1,44 @@
1
+ from .sina import SinaFinancialReport
2
+ from .base import FinancialDataProvider
3
+
4
+
5
+ class FinancialDataFactory:
6
+ """
7
+ Factory class for creating financial data providers
8
+ """
9
+
10
+ _providers = {
11
+ "sina": SinaFinancialReport,
12
+ }
13
+
14
+ @classmethod
15
+ def get_provider(cls, provider_name: str, **kwargs) -> FinancialDataProvider:
16
+ """
17
+ Get a financial data provider by name
18
+
19
+ Args:
20
+ provider_name: Name of the provider (e.g., 'sina')
21
+ **kwargs: Additional arguments to pass to the provider's constructor
22
+
23
+ Returns:
24
+ FinancialDataProvider: An instance of the requested provider
25
+
26
+ Raises:
27
+ ValueError: If the requested provider is not found
28
+ """
29
+ provider_class = cls._providers.get(provider_name.lower())
30
+ if not provider_class:
31
+ raise ValueError(f"Unknown financial data provider: {provider_name}")
32
+
33
+ return provider_class(**kwargs)
34
+
35
+ @classmethod
36
+ def register_provider(cls, name: str, provider_class: type):
37
+ """
38
+ Register a new financial data provider
39
+
40
+ Args:
41
+ name: Name to associate with this provider
42
+ provider_class: The provider class to register
43
+ """
44
+ cls._providers[name.lower()] = provider_class
@@ -1,22 +1,23 @@
1
+ from cachetools import cached
1
2
  import pandas as pd
2
3
  import akshare as ak
3
- from cachetools import cached
4
- from .cache.cache import CACHE_CONFIG
5
4
 
5
+ from akshare_one.modules.cache import CACHE_CONFIG
6
+ from .base import FinancialDataProvider
6
7
 
7
- class SinaAdapter:
8
- """Adapter for Sina financial data API
9
8
 
10
- Includes:
11
- - Financial reports (balance sheet, income statement, cash flow)
12
- - Historical market data
13
- """
9
+ class SinaFinancialReport(FinancialDataProvider):
10
+ def __init__(self, symbol: str) -> None:
11
+ super().__init__(symbol)
12
+ self.stock = (
13
+ f"sh{symbol}" if not symbol.startswith(("sh", "sz", "bj")) else symbol
14
+ )
14
15
 
15
16
  @cached(
16
17
  CACHE_CONFIG["financial_cache"],
17
- key=lambda self, symbol: f"sina_balance_{symbol}",
18
+ key=lambda self, symbol=None: f"sina_balance_{self.symbol}",
18
19
  )
19
- def get_balance_sheet(self, symbol: str) -> pd.DataFrame:
20
+ def get_balance_sheet(self) -> pd.DataFrame:
20
21
  """获取资产负债表数据
21
22
 
22
23
  Args:
@@ -25,15 +26,14 @@ class SinaAdapter:
25
26
  Returns:
26
27
  Standardized DataFrame with balance sheet data
27
28
  """
28
- stock = f"sh{symbol}" if not symbol.startswith(("sh", "sz", "bj")) else symbol
29
- raw_df = ak.stock_financial_report_sina(stock=stock, symbol="资产负债表")
29
+ raw_df = ak.stock_financial_report_sina(stock=self.stock, symbol="资产负债表")
30
30
  return self._clean_balance_data(raw_df)
31
31
 
32
32
  @cached(
33
33
  CACHE_CONFIG["financial_cache"],
34
- key=lambda self, symbol: f"sina_income_{symbol}",
34
+ key=lambda self, symbol=None: f"sina_income_{self.symbol}",
35
35
  )
36
- def get_income_statement(self, symbol: str) -> pd.DataFrame:
36
+ def get_income_statement(self) -> pd.DataFrame:
37
37
  """获取利润表数据
38
38
 
39
39
  Args:
@@ -42,14 +42,14 @@ class SinaAdapter:
42
42
  Returns:
43
43
  Standardized DataFrame with income statement data
44
44
  """
45
- stock = f"sh{symbol}" if not symbol.startswith(("sh", "sz", "bj")) else symbol
46
- raw_df = ak.stock_financial_report_sina(stock=stock, symbol="利润表")
45
+ raw_df = ak.stock_financial_report_sina(stock=self.stock, symbol="利润表")
47
46
  return self._clean_income_data(raw_df)
48
47
 
49
48
  @cached(
50
- CACHE_CONFIG["financial_cache"], key=lambda self, symbol: f"sina_cash_{symbol}"
49
+ CACHE_CONFIG["financial_cache"],
50
+ key=lambda self, symbol=None: f"sina_cash_{self.symbol}",
51
51
  )
52
- def get_cash_flow(self, symbol: str) -> pd.DataFrame:
52
+ def get_cash_flow(self) -> pd.DataFrame:
53
53
  """获取现金流量表数据
54
54
 
55
55
  Args:
@@ -58,8 +58,7 @@ class SinaAdapter:
58
58
  Returns:
59
59
  Standardized DataFrame with cash flow data
60
60
  """
61
- stock = f"sh{symbol}" if not symbol.startswith(("sh", "sz", "bj")) else symbol
62
- raw_df = ak.stock_financial_report_sina(stock=stock, symbol="现金流量表")
61
+ raw_df = ak.stock_financial_report_sina(stock=self.stock, symbol="现金流量表")
63
62
  return self._clean_cash_data(raw_df)
64
63
 
65
64
  def _clean_cash_data(self, raw_df: pd.DataFrame) -> pd.DataFrame:
@@ -207,189 +206,6 @@ class SinaAdapter:
207
206
  available_columns = [col for col in required_columns if col in raw_df.columns]
208
207
  return raw_df[available_columns]
209
208
 
210
- @cached(
211
- CACHE_CONFIG["hist_data_cache"],
212
- key=lambda self,
213
- symbol,
214
- interval,
215
- interval_multiplier,
216
- start_date,
217
- end_date,
218
- adjust: (
219
- "sina",
220
- symbol,
221
- interval,
222
- interval_multiplier,
223
- start_date,
224
- end_date,
225
- adjust,
226
- ),
227
- )
228
- def get_hist_data(
229
- self,
230
- symbol: str,
231
- interval: str = "day",
232
- interval_multiplier: int = 1,
233
- start_date: str = "1970-01-01",
234
- end_date: str = "2030-12-31",
235
- adjust: str = "none",
236
- ) -> pd.DataFrame:
237
- """获取新浪历史行情数据
238
-
239
- Args:
240
- symbol: 股票代码 (如 "600000")
241
- interval: 时间粒度 (支持: "minute", "hour", "day", "week", "month", "year")
242
- interval_multiplier: 时间间隔倍数 (如: 5分钟数据设为5)
243
- start_date: 开始日期 (YYYY-MM-DD)
244
- end_date: 结束日期 (YYYY-MM-DD)
245
- adjust: 复权类型 ('none','qfq','hfq','qfq-factor','hfq-factor')
246
-
247
- Returns:
248
- Standardized DataFrame with OHLCV data
249
- """
250
- interval = interval.lower()
251
- stock = f"sh{symbol}" if not symbol.startswith(("sh", "sz", "bj")) else symbol
252
-
253
- if interval == "minute":
254
- raw_df = ak.stock_zh_a_minute(
255
- symbol=stock,
256
- period="1",
257
- adjust=adjust if adjust != "none" else "",
258
- )
259
- raw_df = raw_df.rename(columns={"day": "date"})
260
- raw_df["date"] = pd.to_datetime(raw_df["date"])
261
- raw_df = raw_df.set_index("date")
262
- raw_df = (
263
- raw_df.resample(f"{interval_multiplier}min")
264
- .agg(
265
- {
266
- "open": "first",
267
- "high": "max",
268
- "low": "min",
269
- "close": "last",
270
- "volume": "sum",
271
- }
272
- )
273
- .reset_index()
274
- )
275
-
276
- elif interval == "hour":
277
- if interval_multiplier < 1:
278
- raise ValueError("Hour interval multiplier must be >= 1")
279
-
280
- raw_df = ak.stock_zh_a_minute(
281
- symbol=stock,
282
- period="60",
283
- adjust=adjust if adjust != "none" else "",
284
- )
285
- raw_df = raw_df.rename(columns={"day": "date"})
286
- raw_df["date"] = pd.to_datetime(raw_df["date"])
287
- raw_df = raw_df.set_index("date")
288
- raw_df = (
289
- raw_df.resample(f"{interval_multiplier}h")
290
- .agg(
291
- {
292
- "open": "first",
293
- "high": "max",
294
- "low": "min",
295
- "close": "last",
296
- "volume": "sum",
297
- }
298
- )
299
- .reset_index()
300
- )
301
-
302
- elif interval in ["day", "week", "month", "year"]:
303
- # Convert date format from YYYY-MM-DD to YYYYMMDD
304
- start_date = (
305
- start_date.replace("-", "") if "-" in start_date else start_date
306
- )
307
- end_date = end_date.replace("-", "") if "-" in end_date else end_date
308
-
309
- raw_df = ak.stock_zh_a_daily(
310
- symbol=stock,
311
- start_date=start_date,
312
- end_date=end_date,
313
- adjust=adjust if adjust != "none" else "",
314
- )
315
-
316
- if interval_multiplier > 1:
317
- raw_df = self._resample_data(raw_df, interval, interval_multiplier)
318
- else:
319
- raise ValueError(f"Unsupported interval: {interval}")
320
-
321
- return self._clean_hist_data(raw_df, adjust)
322
-
323
- def _resample_data(
324
- self, df: pd.DataFrame, interval: str, multiplier: int
325
- ) -> pd.DataFrame:
326
- if interval == "day":
327
- freq = f"{multiplier}D"
328
- elif interval == "week":
329
- freq = f"{multiplier}W-MON"
330
- elif interval == "month":
331
- freq = f"{multiplier}MS"
332
- elif interval == "year":
333
- freq = f"{multiplier}AS-JAN"
334
-
335
- df["date"] = pd.to_datetime(df["date"])
336
- df = df.set_index("date")
337
- resampled = df.resample(freq).agg(
338
- {
339
- "open": "first",
340
- "high": "max",
341
- "low": "min",
342
- "close": "last",
343
- "volume": "sum",
344
- }
345
- )
346
- return resampled.reset_index()
347
-
348
- def _clean_hist_data(self, raw_df: pd.DataFrame, adjust: str) -> pd.DataFrame:
349
- """清理和标准化历史行情数据
350
-
351
- Args:
352
- raw_df: 原始数据DataFrame
353
- adjust: 复权类型
354
-
355
- Returns:
356
- 标准化后的DataFrame
357
- """
358
- column_mapping = {
359
- "date": "timestamp",
360
- "open": "open",
361
- "high": "high",
362
- "low": "low",
363
- "close": "close",
364
- "volume": "volume",
365
- }
366
-
367
- df = raw_df.rename(columns=column_mapping)
368
-
369
- # Process timestamp
370
- if "timestamp" in df.columns:
371
- df["timestamp"] = (
372
- pd.to_datetime(df["timestamp"])
373
- .dt.tz_localize("Asia/Shanghai")
374
- .dt.tz_convert("UTC")
375
- )
376
-
377
- # Process volume
378
- if "volume" in df.columns:
379
- df["volume"] = df["volume"].astype("int64")
380
-
381
- # Select and order columns
382
- standard_columns = [
383
- "timestamp",
384
- "open",
385
- "high",
386
- "low",
387
- "close",
388
- "volume",
389
- ]
390
-
391
- return df[[col for col in standard_columns if col in df.columns]]
392
-
393
209
  def _clean_income_data(self, raw_df: pd.DataFrame) -> pd.DataFrame:
394
210
  """清理和标准化利润表数据
395
211
 
@@ -0,0 +1,39 @@
1
+ from abc import ABC, abstractmethod
2
+ import pandas as pd
3
+
4
+
5
+ class HistoricalDataProvider(ABC):
6
+ def __init__(
7
+ self,
8
+ symbol: str,
9
+ interval: str = "day",
10
+ interval_multiplier: int = 1,
11
+ start_date: str = "1970-01-01",
12
+ end_date: str = "2030-12-31",
13
+ adjust: str = "none",
14
+ ) -> None:
15
+ self.symbol = symbol
16
+ self.interval = interval
17
+ self.interval_multiplier = interval_multiplier
18
+ self.start_date = start_date
19
+ self.end_date = end_date
20
+ self.adjust = adjust
21
+
22
+ @classmethod
23
+ def get_supported_intervals(cls):
24
+ return ["minute", "hour", "day", "week", "month", "year"]
25
+
26
+ @abstractmethod
27
+ def get_hist_data(self) -> pd.DataFrame:
28
+ """Fetches historical market data
29
+
30
+ Returns:
31
+ pd.DataFrame:
32
+ - timestamp (UTC)
33
+ - open
34
+ - high
35
+ - low
36
+ - close
37
+ - volume
38
+ """
39
+ pass