fetch_a50_v2.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. A50期货 - 30分钟K线数据获取(多方案尝试)
  5. 标的:富时中国A50指数期货 (CN0Y, XIN9)
  6. """
  7. import pandas as pd
  8. import numpy as np
  9. import akshare as ak
  10. import requests
  11. import json
  12. import re
  13. from datetime import datetime, timedelta
  14. import warnings
  15. warnings.filterwarnings('ignore')
  16. class A50FuturesFetcher:
  17. """A50期货数据获取器 - 多方案"""
  18. def __init__(self):
  19. print("="*70)
  20. print("A50期货30分钟K线数据获取工具")
  21. print("="*70)
  22. def fetch_data(self, days=40):
  23. """获取数据 - 尝试多种方案"""
  24. end_date = datetime.now()
  25. start_date = end_date - timedelta(days=days+5)
  26. print(f"\n目标: 获取近{days}天30分钟K线")
  27. print(f"时间范围: {start_date.strftime('%Y-%m-%d')} ~ {end_date.strftime('%Y-%m-%d')}")
  28. # 方案1: AKShare期货分钟数据
  29. print("\n" + "-"*70)
  30. print("[方案1] AKShare期货分钟数据...")
  31. df = self._try_akshare_futures(start_date, end_date)
  32. if df is not None: return df
  33. # 方案2: AKShare股指期货
  34. print("\n" + "-"*70)
  35. print("[方案2] AKShare股指期货(CN0Y)...")
  36. df = self._try_akshare_cn0y(start_date, end_date)
  37. if df is not None: return df
  38. # 方案3: 新浪财经期货
  39. print("\n" + "-"*70)
  40. print("[方案3] 新浪财经期货接口...")
  41. df = self._try_sina_futures(days)
  42. if df is not None: return df
  43. # 方案4: 东方财富期货
  44. print("\n" + "-"*70)
  45. print("[方案4] 东方财富期货接口...")
  46. df = self._try_eastmoney_futures()
  47. if df is not None: return df
  48. # 方案5: 使用相关ETF作为替代
  49. print("\n" + "-"*70)
  50. print("[方案5] A50相关ETF数据...")
  51. df = self._try_a50_etf(start_date, end_date)
  52. if df is not None: return df
  53. # 方案6: 上证50指数作为替代
  54. print("\n" + "-"*70)
  55. print("[方案6] 上证50指数(000016)作为替代...")
  56. df = self._try_sz50_index(start_date, end_date)
  57. if df is not None: return df
  58. print("\n❌ 所有方案均失败")
  59. return None
  60. def _try_akshare_futures(self, start_date, end_date):
  61. """方案1: AKShare期货分钟数据"""
  62. try:
  63. # 尝试获取富时A50期货分钟数据
  64. print(" 尝试: futures_zh_minute_sina...")
  65. # 富时中国A50期货代码
  66. symbol = "CN0Y"
  67. df = ak.futures_zh_minute_sina(symbol=symbol, period="30")
  68. if df is not None and len(df) > 0:
  69. df['datetime'] = pd.to_datetime(df['datetime'])
  70. df = df.set_index('datetime').sort_index()
  71. df = df[(df.index >= start_date) & (df.index <= end_date)]
  72. if len(df) > 10:
  73. print(f" ✅ 成功: {len(df)}条")
  74. return df
  75. except Exception as e:
  76. print(f" 失败: {str(e)[:80]}")
  77. return None
  78. def _try_akshare_cn0y(self, start_date, end_date):
  79. """方案2: AKShare股指期货数据"""
  80. try:
  81. print(" 尝试: futures_zh_realtime...")
  82. # 获取股指期货实时数据(包含历史)
  83. df = ak.futures_zh_realtime(symbol="富时A50")
  84. if df is not None and len(df) > 0:
  85. print(f" 获取到实时数据: {len(df)}条")
  86. # 注意:这是实时报价,不是K线
  87. return None
  88. except Exception as e:
  89. print(f" 失败: {str(e)[:80]}")
  90. return None
  91. def _try_sina_futures(self, days):
  92. """方案3: 新浪财经期货K线"""
  93. try:
  94. print(" 尝试: 新浪财经期货K线接口...")
  95. # 新浪财经A50连续合约
  96. url = "https://stock.finance.sina.com.cn/futures/api/jsonp.php/var_CN0Y=/InnerFuturesNewService.getMinLine?symbol=CN0Y"
  97. headers = {
  98. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
  99. 'Referer': 'https://finance.sina.com.cn/'
  100. }
  101. response = requests.get(url, headers=headers, timeout=10)
  102. # 解析JSONP
  103. text = response.text
  104. json_start = text.find('[')
  105. json_end = text.rfind(']') + 1
  106. if json_start > 0 and json_end > json_start:
  107. json_str = text[json_start:json_end]
  108. data = json.loads(json_str)
  109. if data and len(data) > 0:
  110. df_list = []
  111. for item in data:
  112. df_list.append({
  113. 'datetime': item.get('time'),
  114. 'open': float(item.get('open', 0)),
  115. 'high': float(item.get('high', 0)),
  116. 'low': float(item.get('low', 0)),
  117. 'close': float(item.get('close', 0)),
  118. 'volume': float(item.get('volume', 0))
  119. })
  120. df = pd.DataFrame(df_list)
  121. df['datetime'] = pd.to_datetime(df['datetime'])
  122. df = df.set_index('datetime').sort_index()
  123. # 只保留近N天
  124. start = datetime.now() - timedelta(days=days)
  125. df = df[df.index >= start]
  126. if len(df) > 10:
  127. print(f" ✅ 成功: {len(df)}条")
  128. return df
  129. except Exception as e:
  130. print(f" 失败: {str(e)[:80]}")
  131. return None
  132. def _try_eastmoney_futures(self):
  133. """方案4: 东方财富期货数据"""
  134. try:
  135. print(" 尝试: 东方财富期货接口...")
  136. # 东方财富A50期货
  137. # 使用股指期货分钟数据接口
  138. df = ak.futures_zh_minute_sina(symbol="XIN9", period="30")
  139. if df is not None and len(df) > 0:
  140. df['datetime'] = pd.to_datetime(df['datetime'])
  141. df = df.set_index('datetime').sort_index()
  142. if len(df) > 10:
  143. print(f" ✅ 成功: {len(df)}条")
  144. return df
  145. except Exception as e:
  146. print(f" 失败: {str(e)[:80]}")
  147. return None
  148. def _try_a50_etf(self, start_date, end_date):
  149. """方案5: A50 ETF数据"""
  150. try:
  151. print(" 尝试: 华夏A50ETF(159601)...")
  152. df = ak.fund_etf_hist_min_em(symbol="159601", period="30",
  153. start_date=start_date.strftime('%Y%m%d%H%M%S'),
  154. end_date=end_date.strftime('%Y%m%d%H%M%S'))
  155. if df is not None and len(df) > 10:
  156. df = df.rename(columns={
  157. '时间': 'datetime', '开盘': 'open', '收盘': 'close',
  158. '最高': 'high', '最低': 'low', '成交量': 'volume'
  159. })
  160. df['datetime'] = pd.to_datetime(df['datetime'])
  161. df = df.set_index('datetime').sort_index()
  162. print(f" ✅ 成功: {len(df)}条 (ETF替代数据)")
  163. return df
  164. except Exception as e:
  165. print(f" 失败: {str(e)[:80]}")
  166. return None
  167. def _try_sz50_index(self, start_date, end_date):
  168. """方案6: 上证50指数"""
  169. try:
  170. print(" 尝试: 上证50指数(000016)...")
  171. df = ak.index_zh_a_hist_min_em(symbol="000016", period="30",
  172. start_date=start_date.strftime('%Y%m%d%H%M%S'),
  173. end_date=end_date.strftime('%Y%m%d%H%M%S'))
  174. if df is not None and len(df) > 10:
  175. df = df.rename(columns={
  176. '时间': 'datetime', '开盘': 'open', '收盘': 'close',
  177. '最高': 'high', '最低': 'low', '成交量': 'volume'
  178. })
  179. df['datetime'] = pd.to_datetime(df['datetime'])
  180. df = df.set_index('datetime').sort_index()
  181. print(f" ✅ 成功: {len(df)}条 (上证50替代数据)")
  182. return df
  183. except Exception as e:
  184. print(f" 失败: {str(e)[:80]}")
  185. return None
  186. def save_and_show(self, df, filename=None):
  187. """保存并显示数据"""
  188. if df is None or len(df) == 0:
  189. print("\n❌ 无数据")
  190. return
  191. # 保存
  192. if filename is None:
  193. filename = f"A50_30min_{datetime.now().strftime('%Y%m%d_%H%M')}.csv"
  194. df.to_csv(filename)
  195. print(f"\n💾 已保存: {filename}")
  196. # 统计
  197. print(f"\n📊 数据统计:")
  198. print(f" 数据条数: {len(df)}")
  199. print(f" 时间区间: {df.index[0]} ~ {df.index[-1]}")
  200. print(f" 价格区间: {df['low'].min():.2f} ~ {df['high'].max():.2f}")
  201. print(f" 最新价格: {df['close'].iloc[-1]:.2f}")
  202. # 显示最新10条
  203. print(f"\n📋 最新10条数据:")
  204. print(df.tail(10)[['open', 'high', 'low', 'close', 'volume']].to_string())
  205. def main():
  206. fetcher = A50FuturesFetcher()
  207. df = fetcher.fetch_data(days=40)
  208. if df is not None:
  209. fetcher.save_and_show(df)
  210. print("\n✅ 完成!")
  211. else:
  212. print("\n❌ 获取失败,请检查网络连接")
  213. print("="*70)
  214. if __name__ == "__main__":
  215. main()