Initial commit with translated description
This commit is contained in:
524
scripts/market.py
Normal file
524
scripts/market.py
Normal file
@@ -0,0 +1,524 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tushare 金融数据接口
|
||||
支持:股票行情、期货数据、基本面数据、宏观经济
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, Dict, List
|
||||
|
||||
# 尝试导入 tushare
|
||||
try:
|
||||
import tushare as ts
|
||||
TUSHARE_AVAILABLE = True
|
||||
except ImportError:
|
||||
TUSHARE_AVAILABLE = False
|
||||
print("⚠️ tushare 库未安装,请先运行: pip3 install tushare --user")
|
||||
|
||||
def get_pro_api():
|
||||
"""获取 Tushare Pro API 实例"""
|
||||
if not TUSHARE_AVAILABLE:
|
||||
return None
|
||||
|
||||
token = os.getenv('TUSHARE_TOKEN')
|
||||
if not token:
|
||||
print("❌ 错误: 请设置 TUSHARE_TOKEN 环境变量")
|
||||
print("获取方式: https://tushare.pro/register")
|
||||
return None
|
||||
|
||||
ts.set_token(token)
|
||||
return ts.pro_api()
|
||||
|
||||
# ==================== 股票数据 ====================
|
||||
|
||||
def get_stock_basic(exchange: str = '', list_status: str = 'L') -> List[Dict]:
|
||||
"""获取股票基础信息"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.stock_basic(exchange=exchange, list_status=list_status)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取股票基础信息失败: {e}")
|
||||
return []
|
||||
|
||||
def get_daily(ts_code: str = '', trade_date: str = '', start_date: str = '', end_date: str = '') -> List[Dict]:
|
||||
"""获取日线行情"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.daily(ts_code=ts_code, trade_date=trade_date,
|
||||
start_date=start_date, end_date=end_date)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取日线行情失败: {e}")
|
||||
return []
|
||||
|
||||
def get_weekly(ts_code: str = '', trade_date: str = '', start_date: str = '', end_date: str = '') -> List[Dict]:
|
||||
"""获取周线行情"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.weekly(ts_code=ts_code, trade_date=trade_date,
|
||||
start_date=start_date, end_date=end_date)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取周线行情失败: {e}")
|
||||
return []
|
||||
|
||||
def get_monthly(ts_code: str = '', trade_date: str = '', start_date: str = '', end_date: str = '') -> List[Dict]:
|
||||
"""获取月线行情"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.monthly(ts_code=ts_code, trade_date=trade_date,
|
||||
start_date=start_date, end_date=end_date)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取月线行情失败: {e}")
|
||||
return []
|
||||
|
||||
def get_realtime_quote(ts_code: str) -> Optional[Dict]:
|
||||
"""获取实时行情(使用旧版接口)"""
|
||||
if not TUSHARE_AVAILABLE:
|
||||
return None
|
||||
|
||||
try:
|
||||
df = ts.get_realtime_quotes(ts_code)
|
||||
if df is not None and not df.empty:
|
||||
return df.iloc[0].to_dict()
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"❌ 获取实时行情失败: {e}")
|
||||
return None
|
||||
|
||||
def get_stock_company(ts_code: str = '') -> List[Dict]:
|
||||
"""获取上市公司基本信息"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.stock_company(ts_code=ts_code)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取公司信息失败: {e}")
|
||||
return []
|
||||
|
||||
def get_top10_holders(ts_code: str, start_date: str = '', end_date: str = '') -> List[Dict]:
|
||||
"""获取前十大股东"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.top10_holders(ts_code=ts_code, start_date=start_date, end_date=end_date)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取股东信息失败: {e}")
|
||||
return []
|
||||
|
||||
def get_moneyflow(ts_code: str = '', trade_date: str = '') -> List[Dict]:
|
||||
"""获取个股资金流向"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.moneyflow(ts_code=ts_code, trade_date=trade_date)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取资金流向失败: {e}")
|
||||
return []
|
||||
|
||||
# ==================== 期货数据 ====================
|
||||
|
||||
def get_fut_basic(exchange: str = '', fut_type: str = '2') -> List[Dict]:
|
||||
"""获取期货合约基础信息"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.fut_basic(exchange=exchange, fut_type=fut_type)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取期货基础信息失败: {e}")
|
||||
return []
|
||||
|
||||
def get_fut_daily(ts_code: str = '', trade_date: str = '', start_date: str = '', end_date: str = '') -> List[Dict]:
|
||||
"""获取期货日线行情"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.fut_daily(ts_code=ts_code, trade_date=trade_date,
|
||||
start_date=start_date, end_date=end_date)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取期货日线失败: {e}")
|
||||
return []
|
||||
|
||||
def get_fut_holding(trade_date: str = '', symbol: str = '') -> List[Dict]:
|
||||
"""获取每日持仓排名"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.fut_holding(trade_date=trade_date, symbol=symbol)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取持仓排名失败: {e}")
|
||||
return []
|
||||
|
||||
def get_fut_wsr(trade_date: str = '') -> List[Dict]:
|
||||
"""获取仓单日报"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.fut_wsr(trade_date=trade_date)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取仓单数据失败: {e}")
|
||||
return []
|
||||
|
||||
def get_fut_settle(trade_date: str = '', exchange: str = '') -> List[Dict]:
|
||||
"""获取期货结算参数"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.fut_settle(trade_date=trade_date, exchange=exchange)
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取结算参数失败: {e}")
|
||||
return []
|
||||
|
||||
# ==================== 宏观经济 ====================
|
||||
|
||||
def get_gdp() -> List[Dict]:
|
||||
"""获取GDP数据"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.cn_gdp()
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取GDP数据失败: {e}")
|
||||
return []
|
||||
|
||||
def get_cpi() -> List[Dict]:
|
||||
"""获取CPI数据"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.cn_cpi()
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取CPI数据失败: {e}")
|
||||
return []
|
||||
|
||||
def get_ppi() -> List[Dict]:
|
||||
"""获取PPI数据"""
|
||||
pro = get_pro_api()
|
||||
if not pro:
|
||||
return []
|
||||
|
||||
try:
|
||||
df = pro.cn_ppi()
|
||||
return df.to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"❌ 获取PPI数据失败: {e}")
|
||||
return []
|
||||
|
||||
# ==================== 输出格式化 ====================
|
||||
|
||||
def print_stock_basic(data: List[Dict]):
|
||||
"""打印股票基础信息"""
|
||||
if not data:
|
||||
print("📭 没有数据")
|
||||
return
|
||||
|
||||
print(f"\n📈 股票列表 ({len(data)} 只):\n")
|
||||
print(f"{'代码':<12} {'名称':<15} {'行业':<15} {'上市日期':<12}")
|
||||
print("-" * 60)
|
||||
|
||||
for item in data[:50]: # 限制显示数量
|
||||
ts_code = item.get('ts_code', 'N/A')
|
||||
name = item.get('name', 'Unknown')[:14]
|
||||
industry = item.get('industry', '-')[:14]
|
||||
list_date = item.get('list_date', '-')
|
||||
print(f"{ts_code:<12} {name:<15} {industry:<15} {list_date:<12}")
|
||||
|
||||
def print_daily(data: List[Dict]):
|
||||
"""打印日线行情"""
|
||||
if not data:
|
||||
print("📭 没有数据")
|
||||
return
|
||||
|
||||
print(f"\n📊 日线行情 ({len(data)} 条):\n")
|
||||
print(f"{'日期':<12} {'开盘':<10} {'收盘':<10} {'最高':<10} {'最低':<10} {'成交量':<12}")
|
||||
print("-" * 70)
|
||||
|
||||
for item in data[:20]:
|
||||
trade_date = item.get('trade_date', '-')
|
||||
open_price = item.get('open', 0)
|
||||
close = item.get('close', 0)
|
||||
high = item.get('high', 0)
|
||||
low = item.get('low', 0)
|
||||
vol = item.get('vol', 0)
|
||||
|
||||
change = item.get('change', 0)
|
||||
pct_chg = item.get('pct_chg', 0)
|
||||
|
||||
emoji = "🟢" if pct_chg > 0 else "🔴" if pct_chg < 0 else "⚪"
|
||||
|
||||
print(f"{trade_date:<12} {open_price:<10.2f} {close:<10.2f} {high:<10.2f} {low:<10.2f} {vol:<12}")
|
||||
print(f" {emoji} 涨跌: {change:+.2f} ({pct_chg:+.2f}%)")
|
||||
print()
|
||||
|
||||
def print_fut_basic(data: List[Dict]):
|
||||
"""打印期货基础信息"""
|
||||
if not data:
|
||||
print("📭 没有数据")
|
||||
return
|
||||
|
||||
print(f"\n📦 期货合约 ({len(data)} 个):\n")
|
||||
print(f"{'代码':<15} {'名称':<20} {'交易所':<10} {'合约类型':<10}")
|
||||
print("-" * 60)
|
||||
|
||||
for item in data[:50]:
|
||||
ts_code = item.get('ts_code', 'N/A')
|
||||
name = item.get('name', 'Unknown')[:18]
|
||||
exchange = item.get('exchange', '-')
|
||||
fut_type = item.get('fut_type', '-')
|
||||
print(f"{ts_code:<15} {name:<20} {exchange:<10} {fut_type:<10}")
|
||||
|
||||
def print_fut_daily(data: List[Dict]):
|
||||
"""打印期货日线"""
|
||||
if not data:
|
||||
print("📭 没有数据")
|
||||
return
|
||||
|
||||
print(f"\n📊 期货日线 ({len(data)} 条):\n")
|
||||
print(f"{'日期':<12} {'开盘':<10} {'收盘':<10} {'最高':<10} {'最低':<10} {'持仓':<10}")
|
||||
print("-" * 70)
|
||||
|
||||
for item in data[:20]:
|
||||
trade_date = item.get('trade_date', '-')
|
||||
open_price = item.get('open', 0)
|
||||
close = item.get('close', 0)
|
||||
high = item.get('high', 0)
|
||||
low = item.get('low', 0)
|
||||
oi = item.get('oi', 0) # 持仓量
|
||||
|
||||
change = close - open_price
|
||||
|
||||
emoji = "🟢" if change > 0 else "🔴" if change < 0 else "⚪"
|
||||
|
||||
print(f"{trade_date:<12} {open_price:<10.2f} {close:<10.2f} {high:<10.2f} {low:<10.2f} {oi:<10.0f}")
|
||||
|
||||
def print_moneyflow(data: List[Dict]):
|
||||
"""打印资金流向"""
|
||||
if not data:
|
||||
print("📭 没有数据")
|
||||
return
|
||||
|
||||
print(f"\n💰 资金流向 ({len(data)} 条):\n")
|
||||
print(f"{'代码':<12} {'日期':<12} {'净流入':<12} {'主力净流入':<12}")
|
||||
print("-" * 55)
|
||||
|
||||
for item in data[:20]:
|
||||
ts_code = item.get('ts_code', '-')
|
||||
trade_date = item.get('trade_date', '-')
|
||||
net_mf = item.get('net_mf', 0) / 10000 # 万元
|
||||
net_mf_amount = item.get('net_mf_amount', 0) / 10000
|
||||
|
||||
emoji = "🟢" if net_mf > 0 else "🔴"
|
||||
|
||||
print(f"{ts_code:<12} {trade_date:<12} {emoji} {net_mf:<10.1f}万 {net_mf_amount:<10.1f}万")
|
||||
|
||||
# ==================== 主程序 ====================
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Tushare 金融数据接口')
|
||||
subparsers = parser.add_subparsers(dest='command', help='可用命令')
|
||||
|
||||
# 股票基础信息
|
||||
stock_basic = subparsers.add_parser('stock_basic', help='获取股票基础信息')
|
||||
stock_basic.add_argument('--exchange', default='', help='交易所 (SSE/SZSE)')
|
||||
|
||||
# 日线行情
|
||||
daily = subparsers.add_parser('daily', help='获取日线行情')
|
||||
daily.add_argument('--ts_code', required=True, help='股票代码 (如: 000001.SZ)')
|
||||
daily.add_argument('--start_date', help='开始日期 (YYYYMMDD)')
|
||||
daily.add_argument('--end_date', help='结束日期 (YYYYMMDD)')
|
||||
daily.add_argument('--trade_date', help='交易日期 (YYYYMMDD)')
|
||||
|
||||
# 周线行情
|
||||
weekly = subparsers.add_parser('weekly', help='获取周线行情')
|
||||
weekly.add_argument('--ts_code', required=True, help='股票代码 (如: 000001.SZ)')
|
||||
weekly.add_argument('--start_date', help='开始日期 (YYYYMMDD)')
|
||||
weekly.add_argument('--end_date', help='结束日期 (YYYYMMDD)')
|
||||
weekly.add_argument('--trade_date', help='交易日期 (YYYYMMDD)')
|
||||
|
||||
# 月线行情
|
||||
monthly = subparsers.add_parser('monthly', help='获取月线行情')
|
||||
monthly.add_argument('--ts_code', required=True, help='股票代码 (如: 000001.SZ)')
|
||||
monthly.add_argument('--start_date', help='开始日期 (YYYYMMDD)')
|
||||
monthly.add_argument('--end_date', help='结束日期 (YYYYMMDD)')
|
||||
monthly.add_argument('--trade_date', help='交易日期 (YYYYMMDD)')
|
||||
|
||||
# 实时行情
|
||||
realtime = subparsers.add_parser('realtime', help='获取实时行情')
|
||||
realtime.add_argument('ts_code', help='股票代码')
|
||||
|
||||
# 公司信息
|
||||
subparsers.add_parser('company', help='获取上市公司信息')
|
||||
|
||||
# 资金流向
|
||||
moneyflow = subparsers.add_parser('moneyflow', help='获取资金流向')
|
||||
moneyflow.add_argument('--ts_code', help='股票代码')
|
||||
moneyflow.add_argument('--trade_date', help='交易日期')
|
||||
|
||||
# 期货基础
|
||||
fut_basic = subparsers.add_parser('fut_basic', help='获取期货基础信息')
|
||||
fut_basic.add_argument('--exchange', default='', help='交易所')
|
||||
|
||||
# 期货日线
|
||||
fut_daily = subparsers.add_parser('fut_daily', help='获取期货日线')
|
||||
fut_daily.add_argument('--ts_code', required=True, help='期货代码 (如: CU.SHF)')
|
||||
fut_daily.add_argument('--start_date', help='开始日期')
|
||||
fut_daily.add_argument('--end_date', help='结束日期')
|
||||
|
||||
# 期货持仓
|
||||
fut_holding = subparsers.add_parser('fut_holding', help='获取期货持仓排名')
|
||||
fut_holding.add_argument('--trade_date', help='交易日期')
|
||||
fut_holding.add_argument('--symbol', help='合约代码')
|
||||
|
||||
# 宏观经济
|
||||
subparsers.add_parser('gdp', help='获取GDP数据')
|
||||
subparsers.add_parser('cpi', help='获取CPI数据')
|
||||
subparsers.add_parser('ppi', help='获取PPI数据')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.command:
|
||||
parser.print_help()
|
||||
return
|
||||
|
||||
if not TUSHARE_AVAILABLE:
|
||||
print("\n❌ 请先安装 tushare:")
|
||||
print(" pip3 install tushare --user")
|
||||
return
|
||||
|
||||
# 执行命令
|
||||
if args.command == 'stock_basic':
|
||||
data = get_stock_basic(exchange=args.exchange)
|
||||
print_stock_basic(data)
|
||||
|
||||
elif args.command == 'daily':
|
||||
data = get_daily(ts_code=args.ts_code, trade_date=args.trade_date,
|
||||
start_date=args.start_date, end_date=args.end_date)
|
||||
print_daily(data)
|
||||
|
||||
elif args.command == 'weekly':
|
||||
data = get_weekly(ts_code=args.ts_code, trade_date=args.trade_date,
|
||||
start_date=args.start_date, end_date=args.end_date)
|
||||
print(f"\n📊 周线行情 ({len(data)} 条):\n")
|
||||
print(f"{'日期':<12} {'开盘':<10} {'收盘':<10} {'最高':<10} {'最低':<10} {'成交量':<12}")
|
||||
print("-" * 70)
|
||||
for item in data[:20]:
|
||||
trade_date = item.get('trade_date', '-')
|
||||
open_price = item.get('open', 0)
|
||||
close = item.get('close', 0)
|
||||
high = item.get('high', 0)
|
||||
low = item.get('low', 0)
|
||||
vol = item.get('vol', 0)
|
||||
print(f"{trade_date:<12} {open_price:<10.2f} {close:<10.2f} {high:<10.2f} {low:<10.2f} {vol:<12}")
|
||||
|
||||
elif args.command == 'monthly':
|
||||
data = get_monthly(ts_code=args.ts_code, trade_date=args.trade_date,
|
||||
start_date=args.start_date, end_date=args.end_date)
|
||||
print(f"\n📊 月线行情 ({len(data)} 条):\n")
|
||||
print(f"{'日期':<12} {'开盘':<10} {'收盘':<10} {'最高':<10} {'最低':<10} {'成交量':<12}")
|
||||
print("-" * 70)
|
||||
for item in data[:20]:
|
||||
trade_date = item.get('trade_date', '-')
|
||||
open_price = item.get('open', 0)
|
||||
close = item.get('close', 0)
|
||||
high = item.get('high', 0)
|
||||
low = item.get('low', 0)
|
||||
vol = item.get('vol', 0)
|
||||
print(f"{trade_date:<12} {open_price:<10.2f} {close:<10.2f} {high:<10.2f} {low:<10.2f} {vol:<12}")
|
||||
|
||||
elif args.command == 'realtime':
|
||||
data = get_realtime_quote(args.ts_code)
|
||||
if data:
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
|
||||
elif args.command == 'company':
|
||||
data = get_stock_company()
|
||||
print(f"\n🏢 上市公司信息 ({len(data)} 家):\n")
|
||||
for item in data[:20]:
|
||||
print(f"{item.get('ts_code')}: {item.get('chairman', '-')} - {item.get('main_business', '-')[:30]}...")
|
||||
|
||||
elif args.command == 'moneyflow':
|
||||
data = get_moneyflow(ts_code=args.ts_code, trade_date=args.trade_date)
|
||||
print_moneyflow(data)
|
||||
|
||||
elif args.command == 'fut_basic':
|
||||
data = get_fut_basic(exchange=args.exchange)
|
||||
print_fut_basic(data)
|
||||
|
||||
elif args.command == 'fut_daily':
|
||||
data = get_fut_daily(ts_code=args.ts_code, start_date=args.start_date, end_date=args.end_date)
|
||||
print_fut_daily(data)
|
||||
|
||||
elif args.command == 'fut_holding':
|
||||
data = get_fut_holding(trade_date=args.trade_date, symbol=args.symbol)
|
||||
print(f"\n📊 期货持仓排名 ({len(data)} 条):\n")
|
||||
for item in data[:20]:
|
||||
print(f"{item.get('trade_date')} {item.get('symbol')} {item.get('broker')}: {item.get('vol')} 手")
|
||||
|
||||
elif args.command == 'gdp':
|
||||
data = get_gdp()
|
||||
print(f"\n📈 GDP数据 ({len(data)} 条):\n")
|
||||
for item in data[:10]:
|
||||
print(f"{item.get('year')}年{item.get('quarter')}季度: GDP {item.get('gdp')}亿元, 增速 {item.get('gdp_yoy')}%")
|
||||
|
||||
elif args.command == 'cpi':
|
||||
data = get_cpi()
|
||||
print(f"\n📊 CPI数据 ({len(data)} 条):\n")
|
||||
for item in data[:10]:
|
||||
print(f"{item.get('month')}: 全国 {item.get('nt_val')}%, 城市 {item.get('town_val')}%, 农村 {item.get('cnt_val')}%")
|
||||
|
||||
elif args.command == 'ppi':
|
||||
data = get_ppi()
|
||||
print(f"\n🏭 PPI数据 ({len(data)} 条):\n")
|
||||
for item in data[:10]:
|
||||
print(f"{item.get('month')}: PPI {item.get('ppi')}%, 环比 {item.get('ppi_mp')}%")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user