akshare-one 0.3.0__py3-none-any.whl → 0.3.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 +31 -31
- akshare_one/financial.py +46 -46
- akshare_one/indicators.py +395 -395
- akshare_one/insider.py +33 -33
- akshare_one/modules/cache.py +9 -9
- akshare_one/modules/eastmoney/client.py +88 -88
- akshare_one/modules/eastmoney/utils.py +104 -104
- akshare_one/modules/financial/base.py +22 -22
- akshare_one/modules/financial/factory.py +44 -44
- akshare_one/modules/financial/sina.py +273 -273
- akshare_one/modules/historical/base.py +47 -47
- akshare_one/modules/historical/eastmoney.py +241 -241
- akshare_one/modules/historical/eastmoney_direct.py +79 -79
- akshare_one/modules/historical/factory.py +48 -48
- akshare_one/modules/historical/sina.py +254 -218
- akshare_one/modules/indicators/base.py +158 -158
- akshare_one/modules/indicators/factory.py +33 -33
- akshare_one/modules/indicators/simple.py +230 -230
- akshare_one/modules/indicators/talib.py +263 -263
- akshare_one/modules/insider/base.py +28 -28
- akshare_one/modules/insider/factory.py +44 -44
- akshare_one/modules/insider/xueqiu.py +115 -115
- akshare_one/modules/news/base.py +22 -22
- akshare_one/modules/news/eastmoney.py +47 -47
- akshare_one/modules/news/factory.py +44 -44
- akshare_one/modules/realtime/base.py +27 -27
- akshare_one/modules/realtime/eastmoney.py +57 -57
- akshare_one/modules/realtime/eastmoney_direct.py +37 -37
- akshare_one/modules/realtime/factory.py +48 -48
- akshare_one/modules/realtime/xueqiu.py +60 -60
- akshare_one/modules/utils.py +10 -10
- akshare_one/news.py +27 -27
- akshare_one/stock.py +78 -78
- {akshare_one-0.3.0.dist-info → akshare_one-0.3.1.dist-info}/METADATA +70 -70
- akshare_one-0.3.1.dist-info/RECORD +39 -0
- {akshare_one-0.3.0.dist-info → akshare_one-0.3.1.dist-info}/licenses/LICENSE +21 -21
- akshare_one-0.3.0.dist-info/RECORD +0 -39
- {akshare_one-0.3.0.dist-info → akshare_one-0.3.1.dist-info}/WHEEL +0 -0
- {akshare_one-0.3.0.dist-info → akshare_one-0.3.1.dist-info}/top_level.txt +0 -0
akshare_one/insider.py
CHANGED
@@ -1,33 +1,33 @@
|
|
1
|
-
"""内部交易数据模块
|
2
|
-
|
3
|
-
包含上市公司内部交易相关功能
|
4
|
-
"""
|
5
|
-
|
6
|
-
import pandas as pd
|
7
|
-
from .modules.insider.factory import InsiderDataFactory
|
8
|
-
|
9
|
-
|
10
|
-
def get_inner_trade_data(symbol: str, source: str = "xueqiu") -> "pd.DataFrame":
|
11
|
-
"""获取雪球内部交易数据
|
12
|
-
|
13
|
-
Args:
|
14
|
-
source: 数据源 (目前支持 "xueqiu")
|
15
|
-
symbol: 股票代码,如"600000"
|
16
|
-
|
17
|
-
Returns:
|
18
|
-
pd.DataFrame:
|
19
|
-
- symbol: 股票代码
|
20
|
-
- issuer: 股票名称
|
21
|
-
- name: 变动人
|
22
|
-
- title: 董监高职务
|
23
|
-
- transaction_date: 变动日期(UTC时区)
|
24
|
-
- transaction_shares: 变动股数
|
25
|
-
- transaction_price_per_share: 成交均价
|
26
|
-
- shares_owned_after_transaction: 变动后持股数
|
27
|
-
- relationship: 与董监高关系
|
28
|
-
- is_board_director: 是否为董事会成员
|
29
|
-
- transaction_value: 交易金额(变动股数*成交均价)
|
30
|
-
- shares_owned_before_transaction: 变动前持股数
|
31
|
-
"""
|
32
|
-
provider = InsiderDataFactory.get_provider(source, symbol=symbol)
|
33
|
-
return provider.get_inner_trade_data()
|
1
|
+
"""内部交易数据模块
|
2
|
+
|
3
|
+
包含上市公司内部交易相关功能
|
4
|
+
"""
|
5
|
+
|
6
|
+
import pandas as pd
|
7
|
+
from .modules.insider.factory import InsiderDataFactory
|
8
|
+
|
9
|
+
|
10
|
+
def get_inner_trade_data(symbol: str, source: str = "xueqiu") -> "pd.DataFrame":
|
11
|
+
"""获取雪球内部交易数据
|
12
|
+
|
13
|
+
Args:
|
14
|
+
source: 数据源 (目前支持 "xueqiu")
|
15
|
+
symbol: 股票代码,如"600000"
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
pd.DataFrame:
|
19
|
+
- symbol: 股票代码
|
20
|
+
- issuer: 股票名称
|
21
|
+
- name: 变动人
|
22
|
+
- title: 董监高职务
|
23
|
+
- transaction_date: 变动日期(UTC时区)
|
24
|
+
- transaction_shares: 变动股数
|
25
|
+
- transaction_price_per_share: 成交均价
|
26
|
+
- shares_owned_after_transaction: 变动后持股数
|
27
|
+
- relationship: 与董监高关系
|
28
|
+
- is_board_director: 是否为董事会成员
|
29
|
+
- transaction_value: 交易金额(变动股数*成交均价)
|
30
|
+
- shares_owned_before_transaction: 变动前持股数
|
31
|
+
"""
|
32
|
+
provider = InsiderDataFactory.get_provider(source, symbol=symbol)
|
33
|
+
return provider.get_inner_trade_data()
|
akshare_one/modules/cache.py
CHANGED
@@ -1,9 +1,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
|
-
}
|
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,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")):
|
39
|
-
market = "0"
|
40
|
-
elif symbol.startswith(("600", "601", "603", "605", "688")):
|
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,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
|
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,44 +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
|
+
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
|