akshare-one 0.3.6__py3-none-any.whl → 0.3.8__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 +214 -214
- akshare_one/eastmoney/client.py +80 -80
- akshare_one/eastmoney/utils.py +102 -102
- akshare_one/indicators.py +395 -395
- akshare_one/modules/cache.py +27 -27
- akshare_one/modules/financial/base.py +27 -27
- akshare_one/modules/financial/eastmoney_direct.py +183 -183
- akshare_one/modules/financial/factory.py +46 -46
- akshare_one/modules/financial/sina.py +292 -292
- akshare_one/modules/historical/base.py +47 -47
- akshare_one/modules/historical/eastmoney.py +236 -236
- akshare_one/modules/historical/eastmoney_direct.py +78 -78
- akshare_one/modules/historical/factory.py +48 -48
- akshare_one/modules/historical/sina.py +250 -250
- akshare_one/modules/indicators/base.py +158 -158
- akshare_one/modules/indicators/factory.py +33 -33
- akshare_one/modules/indicators/simple.py +384 -230
- akshare_one/modules/indicators/talib.py +263 -263
- akshare_one/modules/info/base.py +25 -25
- akshare_one/modules/info/eastmoney.py +51 -51
- akshare_one/modules/info/factory.py +44 -44
- akshare_one/modules/insider/base.py +28 -28
- akshare_one/modules/insider/factory.py +44 -44
- akshare_one/modules/insider/xueqiu.py +110 -110
- akshare_one/modules/news/base.py +22 -22
- akshare_one/modules/news/eastmoney.py +43 -43
- akshare_one/modules/news/factory.py +44 -44
- akshare_one/modules/realtime/base.py +27 -27
- akshare_one/modules/realtime/eastmoney.py +53 -53
- akshare_one/modules/realtime/eastmoney_direct.py +36 -36
- akshare_one/modules/realtime/factory.py +48 -48
- akshare_one/modules/realtime/xueqiu.py +57 -57
- akshare_one/modules/utils.py +10 -10
- {akshare_one-0.3.6.dist-info → akshare_one-0.3.8.dist-info}/METADATA +74 -74
- akshare_one-0.3.8.dist-info/RECORD +39 -0
- {akshare_one-0.3.6.dist-info → akshare_one-0.3.8.dist-info}/licenses/LICENSE +21 -21
- akshare_one-0.3.6.dist-info/RECORD +0 -39
- {akshare_one-0.3.6.dist-info → akshare_one-0.3.8.dist-info}/WHEEL +0 -0
- {akshare_one-0.3.6.dist-info → akshare_one-0.3.8.dist-info}/top_level.txt +0 -0
@@ -1,43 +1,43 @@
|
|
1
|
-
import pandas as pd
|
2
|
-
import akshare as ak
|
3
|
-
|
4
|
-
from ..cache import cache
|
5
|
-
from .base import NewsDataProvider
|
6
|
-
|
7
|
-
|
8
|
-
class EastMoneyNews(NewsDataProvider):
|
9
|
-
@cache(
|
10
|
-
"news_cache",
|
11
|
-
key=lambda self: f"eastmoney_news_{self.symbol}",
|
12
|
-
)
|
13
|
-
def get_news_data(self) -> pd.DataFrame:
|
14
|
-
"""获取东方财富个股新闻数据"""
|
15
|
-
raw_df = ak.stock_news_em(symbol=self.symbol)
|
16
|
-
return self._clean_news_data(raw_df)
|
17
|
-
|
18
|
-
def _clean_news_data(self, raw_df: pd.DataFrame) -> pd.DataFrame:
|
19
|
-
"""清理和标准化新闻数据"""
|
20
|
-
column_mapping = {
|
21
|
-
"关键词": "keyword",
|
22
|
-
"新闻标题": "title",
|
23
|
-
"新闻内容": "content",
|
24
|
-
"发布时间": "publish_time",
|
25
|
-
"文章来源": "source",
|
26
|
-
"新闻链接": "url",
|
27
|
-
}
|
28
|
-
|
29
|
-
df = raw_df.rename(columns=column_mapping)
|
30
|
-
|
31
|
-
df["publish_time"] = pd.to_datetime(df["publish_time"]).dt.tz_localize(
|
32
|
-
"Asia/Shanghai"
|
33
|
-
)
|
34
|
-
|
35
|
-
required_columns = [
|
36
|
-
"keyword",
|
37
|
-
"title",
|
38
|
-
"content",
|
39
|
-
"publish_time",
|
40
|
-
"source",
|
41
|
-
"url",
|
42
|
-
]
|
43
|
-
return df[required_columns]
|
1
|
+
import pandas as pd
|
2
|
+
import akshare as ak
|
3
|
+
|
4
|
+
from ..cache import cache
|
5
|
+
from .base import NewsDataProvider
|
6
|
+
|
7
|
+
|
8
|
+
class EastMoneyNews(NewsDataProvider):
|
9
|
+
@cache(
|
10
|
+
"news_cache",
|
11
|
+
key=lambda self: f"eastmoney_news_{self.symbol}",
|
12
|
+
)
|
13
|
+
def get_news_data(self) -> pd.DataFrame:
|
14
|
+
"""获取东方财富个股新闻数据"""
|
15
|
+
raw_df = ak.stock_news_em(symbol=self.symbol)
|
16
|
+
return self._clean_news_data(raw_df)
|
17
|
+
|
18
|
+
def _clean_news_data(self, raw_df: pd.DataFrame) -> pd.DataFrame:
|
19
|
+
"""清理和标准化新闻数据"""
|
20
|
+
column_mapping = {
|
21
|
+
"关键词": "keyword",
|
22
|
+
"新闻标题": "title",
|
23
|
+
"新闻内容": "content",
|
24
|
+
"发布时间": "publish_time",
|
25
|
+
"文章来源": "source",
|
26
|
+
"新闻链接": "url",
|
27
|
+
}
|
28
|
+
|
29
|
+
df = raw_df.rename(columns=column_mapping)
|
30
|
+
|
31
|
+
df["publish_time"] = pd.to_datetime(df["publish_time"]).dt.tz_localize(
|
32
|
+
"Asia/Shanghai"
|
33
|
+
)
|
34
|
+
|
35
|
+
required_columns = [
|
36
|
+
"keyword",
|
37
|
+
"title",
|
38
|
+
"content",
|
39
|
+
"publish_time",
|
40
|
+
"source",
|
41
|
+
"url",
|
42
|
+
]
|
43
|
+
return df[required_columns]
|
@@ -1,44 +1,44 @@
|
|
1
|
-
from .eastmoney import EastMoneyNews
|
2
|
-
from .base import NewsDataProvider
|
3
|
-
|
4
|
-
|
5
|
-
class NewsDataFactory:
|
6
|
-
"""
|
7
|
-
Factory class for creating news data providers
|
8
|
-
"""
|
9
|
-
|
10
|
-
_providers = {
|
11
|
-
"eastmoney": EastMoneyNews,
|
12
|
-
}
|
13
|
-
|
14
|
-
@classmethod
|
15
|
-
def get_provider(cls, provider_name: str, **kwargs) -> NewsDataProvider:
|
16
|
-
"""
|
17
|
-
Get a news data provider by name
|
18
|
-
|
19
|
-
Args:
|
20
|
-
provider_name: Name of the provider (e.g., 'eastmoney')
|
21
|
-
**kwargs: Additional arguments to pass to the provider's constructor
|
22
|
-
|
23
|
-
Returns:
|
24
|
-
NewsDataProvider: 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 news 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 news 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
|
+
from .eastmoney import EastMoneyNews
|
2
|
+
from .base import NewsDataProvider
|
3
|
+
|
4
|
+
|
5
|
+
class NewsDataFactory:
|
6
|
+
"""
|
7
|
+
Factory class for creating news data providers
|
8
|
+
"""
|
9
|
+
|
10
|
+
_providers = {
|
11
|
+
"eastmoney": EastMoneyNews,
|
12
|
+
}
|
13
|
+
|
14
|
+
@classmethod
|
15
|
+
def get_provider(cls, provider_name: str, **kwargs) -> NewsDataProvider:
|
16
|
+
"""
|
17
|
+
Get a news data provider by name
|
18
|
+
|
19
|
+
Args:
|
20
|
+
provider_name: Name of the provider (e.g., 'eastmoney')
|
21
|
+
**kwargs: Additional arguments to pass to the provider's constructor
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
NewsDataProvider: 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 news 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 news 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,27 +1,27 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
import pandas as pd
|
3
|
-
|
4
|
-
|
5
|
-
class RealtimeDataProvider(ABC):
|
6
|
-
def __init__(self, symbol: str) -> None:
|
7
|
-
self.symbol = symbol
|
8
|
-
|
9
|
-
@abstractmethod
|
10
|
-
def get_current_data(self) -> pd.DataFrame:
|
11
|
-
"""Fetches realtime market data
|
12
|
-
|
13
|
-
Returns:
|
14
|
-
pd.DataFrame:
|
15
|
-
- symbol: 股票代码
|
16
|
-
- price: 最新价
|
17
|
-
- change: 涨跌额
|
18
|
-
- pct_change: 涨跌幅(%)
|
19
|
-
- timestamp: 时间戳
|
20
|
-
- volume: 成交量(手)
|
21
|
-
- amount: 成交额(元)
|
22
|
-
- open: 今开
|
23
|
-
- high: 最高
|
24
|
-
- low: 最低
|
25
|
-
- prev_close: 昨收
|
26
|
-
"""
|
27
|
-
pass
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
import pandas as pd
|
3
|
+
|
4
|
+
|
5
|
+
class RealtimeDataProvider(ABC):
|
6
|
+
def __init__(self, symbol: str) -> None:
|
7
|
+
self.symbol = symbol
|
8
|
+
|
9
|
+
@abstractmethod
|
10
|
+
def get_current_data(self) -> pd.DataFrame:
|
11
|
+
"""Fetches realtime market data
|
12
|
+
|
13
|
+
Returns:
|
14
|
+
pd.DataFrame:
|
15
|
+
- symbol: 股票代码
|
16
|
+
- price: 最新价
|
17
|
+
- change: 涨跌额
|
18
|
+
- pct_change: 涨跌幅(%)
|
19
|
+
- timestamp: 时间戳
|
20
|
+
- volume: 成交量(手)
|
21
|
+
- amount: 成交额(元)
|
22
|
+
- open: 今开
|
23
|
+
- high: 最高
|
24
|
+
- low: 最低
|
25
|
+
- prev_close: 昨收
|
26
|
+
"""
|
27
|
+
pass
|
@@ -1,53 +1,53 @@
|
|
1
|
-
import pandas as pd
|
2
|
-
import akshare as ak
|
3
|
-
|
4
|
-
from ..cache import cache
|
5
|
-
from .base import RealtimeDataProvider
|
6
|
-
|
7
|
-
|
8
|
-
class EastmoneyRealtime(RealtimeDataProvider):
|
9
|
-
@cache(
|
10
|
-
"realtime_cache",
|
11
|
-
key=lambda self, symbol=None: f"eastmoney_{symbol if symbol else 'all'}",
|
12
|
-
)
|
13
|
-
def get_current_data(self) -> pd.DataFrame:
|
14
|
-
"""获取沪深京A股实时行情数据"""
|
15
|
-
raw_df = ak.stock_zh_a_spot_em()
|
16
|
-
df = self._clean_spot_data(raw_df)
|
17
|
-
if self.symbol:
|
18
|
-
df = df[df["symbol"] == self.symbol].reset_index(drop=True)
|
19
|
-
return df
|
20
|
-
|
21
|
-
def _clean_spot_data(self, raw_df: pd.DataFrame) -> pd.DataFrame:
|
22
|
-
"""清理和标准化实时行情数据"""
|
23
|
-
column_mapping = {
|
24
|
-
"代码": "symbol",
|
25
|
-
"最新价": "price",
|
26
|
-
"涨跌额": "change",
|
27
|
-
"涨跌幅": "pct_change",
|
28
|
-
"成交量": "volume",
|
29
|
-
"成交额": "amount",
|
30
|
-
"今开": "open",
|
31
|
-
"最高": "high",
|
32
|
-
"最低": "low",
|
33
|
-
"昨收": "prev_close",
|
34
|
-
}
|
35
|
-
|
36
|
-
df = raw_df.rename(columns=column_mapping)
|
37
|
-
|
38
|
-
df = df.assign(timestamp=lambda x: pd.Timestamp.now(tz="Asia/Shanghai"))
|
39
|
-
|
40
|
-
required_columns = [
|
41
|
-
"symbol",
|
42
|
-
"price",
|
43
|
-
"change",
|
44
|
-
"pct_change",
|
45
|
-
"timestamp",
|
46
|
-
"volume",
|
47
|
-
"amount",
|
48
|
-
"open",
|
49
|
-
"high",
|
50
|
-
"low",
|
51
|
-
"prev_close",
|
52
|
-
]
|
53
|
-
return df[required_columns]
|
1
|
+
import pandas as pd
|
2
|
+
import akshare as ak
|
3
|
+
|
4
|
+
from ..cache import cache
|
5
|
+
from .base import RealtimeDataProvider
|
6
|
+
|
7
|
+
|
8
|
+
class EastmoneyRealtime(RealtimeDataProvider):
|
9
|
+
@cache(
|
10
|
+
"realtime_cache",
|
11
|
+
key=lambda self, symbol=None: f"eastmoney_{symbol if symbol else 'all'}",
|
12
|
+
)
|
13
|
+
def get_current_data(self) -> pd.DataFrame:
|
14
|
+
"""获取沪深京A股实时行情数据"""
|
15
|
+
raw_df = ak.stock_zh_a_spot_em()
|
16
|
+
df = self._clean_spot_data(raw_df)
|
17
|
+
if self.symbol:
|
18
|
+
df = df[df["symbol"] == self.symbol].reset_index(drop=True)
|
19
|
+
return df
|
20
|
+
|
21
|
+
def _clean_spot_data(self, raw_df: pd.DataFrame) -> pd.DataFrame:
|
22
|
+
"""清理和标准化实时行情数据"""
|
23
|
+
column_mapping = {
|
24
|
+
"代码": "symbol",
|
25
|
+
"最新价": "price",
|
26
|
+
"涨跌额": "change",
|
27
|
+
"涨跌幅": "pct_change",
|
28
|
+
"成交量": "volume",
|
29
|
+
"成交额": "amount",
|
30
|
+
"今开": "open",
|
31
|
+
"最高": "high",
|
32
|
+
"最低": "low",
|
33
|
+
"昨收": "prev_close",
|
34
|
+
}
|
35
|
+
|
36
|
+
df = raw_df.rename(columns=column_mapping)
|
37
|
+
|
38
|
+
df = df.assign(timestamp=lambda x: pd.Timestamp.now(tz="Asia/Shanghai"))
|
39
|
+
|
40
|
+
required_columns = [
|
41
|
+
"symbol",
|
42
|
+
"price",
|
43
|
+
"change",
|
44
|
+
"pct_change",
|
45
|
+
"timestamp",
|
46
|
+
"volume",
|
47
|
+
"amount",
|
48
|
+
"open",
|
49
|
+
"high",
|
50
|
+
"low",
|
51
|
+
"prev_close",
|
52
|
+
]
|
53
|
+
return df[required_columns]
|
@@ -1,36 +1,36 @@
|
|
1
|
-
import pandas as pd
|
2
|
-
from .base import RealtimeDataProvider
|
3
|
-
from ..cache import cache
|
4
|
-
from akshare_one.eastmoney.client import EastMoneyClient
|
5
|
-
from akshare_one.eastmoney.utils import parse_realtime_data
|
6
|
-
|
7
|
-
|
8
|
-
class EastMoneyDirectRealtime(RealtimeDataProvider):
|
9
|
-
"""Direct implementation for EastMoney realtime stock data API"""
|
10
|
-
|
11
|
-
def __init__(self, symbol: str):
|
12
|
-
super().__init__(symbol)
|
13
|
-
self.client = EastMoneyClient()
|
14
|
-
|
15
|
-
@cache(
|
16
|
-
"realtime_cache",
|
17
|
-
key=lambda self: f"eastmoney_direct_realtime_{self.symbol}",
|
18
|
-
)
|
19
|
-
def get_current_data(self) -> pd.DataFrame:
|
20
|
-
"""Get real-time stock data"""
|
21
|
-
try:
|
22
|
-
raw_data = self.client.fetch_realtime_quote(self.symbol)
|
23
|
-
|
24
|
-
if raw_data.get("rc") != 0:
|
25
|
-
raise ValueError(f"API returned error: {raw_data.get('msg')}")
|
26
|
-
|
27
|
-
df = parse_realtime_data(raw_data)
|
28
|
-
|
29
|
-
# Ensure the output matches the base class definition
|
30
|
-
if self.symbol:
|
31
|
-
df = df[df["symbol"] == self.symbol].reset_index(drop=True)
|
32
|
-
|
33
|
-
return df
|
34
|
-
|
35
|
-
except Exception as e:
|
36
|
-
raise ValueError(f"Failed to get real-time data for {self.symbol}: {e}")
|
1
|
+
import pandas as pd
|
2
|
+
from .base import RealtimeDataProvider
|
3
|
+
from ..cache import cache
|
4
|
+
from akshare_one.eastmoney.client import EastMoneyClient
|
5
|
+
from akshare_one.eastmoney.utils import parse_realtime_data
|
6
|
+
|
7
|
+
|
8
|
+
class EastMoneyDirectRealtime(RealtimeDataProvider):
|
9
|
+
"""Direct implementation for EastMoney realtime stock data API"""
|
10
|
+
|
11
|
+
def __init__(self, symbol: str):
|
12
|
+
super().__init__(symbol)
|
13
|
+
self.client = EastMoneyClient()
|
14
|
+
|
15
|
+
@cache(
|
16
|
+
"realtime_cache",
|
17
|
+
key=lambda self: f"eastmoney_direct_realtime_{self.symbol}",
|
18
|
+
)
|
19
|
+
def get_current_data(self) -> pd.DataFrame:
|
20
|
+
"""Get real-time stock data"""
|
21
|
+
try:
|
22
|
+
raw_data = self.client.fetch_realtime_quote(self.symbol)
|
23
|
+
|
24
|
+
if raw_data.get("rc") != 0:
|
25
|
+
raise ValueError(f"API returned error: {raw_data.get('msg')}")
|
26
|
+
|
27
|
+
df = parse_realtime_data(raw_data)
|
28
|
+
|
29
|
+
# Ensure the output matches the base class definition
|
30
|
+
if self.symbol:
|
31
|
+
df = df[df["symbol"] == self.symbol].reset_index(drop=True)
|
32
|
+
|
33
|
+
return df
|
34
|
+
|
35
|
+
except Exception as e:
|
36
|
+
raise ValueError(f"Failed to get real-time data for {self.symbol}: {e}")
|
@@ -1,48 +1,48 @@
|
|
1
|
-
from .eastmoney import EastmoneyRealtime
|
2
|
-
from .xueqiu import XueQiuRealtime
|
3
|
-
from .base import RealtimeDataProvider
|
4
|
-
from .eastmoney_direct import EastMoneyDirectRealtime
|
5
|
-
|
6
|
-
|
7
|
-
class RealtimeDataFactory:
|
8
|
-
"""
|
9
|
-
Factory class for creating realtime data providers
|
10
|
-
"""
|
11
|
-
|
12
|
-
_providers = {
|
13
|
-
"eastmoney": EastmoneyRealtime,
|
14
|
-
"xueqiu": XueQiuRealtime,
|
15
|
-
"eastmoney_direct": EastMoneyDirectRealtime,
|
16
|
-
}
|
17
|
-
|
18
|
-
@classmethod
|
19
|
-
def get_provider(cls, provider_name: str, **kwargs) -> RealtimeDataProvider:
|
20
|
-
"""
|
21
|
-
Get a realtime data provider by name
|
22
|
-
|
23
|
-
Args:
|
24
|
-
provider_name: Name of the provider (e.g., 'eastmoney')
|
25
|
-
**kwargs: Additional arguments to pass to the provider's constructor
|
26
|
-
|
27
|
-
Returns:
|
28
|
-
RealtimeDataProvider: An instance of the requested provider
|
29
|
-
|
30
|
-
Raises:
|
31
|
-
ValueError: If the requested provider is not found
|
32
|
-
"""
|
33
|
-
provider_class = cls._providers.get(provider_name.lower())
|
34
|
-
if not provider_class:
|
35
|
-
raise ValueError(f"Unknown realtime data provider: {provider_name}")
|
36
|
-
|
37
|
-
return provider_class(**kwargs)
|
38
|
-
|
39
|
-
@classmethod
|
40
|
-
def register_provider(cls, name: str, provider_class: type):
|
41
|
-
"""
|
42
|
-
Register a new realtime data provider
|
43
|
-
|
44
|
-
Args:
|
45
|
-
name: Name to associate with this provider
|
46
|
-
provider_class: The provider class to register
|
47
|
-
"""
|
48
|
-
cls._providers[name.lower()] = provider_class
|
1
|
+
from .eastmoney import EastmoneyRealtime
|
2
|
+
from .xueqiu import XueQiuRealtime
|
3
|
+
from .base import RealtimeDataProvider
|
4
|
+
from .eastmoney_direct import EastMoneyDirectRealtime
|
5
|
+
|
6
|
+
|
7
|
+
class RealtimeDataFactory:
|
8
|
+
"""
|
9
|
+
Factory class for creating realtime data providers
|
10
|
+
"""
|
11
|
+
|
12
|
+
_providers = {
|
13
|
+
"eastmoney": EastmoneyRealtime,
|
14
|
+
"xueqiu": XueQiuRealtime,
|
15
|
+
"eastmoney_direct": EastMoneyDirectRealtime,
|
16
|
+
}
|
17
|
+
|
18
|
+
@classmethod
|
19
|
+
def get_provider(cls, provider_name: str, **kwargs) -> RealtimeDataProvider:
|
20
|
+
"""
|
21
|
+
Get a realtime data provider by name
|
22
|
+
|
23
|
+
Args:
|
24
|
+
provider_name: Name of the provider (e.g., 'eastmoney')
|
25
|
+
**kwargs: Additional arguments to pass to the provider's constructor
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
RealtimeDataProvider: An instance of the requested provider
|
29
|
+
|
30
|
+
Raises:
|
31
|
+
ValueError: If the requested provider is not found
|
32
|
+
"""
|
33
|
+
provider_class = cls._providers.get(provider_name.lower())
|
34
|
+
if not provider_class:
|
35
|
+
raise ValueError(f"Unknown realtime data provider: {provider_name}")
|
36
|
+
|
37
|
+
return provider_class(**kwargs)
|
38
|
+
|
39
|
+
@classmethod
|
40
|
+
def register_provider(cls, name: str, provider_class: type):
|
41
|
+
"""
|
42
|
+
Register a new realtime data provider
|
43
|
+
|
44
|
+
Args:
|
45
|
+
name: Name to associate with this provider
|
46
|
+
provider_class: The provider class to register
|
47
|
+
"""
|
48
|
+
cls._providers[name.lower()] = provider_class
|
@@ -1,57 +1,57 @@
|
|
1
|
-
import pandas as pd
|
2
|
-
import akshare as ak
|
3
|
-
from ..utils import convert_xieqiu_symbol
|
4
|
-
from ..cache import cache
|
5
|
-
from .base import RealtimeDataProvider
|
6
|
-
|
7
|
-
|
8
|
-
class XueQiuRealtime(RealtimeDataProvider):
|
9
|
-
@cache(
|
10
|
-
"realtime_cache",
|
11
|
-
key=lambda self, symbol=None: f"xueqiu_{symbol}",
|
12
|
-
)
|
13
|
-
def get_current_data(self) -> pd.DataFrame:
|
14
|
-
"""获取雪球实时行情数据
|
15
|
-
|
16
|
-
Args:
|
17
|
-
symbol: 股票代码 ("600000")
|
18
|
-
|
19
|
-
Returns:
|
20
|
-
pd.DataFrame with columns:
|
21
|
-
- symbol: 股票代码
|
22
|
-
- price: 最新价
|
23
|
-
- change: 涨跌额
|
24
|
-
- pct_change: 涨跌幅(%)
|
25
|
-
- timestamp: 时间戳
|
26
|
-
- volume: 成交量(手)
|
27
|
-
- amount: 成交额(元)
|
28
|
-
- open: 今开
|
29
|
-
- high: 最高
|
30
|
-
- low: 最低
|
31
|
-
- prev_close: 昨收
|
32
|
-
"""
|
33
|
-
raw_df = ak.stock_individual_spot_xq(symbol=convert_xieqiu_symbol(self.symbol))
|
34
|
-
|
35
|
-
# Transform to match standard format
|
36
|
-
data = {
|
37
|
-
"symbol": self.symbol,
|
38
|
-
"price": float(raw_df.loc[raw_df["item"] == "现价", "value"].values[0]),
|
39
|
-
"change": float(raw_df.loc[raw_df["item"] == "涨跌", "value"].values[0]),
|
40
|
-
"pct_change": float(
|
41
|
-
raw_df.loc[raw_df["item"] == "涨幅", "value"].values[0]
|
42
|
-
),
|
43
|
-
"timestamp": pd.to_datetime(
|
44
|
-
raw_df.loc[raw_df["item"] == "时间", "value"].values[0]
|
45
|
-
).tz_localize("Asia/Shanghai"),
|
46
|
-
"volume": int(raw_df.loc[raw_df["item"] == "成交量", "value"].values[0])
|
47
|
-
/ 100,
|
48
|
-
"amount": float(raw_df.loc[raw_df["item"] == "成交额", "value"].values[0]),
|
49
|
-
"open": float(raw_df.loc[raw_df["item"] == "今开", "value"].values[0]),
|
50
|
-
"high": float(raw_df.loc[raw_df["item"] == "最高", "value"].values[0]),
|
51
|
-
"low": float(raw_df.loc[raw_df["item"] == "最低", "value"].values[0]),
|
52
|
-
"prev_close": float(
|
53
|
-
raw_df.loc[raw_df["item"] == "昨收", "value"].values[0]
|
54
|
-
),
|
55
|
-
}
|
56
|
-
|
57
|
-
return pd.DataFrame([data])
|
1
|
+
import pandas as pd
|
2
|
+
import akshare as ak
|
3
|
+
from ..utils import convert_xieqiu_symbol
|
4
|
+
from ..cache import cache
|
5
|
+
from .base import RealtimeDataProvider
|
6
|
+
|
7
|
+
|
8
|
+
class XueQiuRealtime(RealtimeDataProvider):
|
9
|
+
@cache(
|
10
|
+
"realtime_cache",
|
11
|
+
key=lambda self, symbol=None: f"xueqiu_{symbol}",
|
12
|
+
)
|
13
|
+
def get_current_data(self) -> pd.DataFrame:
|
14
|
+
"""获取雪球实时行情数据
|
15
|
+
|
16
|
+
Args:
|
17
|
+
symbol: 股票代码 ("600000")
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
pd.DataFrame with columns:
|
21
|
+
- symbol: 股票代码
|
22
|
+
- price: 最新价
|
23
|
+
- change: 涨跌额
|
24
|
+
- pct_change: 涨跌幅(%)
|
25
|
+
- timestamp: 时间戳
|
26
|
+
- volume: 成交量(手)
|
27
|
+
- amount: 成交额(元)
|
28
|
+
- open: 今开
|
29
|
+
- high: 最高
|
30
|
+
- low: 最低
|
31
|
+
- prev_close: 昨收
|
32
|
+
"""
|
33
|
+
raw_df = ak.stock_individual_spot_xq(symbol=convert_xieqiu_symbol(self.symbol))
|
34
|
+
|
35
|
+
# Transform to match standard format
|
36
|
+
data = {
|
37
|
+
"symbol": self.symbol,
|
38
|
+
"price": float(raw_df.loc[raw_df["item"] == "现价", "value"].values[0]),
|
39
|
+
"change": float(raw_df.loc[raw_df["item"] == "涨跌", "value"].values[0]),
|
40
|
+
"pct_change": float(
|
41
|
+
raw_df.loc[raw_df["item"] == "涨幅", "value"].values[0]
|
42
|
+
),
|
43
|
+
"timestamp": pd.to_datetime(
|
44
|
+
raw_df.loc[raw_df["item"] == "时间", "value"].values[0]
|
45
|
+
).tz_localize("Asia/Shanghai"),
|
46
|
+
"volume": int(raw_df.loc[raw_df["item"] == "成交量", "value"].values[0])
|
47
|
+
/ 100,
|
48
|
+
"amount": float(raw_df.loc[raw_df["item"] == "成交额", "value"].values[0]),
|
49
|
+
"open": float(raw_df.loc[raw_df["item"] == "今开", "value"].values[0]),
|
50
|
+
"high": float(raw_df.loc[raw_df["item"] == "最高", "value"].values[0]),
|
51
|
+
"low": float(raw_df.loc[raw_df["item"] == "最低", "value"].values[0]),
|
52
|
+
"prev_close": float(
|
53
|
+
raw_df.loc[raw_df["item"] == "昨收", "value"].values[0]
|
54
|
+
),
|
55
|
+
}
|
56
|
+
|
57
|
+
return pd.DataFrame([data])
|
akshare_one/modules/utils.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
def convert_xieqiu_symbol(symbol: str) -> str:
|
2
|
-
"""
|
3
|
-
Convert Symbol (600000) to XueQiu Symbol (SH600000)
|
4
|
-
"""
|
5
|
-
if symbol.startswith("6"):
|
6
|
-
return f"SH{symbol}"
|
7
|
-
elif symbol.startswith("0") or symbol.startswith("3"):
|
8
|
-
return f"SZ{symbol}"
|
9
|
-
else: # TODO: add more cases
|
10
|
-
return symbol
|
1
|
+
def convert_xieqiu_symbol(symbol: str) -> str:
|
2
|
+
"""
|
3
|
+
Convert Symbol (600000) to XueQiu Symbol (SH600000)
|
4
|
+
"""
|
5
|
+
if symbol.startswith("6"):
|
6
|
+
return f"SH{symbol}"
|
7
|
+
elif symbol.startswith("0") or symbol.startswith("3"):
|
8
|
+
return f"SZ{symbol}"
|
9
|
+
else: # TODO: add more cases
|
10
|
+
return symbol
|