Files
2026-07-13 12:36:27 +08:00

74 lines
2.8 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
from datetime import datetime
from pathlib import Path
import pandas as pd
from futu import OpenQuoteContext, RET_OK, UserSecurityGroupType
def fetch_full_watchlist(host: str, port: int) -> pd.DataFrame:
quote_ctx = OpenQuoteContext(host=host, port=port)
try:
ret, groups = quote_ctx.get_user_security_group(group_type=UserSecurityGroupType.ALL)
if ret != RET_OK:
raise RuntimeError(f"get_user_security_group failed: {groups}")
all_frames: list[pd.DataFrame] = []
for _, g in groups.iterrows():
group_name = str(g.get("group_name", "")).strip()
if not group_name:
continue
ret2, sec = quote_ctx.get_user_security(group_name)
if ret2 != RET_OK:
print(f"[WARN] get_user_security failed for group={group_name}: {sec}")
continue
if sec is None or sec.empty:
continue
sec = sec.copy()
sec["group_name"] = group_name
all_frames.append(sec)
if not all_frames:
return pd.DataFrame(columns=["group_name", "code", "name"])
out = pd.concat(all_frames, ignore_index=True)
out = out.drop_duplicates(subset=["group_name", "code"], keep="first")
first_cols = ["group_name", "code", "name"]
ordered_cols = [c for c in first_cols if c in out.columns] + [c for c in out.columns if c not in first_cols]
return out[ordered_cols]
finally:
quote_ctx.close()
def main() -> None:
parser = argparse.ArgumentParser(description="Export all Futu watchlist groups to CSV/JSON.")
parser.add_argument("--host", default="127.0.0.1", help="OpenD host (default: 127.0.0.1)")
parser.add_argument("--port", type=int, default=11111, help="OpenD port (default: 11111)")
parser.add_argument("--csv", default="", help="Output CSV path")
parser.add_argument("--json", default="", help="Output JSON path")
args = parser.parse_args()
output_dir = Path(__file__).resolve().parent
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
csv_path = Path(args.csv) if args.csv else output_dir / f"futu_watchlist_full_{ts}.csv"
json_path = Path(args.json) if args.json else output_dir / f"futu_watchlist_full_{ts}.json"
df = fetch_full_watchlist(host=args.host, port=args.port)
df.to_csv(csv_path, index=False, encoding="utf-8-sig")
records = json.loads(df.to_json(orient="records", force_ascii=False))
json_path.write_text(json.dumps(records, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"Exported rows: {len(df)}")
print(f"CSV: {csv_path}")
print(f"JSON: {json_path}")
if __name__ == "__main__":
main()