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.
- akshare_one/__init__.py +2 -3
- akshare_one/financial.py +7 -4
- akshare_one/insider.py +5 -9
- akshare_one/modules/financial/base.py +22 -0
- akshare_one/modules/financial/factory.py +44 -0
- akshare_one/{adapters → modules/financial}/sina.py +19 -203
- akshare_one/modules/historical/base.py +39 -0
- akshare_one/modules/historical/eastmoney.py +241 -0
- akshare_one/modules/historical/factory.py +46 -0
- akshare_one/modules/historical/sina.py +218 -0
- akshare_one/modules/insider/base.py +28 -0
- akshare_one/modules/insider/factory.py +44 -0
- akshare_one/{adapters → modules/insider}/xueqiu.py +16 -76
- akshare_one/modules/news/base.py +22 -0
- akshare_one/modules/news/eastmoney.py +47 -0
- akshare_one/modules/news/factory.py +44 -0
- akshare_one/modules/realtime/base.py +27 -0
- akshare_one/modules/realtime/eastmoney.py +57 -0
- akshare_one/modules/realtime/factory.py +46 -0
- akshare_one/modules/realtime/xueqiu.py +60 -0
- akshare_one/modules/utils.py +10 -0
- akshare_one/news.py +3 -4
- akshare_one/stock.py +17 -31
- {akshare_one-0.1.3.dist-info → akshare_one-0.2.1.dist-info}/METADATA +9 -9
- akshare_one-0.2.1.dist-info/RECORD +29 -0
- {akshare_one-0.1.3.dist-info → akshare_one-0.2.1.dist-info}/WHEEL +1 -1
- akshare_one/adapters/__init__.py +0 -7
- akshare_one/adapters/eastmoney.py +0 -353
- akshare_one-0.1.3.dist-info/RECORD +0 -15
- /akshare_one/{adapters/cache → modules}/cache.py +0 -0
- {akshare_one-0.1.3.dist-info → akshare_one-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {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 .
|
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
|
-
|
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
|
-
|
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
|
-
|
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 .
|
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:
|
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
|
-
|
36
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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"],
|
49
|
+
CACHE_CONFIG["financial_cache"],
|
50
|
+
key=lambda self, symbol=None: f"sina_cash_{self.symbol}",
|
51
51
|
)
|
52
|
-
def get_cash_flow(self
|
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
|
-
|
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
|