#!/usr/bin/env python3 """ 从币安拉取最近订单/成交记录,供策略分析或与 DB 对照。 当 DB 记录缺失时,可用此脚本直接查币安数据。 用法: python scripts/fetch_binance_orders.py --account 2 python scripts/fetch_binance_orders.py --account 2 --symbol BTCUSDT --days 7 python scripts/fetch_binance_orders.py --account 2 --type orders -o binance_orders.json python scripts/fetch_binance_orders.py --account 2 --symbols ASTERUSDT,FILUSDT,PENGUUSDT """ import argparse import asyncio import json import os import sys from pathlib import Path from datetime import datetime, timezone, timedelta proj = Path(__file__).resolve().parent.parent if (proj / "backend").exists(): sys.path.insert(0, str(proj / "backend")) sys.path.insert(0, str(proj)) BEIJING_TZ = timezone(timedelta(hours=8)) def _serialize(obj): if hasattr(obj, "isoformat"): return obj.isoformat() if isinstance(obj, datetime): return obj.isoformat() return obj async def main(): parser = argparse.ArgumentParser(description="从币安拉取订单/成交记录") parser.add_argument("--account", "-a", type=int, default=None, help="账号 ID,默认 ATS_ACCOUNT_ID 或 1") parser.add_argument("--symbol", "-s", type=str, default=None, help="交易对,如 BTCUSDT(单 symbol)") parser.add_argument("--symbols", type=str, default=None, help="多个交易对,逗号分隔,如 ASTERUSDT,FILUSDT") parser.add_argument("--days", "-d", type=int, default=7, help="拉取最近 N 天,默认 7(币安单次最多 7 天)") parser.add_argument("--type", "-t", choices=["orders", "trades"], default="trades", help="orders=订单列表, trades=成交记录(策略分析推荐 trades)") parser.add_argument("-o", "--output", type=str, help="导出到 JSON 文件") args = parser.parse_args() account_id = args.account or int(os.getenv("ATS_ACCOUNT_ID") or os.getenv("ACCOUNT_ID") or "1") symbols = [] if args.symbol: symbols = [s.strip().upper() for s in args.symbol.split(",") if s.strip()] elif args.symbols: symbols = [s.strip().upper() for s in args.symbols.split(",") if s.strip()] if not symbols: print("请指定 --symbol 或 --symbols(逗号分隔)") print(" 示例: --symbol BTCUSDT 或 --symbols ASTERUSDT,FILUSDT,PENGUUSDT") sys.exit(1) try: from database.models import Account from trading_system.binance_client import BinanceClient except ImportError as e: print(f"导入失败: {e}") print("请确保在项目根目录运行,且 backend 可访问") sys.exit(1) api_key, api_secret, use_testnet, _ = Account.get_credentials(account_id) if not api_key or not api_secret: print(f"账号 {account_id} 未配置 API 密钥") sys.exit(1) client = BinanceClient(api_key=api_key, api_secret=api_secret, testnet=use_testnet) try: await client.connect() except Exception as e: print(f"连接币安失败: {e}") sys.exit(1) end_ms = int(datetime.now(BEIJING_TZ).timestamp() * 1000) start_ms = end_ms - args.days * 24 * 3600 * 1000 all_data = [] for sym in symbols: try: if args.type == "trades": rows = await client.client.futures_account_trades( symbol=sym, startTime=start_ms, endTime=end_ms, limit=1000, recvWindow=20000, ) for r in rows: r["_symbol"] = sym all_data.extend(rows) print(f" {sym}: 成交 {len(rows)} 条") else: rows = await client.client.futures_get_all_orders( symbol=sym, startTime=start_ms, endTime=end_ms, limit=1000, recvWindow=20000, ) for r in rows: r["_symbol"] = sym all_data.extend(rows) print(f" {sym}: 订单 {len(rows)} 条") except Exception as e: print(f" {sym}: 失败 {e}") await asyncio.sleep(0.2) # 按时间排序 if all_data: time_key = "time" if "time" in all_data[0] else "updateTime" all_data.sort(key=lambda x: x.get(time_key, 0), reverse=True) # 转换大数/日期 out = [] for r in all_data: row = dict(r) for k, v in list(row.items()): if isinstance(v, (datetime,)): row[k] = v.isoformat() out.append(row) print(f"\n账号 {account_id} | 类型 {args.type} | 共 {len(out)} 条") if args.output: with open(args.output, "w", encoding="utf-8") as f: json.dump(out, f, ensure_ascii=False, indent=2, default=_serialize) print(f"已导出到 {args.output}") else: print(json.dumps(out[:50], ensure_ascii=False, indent=2, default=_serialize)) if len(out) > 50: print(f"... 仅显示前 50 条,共 {len(out)} 条。可用 -o 导出全部") if client.client: await client.client.close_connection() if __name__ == "__main__": asyncio.run(main())