akshare-one 0.1.3__py3-none-any.whl → 0.2.0__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 +4 -5
  4. akshare_one/modules/financial/base.py +41 -0
  5. akshare_one/modules/financial/factory.py +44 -0
  6. akshare_one/{adapters → modules/financial}/sina.py +22 -203
  7. akshare_one/modules/historical/base.py +64 -0
  8. akshare_one/modules/historical/eastmoney.py +242 -0
  9. akshare_one/modules/historical/factory.py +46 -0
  10. akshare_one/modules/historical/sina.py +219 -0
  11. akshare_one/modules/insider/base.py +78 -0
  12. akshare_one/modules/insider/factory.py +44 -0
  13. akshare_one/{adapters → modules/insider}/xueqiu.py +17 -76
  14. akshare_one/modules/news/base.py +51 -0
  15. akshare_one/modules/news/eastmoney.py +48 -0
  16. akshare_one/modules/news/factory.py +44 -0
  17. akshare_one/modules/realtime/base.py +68 -0
  18. akshare_one/modules/realtime/eastmoney.py +58 -0
  19. akshare_one/modules/realtime/factory.py +46 -0
  20. akshare_one/modules/realtime/xueqiu.py +61 -0
  21. akshare_one/modules/utils.py +10 -0
  22. akshare_one/news.py +3 -4
  23. akshare_one/stock.py +16 -30
  24. {akshare_one-0.1.3.dist-info → akshare_one-0.2.0.dist-info}/METADATA +2 -2
  25. akshare_one-0.2.0.dist-info/RECORD +29 -0
  26. {akshare_one-0.1.3.dist-info → akshare_one-0.2.0.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.0.dist-info}/licenses/LICENSE +0 -0
  32. {akshare_one-0.1.3.dist-info → akshare_one-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,353 +0,0 @@
1
- from typing import Optional
2
- import pandas as pd
3
- import akshare as ak
4
- from cachetools import cached
5
- from .cache.cache import CACHE_CONFIG
6
-
7
-
8
- class EastMoneyAdapter:
9
- """Adapter for EastMoney historical stock data API"""
10
-
11
- @cached(
12
- CACHE_CONFIG["hist_data_cache"],
13
- key=lambda self,
14
- symbol,
15
- interval,
16
- interval_multiplier,
17
- start_date,
18
- end_date,
19
- adjust: (
20
- "eastmoney",
21
- symbol,
22
- interval,
23
- interval_multiplier,
24
- start_date,
25
- end_date,
26
- adjust,
27
- ),
28
- )
29
- def get_hist_data(
30
- self,
31
- symbol: str,
32
- interval: str = "day",
33
- interval_multiplier: int = 1,
34
- start_date: str = "1970-01-01",
35
- end_date: str = "2030-12-31",
36
- adjust: str = "none",
37
- ) -> pd.DataFrame:
38
- """获取东方财富历史行情数据
39
-
40
- Args:
41
- symbol: Unified symbol format (e.g. '600000')
42
- interval: Time granularity ('minute','hour','day','week','month','year')
43
- interval_multiplier: Interval multiplier (e.g. 5 for 5 minutes)
44
- start_date: Start date in YYYY-MM-DD format (will be converted to YYYYMMDD)
45
- end_date: End date in YYYY-MM-DD format (will be converted to YYYYMMDD)
46
- adjust: Adjustment type ('none','qfq','hfq')
47
-
48
- Returns:
49
- Standardized DataFrame with OHLCV data
50
- """
51
- # Map standard interval to akshare supported periods
52
- interval = interval.lower()
53
- if interval == "minute":
54
- if interval_multiplier < 1:
55
- raise ValueError("Minute interval multiplier must be >= 1")
56
-
57
- start_date = (
58
- f"{start_date} 09:30:00" if " " not in start_date else start_date
59
- )
60
- end_date = f"{end_date} 15:00:00" if " " not in end_date else end_date
61
-
62
- raw_df = ak.stock_zh_a_hist_min_em(
63
- symbol=symbol,
64
- period="1",
65
- start_date=start_date,
66
- end_date=end_date,
67
- adjust=adjust if adjust != "none" else "",
68
- )
69
- # Resample the data to the desired minute interval
70
- raw_df["时间"] = pd.to_datetime(raw_df["时间"])
71
- raw_df = raw_df.set_index("时间")
72
- resampled = raw_df.resample(f"{interval_multiplier}min").agg(
73
- {
74
- "开盘": "first",
75
- "最高": "max",
76
- "最低": "min",
77
- "收盘": "last",
78
- "成交量": "sum",
79
- "成交额": "sum",
80
- }
81
- )
82
- raw_df = resampled.reset_index()
83
- return self._clean_minute_data(raw_df, str(interval_multiplier))
84
- elif interval == "hour":
85
- if interval_multiplier < 1:
86
- raise ValueError("Hour interval multiplier must be >= 1")
87
-
88
- start_date = (
89
- f"{start_date} 09:30:00" if " " not in start_date else start_date
90
- )
91
- end_date = f"{end_date} 15:00:00" if " " not in end_date else end_date
92
-
93
- raw_df = ak.stock_zh_a_hist_min_em(
94
- symbol=symbol,
95
- period="60",
96
- start_date=start_date,
97
- end_date=end_date,
98
- adjust=adjust if adjust != "none" else "",
99
- )
100
-
101
- # Resample the data to the desired hour interval
102
- raw_df["时间"] = pd.to_datetime(raw_df["时间"])
103
- raw_df = raw_df.set_index("时间")
104
- resampled = raw_df.resample(f"{interval_multiplier}h").agg(
105
- {
106
- "开盘": "first",
107
- "最高": "max",
108
- "最低": "min",
109
- "收盘": "last",
110
- "成交量": "sum",
111
- "成交额": "sum",
112
- }
113
- )
114
- raw_df = resampled.reset_index()
115
-
116
- return self._clean_minute_data(raw_df, f"{interval_multiplier}H")
117
- elif interval == "day":
118
- period = "daily"
119
- elif interval == "week":
120
- period = "weekly"
121
- elif interval == "month":
122
- period = "monthly"
123
- elif interval == "year":
124
- period = "monthly" # use monthly for yearly data
125
- interval_multiplier = 12 * interval_multiplier
126
- else:
127
- raise ValueError(f"Unsupported interval: {interval}")
128
-
129
- # Convert date format from YYYY-MM-DD to YYYYMMDD if needed
130
- start_date = start_date.replace("-", "") if "-" in start_date else start_date
131
- end_date = end_date.replace("-", "") if "-" in end_date else end_date
132
-
133
- # Fetch raw data from akshare
134
- raw_df = ak.stock_zh_a_hist(
135
- symbol=symbol,
136
- period=period, # daily/weekly/monthly
137
- start_date=start_date,
138
- end_date=end_date,
139
- adjust=adjust if adjust != "none" else "",
140
- )
141
-
142
- if interval_multiplier > 1:
143
- raw_df = self._resample_data(raw_df, interval, interval_multiplier)
144
-
145
- # Standardize the data format
146
- return self._clean_data(raw_df)
147
-
148
- @cached(
149
- CACHE_CONFIG["realtime_cache"],
150
- key=lambda self, symbol=None: f"eastmoney_{symbol if symbol else 'all'}",
151
- )
152
- def get_realtime_data(self, symbol: Optional[str] = None) -> pd.DataFrame:
153
- """获取沪深京A股实时行情数据"""
154
- raw_df = ak.stock_zh_a_spot_em()
155
- df = self._clean_spot_data(raw_df)
156
- if symbol:
157
- df = df[df["symbol"] == symbol].reset_index(drop=True)
158
- return df
159
-
160
- def _resample_data(
161
- self, df: pd.DataFrame, interval: str, multiplier: int
162
- ) -> pd.DataFrame:
163
- """Resample the data based on the given interval and multiplier"""
164
- if interval == "day":
165
- freq = f"{multiplier}D"
166
- elif interval == "week":
167
- freq = f"{multiplier}W-MON"
168
- elif interval == "month":
169
- freq = f"{multiplier}MS"
170
- elif interval == "year":
171
- freq = f"{multiplier}AS-JAN"
172
-
173
- df["日期"] = pd.to_datetime(df["日期"])
174
- df = df.set_index("日期")
175
- resampled = df.resample(freq).agg(
176
- {
177
- "开盘": "first",
178
- "最高": "max",
179
- "最低": "min",
180
- "收盘": "last",
181
- "成交量": "sum",
182
- }
183
- )
184
- return resampled.reset_index()
185
-
186
- def _clean_minute_data(self, raw_df: pd.DataFrame, period: str) -> pd.DataFrame:
187
- if period == "1":
188
- column_mapping = {
189
- "时间": "timestamp",
190
- "开盘": "open",
191
- "收盘": "close",
192
- "最高": "high",
193
- "最低": "low",
194
- "成交量": "volume",
195
- "成交额": "amount",
196
- "均价": "vwap",
197
- }
198
- else:
199
- column_mapping = {
200
- "时间": "timestamp",
201
- "开盘": "open",
202
- "收盘": "close",
203
- "最高": "high",
204
- "最低": "low",
205
- "涨跌幅": "pct_change",
206
- "涨跌额": "change",
207
- "成交量": "volume",
208
- "成交额": "amount",
209
- "振幅": "amplitude",
210
- "换手率": "turnover",
211
- }
212
-
213
- df = raw_df.rename(columns=column_mapping)
214
-
215
- if "timestamp" in df.columns:
216
- df["timestamp"] = (
217
- pd.to_datetime(df["timestamp"])
218
- .dt.tz_localize("Asia/Shanghai")
219
- .dt.tz_convert("UTC")
220
- )
221
- standard_columns = [
222
- "timestamp",
223
- "open",
224
- "high",
225
- "low",
226
- "close",
227
- "volume",
228
- ]
229
- return df[[col for col in standard_columns if col in df.columns]]
230
-
231
- def _clean_data(self, raw_df: pd.DataFrame, adjust: str = "none") -> pd.DataFrame:
232
- """清理和标准化历史数据格式
233
-
234
- Args:
235
- raw_df: Raw DataFrame from EastMoney API
236
- adjust: Adjustment type ('none','qfq','hfq')
237
-
238
- Returns:
239
- Standardized DataFrame with consistent columns
240
- """
241
- # Check if required columns exist in raw data
242
- required_columns = {
243
- "日期": "timestamp",
244
- "开盘": "open",
245
- "收盘": "close",
246
- "最高": "high",
247
- "最低": "low",
248
- "成交量": "volume",
249
- }
250
-
251
- # Find available columns in raw data
252
- available_columns = {}
253
- for src_col, target_col in required_columns.items():
254
- if src_col in raw_df.columns:
255
- available_columns[src_col] = target_col
256
-
257
- if not available_columns:
258
- raise ValueError("Raw data does not contain any expected columns")
259
-
260
- # Rename available columns
261
- df = raw_df.rename(columns=available_columns)
262
-
263
- # Process timestamp if available
264
- if "timestamp" in df.columns:
265
- df = df.assign(
266
- timestamp=lambda x: pd.to_datetime(x["timestamp"])
267
- .dt.tz_localize("Asia/Shanghai")
268
- .dt.tz_convert("UTC")
269
- )
270
-
271
- # Process volume if available
272
- if "volume" in df.columns:
273
- df = df.assign(volume=lambda x: x["volume"].astype("int64"))
274
-
275
- # Process adjustment flag
276
- if adjust != "none":
277
- df = df.assign(is_adjusted=lambda x: x["adjust"] != "none")
278
- else:
279
- df = df.assign(is_adjusted=False)
280
-
281
- # Select available standardized columns
282
- standard_columns = [
283
- "timestamp",
284
- "open",
285
- "high",
286
- "low",
287
- "close",
288
- "volume",
289
- ]
290
- return df[[col for col in standard_columns if col in df.columns]]
291
-
292
- @cached(CACHE_CONFIG["news_cache"], key=lambda self, symbol: f"eastmoney_{symbol}")
293
- def get_news_data(self, symbol: str) -> pd.DataFrame:
294
- """获取东方财富个股新闻数据"""
295
- raw_df = ak.stock_news_em(symbol=symbol)
296
-
297
- column_mapping = {
298
- "关键词": "keyword",
299
- "新闻标题": "title",
300
- "新闻内容": "content",
301
- "发布时间": "publish_time",
302
- "文章来源": "source",
303
- "新闻链接": "url",
304
- }
305
-
306
- df = raw_df.rename(columns=column_mapping)
307
-
308
- if "publish_time" in df.columns:
309
- df = df.assign(
310
- publish_time=lambda x: pd.to_datetime(x["publish_time"])
311
- .dt.tz_localize("Asia/Shanghai")
312
- .dt.tz_convert("UTC")
313
- )
314
-
315
- return df
316
-
317
- def _clean_spot_data(self, raw_df: pd.DataFrame) -> pd.DataFrame:
318
- """清理和标准化实时行情数据"""
319
-
320
- column_mapping = {
321
- "代码": "symbol",
322
- "最新价": "price",
323
- "涨跌额": "change",
324
- "涨跌幅": "pct_change",
325
- "成交量": "volume",
326
- "成交额": "amount",
327
- "今开": "open",
328
- "最高": "high",
329
- "最低": "low",
330
- "昨收": "prev_close",
331
- }
332
-
333
- df = raw_df.rename(columns=column_mapping)
334
-
335
- # Change time to UTC
336
- df = df.assign(
337
- timestamp=lambda x: pd.Timestamp.now(tz="Asia/Shanghai").tz_convert("UTC")
338
- )
339
-
340
- required_columns = [
341
- "symbol",
342
- "price",
343
- "change",
344
- "pct_change",
345
- "timestamp",
346
- "volume",
347
- "amount",
348
- "open",
349
- "high",
350
- "low",
351
- "prev_close",
352
- ]
353
- return df[required_columns]
@@ -1,15 +0,0 @@
1
- akshare_one/__init__.py,sha256=xWUyTnh1Tv-2mEPxDmk5giUPwhrsoAuZcyoW6mAbX4k,923
2
- akshare_one/financial.py,sha256=fCi6FPtPc28id7nQf8b3i2bh5vSCpZTmnPQ9kKr2n8Y,1215
3
- akshare_one/insider.py,sha256=Vz1yIvVlk_FVLhqFHdR-CCH5prfO12_CVzyAnrKgH4g,1199
4
- akshare_one/news.py,sha256=qUeX_1SnViDXBAb4Gcl28ow1qvMugEEPL75JgJhLbCA,707
5
- akshare_one/stock.py,sha256=w2gSjtbtcwyJ-qFpo0BJO7gxVrDur6Sun8en-A6LMb4,2727
6
- akshare_one/adapters/__init__.py,sha256=0AVeL7uukxjmkBn4xmq_IGKrZo3LfAWRWYKagSMgD5s,252
7
- akshare_one/adapters/eastmoney.py,sha256=nRs61bR_C12gOnqNBpo5-_Vqamt70-lMPr6vlxwPwuo,11780
8
- akshare_one/adapters/sina.py,sha256=wD67NLtqDwNzmPuxnqhrRFCyTkhSuiXXuP4a8t1BsBU,16789
9
- akshare_one/adapters/xueqiu.py,sha256=u_r3UtlykIZLa86flcqlc4xT8swFN_xLVkRbceNSciA,6073
10
- akshare_one/adapters/cache/cache.py,sha256=47A80Xtfr4gkKvEfBQrT8Dz8hNFR769rBa2gU6ew25s,373
11
- akshare_one-0.1.3.dist-info/licenses/LICENSE,sha256=Gg6A1GNSJCZWQ73aHJ7TXOa0i8RQ3FejZCTZ6Db07cU,1066
12
- akshare_one-0.1.3.dist-info/METADATA,sha256=jEtaMyAF4HllS5DZFdwcYIzT1C6eRkNOvBbfTqvTJX8,1865
13
- akshare_one-0.1.3.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
14
- akshare_one-0.1.3.dist-info/top_level.txt,sha256=kNiucyLVAGa89wmUSpXbBLWD7pF_RuahuiaOfLHZSyw,12
15
- akshare_one-0.1.3.dist-info/RECORD,,
File without changes