From 80063a1cacbeb034172fa739d4e7c4c0fb2dac3d Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 24 Jul 2026 11:44:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=BF=81=E7=A7=BB=20trading=20scripts?= =?UTF-8?q?=20=E7=AC=AC3=E6=89=B9=20(scan=5F*/dca=5Fscanner=20+=20?= =?UTF-8?q?=E6=B8=85=E7=90=86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【迁移】 - dividend-scanner/scripts/scan_cn.sh 改 cd 路径 (原来硬编码 /home/openclaw/scripts) - dividend-scanner/scripts/scan_hk.sh / scan_us.sh 同改 - dividend-scanner/scripts/dca_scanner.py (scan_hk/us 调它) - 删除本地 ~/.hermes/scripts/{scan_cn,scan_hk,scan_us,scan_cn.py,dca_scanner}.* 【cron 路径】 - 84c46e13 (港股高息扫描) → dividend-scanner/scripts/scan_hk.sh - 89a3e7a6 (美股高息扫描) → dividend-scanner/scripts/scan_us.sh - 5d62894c (A股高息扫描) → dividend-scanner/scripts/scan_cn.sh 【清理】git rm --cached lottery-hk/scripts/__pycache__/lottery.cpython-311.pyc (不该 commit 编译产物) + .gitignore 加 __pycache__/ 【保留本地 ~/.hermes/scripts/】(非 trading) - analyze_cmb.sh / memory_check.sh / sync_qdrant_memory.sh / cleanup_disk.sh (Hermes 平台工具) - hk_set_sl_now.sh / okx_trade.sh / merge_hype_shorts.sh / rgti_*.py (一次性工具) - longbridge_hosts_*.sh / update_longbridge_token.sh (长桥 host 维护) - ths_query.py / intraday_cost_check.py / lottery_analyze.sh / lb_test.py - check-gateway-health.sh / clear-telegram-session.sh / mcp_auth_helper.py / mihomo_watchdog.sh / push_to_qq.sh / verify_lb_token.py - stocks/, crypto/ (子目录, 工具集合) - dca_positions.json / rgti_alert_state.json / rgti_t_state.json (数据文件) 【未迁移】crypto/ stocks/ 子目录 (待清理, 不紧急) 【测试】scan_cn.sh 从新位置跑通 --- .gitignore | 1 + dividend-scanner/scripts/dca_scanner.py | 242 ++++++++++++++++++ dividend-scanner/scripts/scan_cn.sh | 3 +- dividend-scanner/scripts/scan_hk.sh | 2 +- dividend-scanner/scripts/scan_us.sh | 2 +- .../__pycache__/lottery.cpython-311.pyc | Bin 26773 -> 0 bytes 6 files changed, 247 insertions(+), 3 deletions(-) create mode 100755 dividend-scanner/scripts/dca_scanner.py delete mode 100644 lottery-hk/scripts/__pycache__/lottery.cpython-311.pyc diff --git a/.gitignore b/.gitignore index b6cefab..47fcff5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ Thumbs.db *.tmp *.bak *~ +__pycache__/ diff --git a/dividend-scanner/scripts/dca_scanner.py b/dividend-scanner/scripts/dca_scanner.py new file mode 100755 index 0000000..69e73b5 --- /dev/null +++ b/dividend-scanner/scripts/dca_scanner.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +""" +高股息股票扫描器 +- 港股/美股/ A股:全部走LongPort API +- A股股息率:使用预设数据(定期更新) +""" +import os, sys, json +from datetime import datetime + +# === 读取LongPort环境变量 === +bashrc = open(os.path.expanduser("~/.bashrc")).read() +for line in bashrc.splitlines(): + if line.startswith("export LONGPORT_") or line.startswith("export LONGBRIDGE_"): + parts = line.replace("export ", "").split("=", 1) + if len(parts) == 2: + os.environ[parts[0]] = parts[1].strip('"').strip("'") + +from longport import openapi +from longport.openapi import CalcIndex + +cfg = openapi.Config.from_env() +ctx = openapi.QuoteContext(config=cfg) + +# === A股候选池(股息率+派息频率,定期更新) === +A_SHARE_POOL = { + "601088.SH": {"name": "中国神华", "yield": 6.7, "freq": "年+中期", "sector": "煤炭"}, + "601328.SH": {"name": "交通银行", "yield": 6.2, "freq": "年度", "sector": "银行"}, + "601398.SH": {"name": "工商银行", "yield": 5.9, "freq": "年度", "sector": "银行"}, + "601288.SH": {"name": "农业银行", "yield": 5.8, "freq": "年度", "sector": "银行"}, + "601939.SH": {"name": "建设银行", "yield": 6.0, "freq": "年度", "sector": "银行"}, + "601988.SH": {"name": "中国银行", "yield": 5.7, "freq": "年度", "sector": "银行"}, + "600900.SH": {"name": "长江电力", "yield": 3.8, "freq": "年度", "sector": "电力"}, + "601857.SH": {"name": "中国石油", "yield": 5.5, "freq": "年度", "sector": "能源"}, + "600028.SH": {"name": "中国石化", "yield": 5.2, "freq": "年度", "sector": "能源"}, + "601728.SH": {"name": "中国电信", "yield": 4.8, "freq": "年度", "sector": "电信"}, + "600036.SH": {"name": "招商银行", "yield": 4.5, "freq": "年度", "sector": "银行"}, + "601166.SH": {"name": "兴业银行", "yield": 5.8, "freq": "年度", "sector": "银行"}, + "601818.SH": {"name": "光大银行", "yield": 5.9, "freq": "年度", "sector": "银行"}, + "600377.SH": {"name": "宁沪高速", "yield": 6.2, "freq": "年度", "sector": "高速"}, + "601666.SH": {"name": "平煤股份", "yield": 6.2, "freq": "年度", "sector": "煤炭"}, + "600023.SH": {"name": "浙能电力", "yield": 5.5, "freq": "年度", "sector": "电力"}, + "000858.SZ": {"name": "五粮液", "yield": 10.5, "freq": "年度", "sector": "白酒"}, + "000568.SZ": {"name": "泸州老窖", "yield": 7.0, "freq": "年度", "sector": "白酒"}, + "000937.SZ": {"name": "冀中能源", "yield": 11.0, "freq": "年度", "sector": "煤炭"}, + "002304.SZ": {"name": "洋河股份", "yield": 10.8, "freq": "年度", "sector": "白酒"}, + "000596.SZ": {"name": "古井贡酒", "yield": 6.9, "freq": "年度", "sector": "白酒"}, + "000001.SZ": {"name": "平安银行", "yield": 5.4, "freq": "年度", "sector": "银行"}, + "600519.SH": {"name": "贵州茅台", "yield": 5.0, "freq": "年+中期", "sector": "白酒"}, +} + +# === 港股/美股候选池 === +STOCK_POOLS = { + "hk": { + "flag": "🇭🇰", "label": "港股", "min_yield": 7.0, + "tickers": [ + "00939.HK", "01398.HK", "00857.HK", "00883.HK", "01088.HK", + "00941.HK", "06823.HK", "00823.HK", "02191.HK", "00778.HK", + "00405.HK", "02778.HK", "01898.HK", "03988.HK", "01288.HK", + "00006.HK", "00002.HK", "00003.HK", "00012.HK", "00016.HK", + "00388.HK", "02318.HK", "00027.HK", "01299.HK", + ], + "freq": { + "00939.HK": "半年", "01398.HK": "半年", "00857.HK": "半年", + "00883.HK": "半年", "01088.HK": "半年", "00941.HK": "半年", + "06823.HK": "半年", "00823.HK": "半年", "02191.HK": "季度", + "00778.HK": "半年", "00405.HK": "半年", "02778.HK": "半年", + "01898.HK": "半年", "03988.HK": "半年", "01288.HK": "半年", + "00006.HK": "半年", "00002.HK": "半年", "00003.HK": "半年", + "00012.HK": "半年", "00016.HK": "半年", "00388.HK": "半年", + "02318.HK": "半年", "00027.HK": "半年", "01299.HK": "半年", + }, + }, + "us": { + "flag": "🇺🇸", "label": "美股", "min_yield": 7.0, + "tickers": [ + "NLY.US", "HTGC.US", "ARCC.US", "AGNC.US", "PSEC.US", + "TWO.US", "STWD.US", "OFS.US", "MAIN.US", "ABR.US", + "CIM.US", "NYMT.US", "SBR.US", "PDI.US", "PTY.US", + ], + "freq": { + "NLY.US": "季度", "HTGC.US": "季度", "ARCC.US": "季度", + "AGNC.US": "月度", "PSEC.US": "月度", "TWO.US": "季度", + "STWD.US": "季度", "OFS.US": "季度", "MAIN.US": "月度", + "ABR.US": "季度", "CIM.US": "季度", "NYMT.US": "季度", + "SBR.US": "月度", "PDI.US": "月度", "PTY.US": "季度", + }, + }, +} + + +def scan_with_longport(market_key): + """用LongPort扫描港股/美股,获取实时价格+股息率""" + pool = STOCK_POOLS[market_key] + tickers = pool["tickers"] + min_yield = pool["min_yield"] + flag = pool["flag"] + freq_map = pool["freq"] + results = [] + all_quotes = {} + + for i in range(0, len(tickers), 15): + batch = tickers[i:i+15] + try: + quotes = ctx.quote(batch) + for q in quotes: + all_quotes[q.symbol] = { + "price": float(q.last_done), + "prev_close": float(q.prev_close), + "name": q.name if hasattr(q, 'name') else q.symbol, + } + except: + pass + try: + indexes = ctx.calc_indexes(batch, [CalcIndex.DividendYield]) + for idx in indexes: + if idx.symbol in all_quotes: + dy = getattr(idx, 'dividend_yield', None) + if dy is not None: + all_quotes[idx.symbol]["yield"] = float(dy) * 100 + except: + pass + + for sym, info in all_quotes.items(): + dy = info.get("yield", 0) + if dy >= min_yield: + change_pct = (info["price"] - info["prev_close"]) / info["prev_close"] * 100 + results.append({ + "symbol": sym, "name": info.get("name", sym), + "price": info["price"], "change_pct": change_pct, + "yield": dy, "freq": freq_map.get(sym, "未知"), + "flag": flag, + }) + + results.sort(key=lambda x: x["yield"], reverse=True) + return results[:5] + + +def scan_a_share(): + """用LongPort获取A股实时价格,配合预设股息率数据""" + flag = "🇨🇳" + min_yield = 5.0 + tickers = list(A_SHARE_POOL.keys()) + all_quotes = {} + + # LongPort获取A股实时价格 + for i in range(0, len(tickers), 15): + batch = tickers[i:i+15] + try: + quotes = ctx.quote(batch) + for q in quotes: + all_quotes[q.symbol] = { + "price": float(q.last_done), + "prev_close": float(q.prev_close), + "name": q.name if hasattr(q, 'name') else q.symbol, + } + except: + pass + + results = [] + for sym, info in A_SHARE_POOL.items(): + if info["yield"] < min_yield: + continue + q = all_quotes.get(sym) + if not q or q["price"] <= 0: + continue + + change_pct = (q["price"] - q["prev_close"]) / q["prev_close"] * 100 + results.append({ + "symbol": sym, "name": q.get("name", info["name"]), + "price": q["price"], "change_pct": change_pct, + "yield": info["yield"], "freq": info["freq"], + "flag": flag, "sector": info.get("sector", ""), + }) + + results.sort(key=lambda x: x["yield"], reverse=True) + return results[:10] + + +def calc_ladder(price): + return [ + {"tier": 1, "pct": -3, "price": round(price * 0.97, 2)}, + {"tier": 2, "pct": -6, "price": round(price * 0.94, 2)}, + {"tier": 3, "pct": -10, "price": round(price * 0.90, 2)}, + ] + + +def format_result(label, flag, results, total_scanned): + now = datetime.now().strftime("%Y-%m-%d %H:%M") + if not results: + return "" # 无候选时不输出,避免空推送 + if "error" in results[0]: + return f"{flag} {label}高息扫描 | {now}\n\n❌ {results[0]['error']}" + + lines = [f"{flag} {label}高息TOP | {now}", ""] + for i, r in enumerate(results, 1): + medal = ["🥇", "🥈", "🥉", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"][min(i-1, 9)] + chg = "📈" if r["change_pct"] >= 0 else "📉" + sector = f" [{r['sector']}]" if r.get("sector") else "" + lines.append(f"{medal} {r['symbol']}{sector}") + lines.append(f" {r['name']}") + lines.append(f" 💰 现价: {r['price']:.2f} {chg} {r['change_pct']:+.1f}%") + lines.append(f" 📊 股息率: {r['yield']:.1f}% 派息: {r['freq']}") + ladder = calc_ladder(r["price"]) + lstr = " → ".join([f"T{l['tier']}:{l['price']:.2f}({l['pct']}%)" for l in ladder]) + lines.append(f" 🪜 阶梯: {lstr}") + lines.append("") + + lines.append("━━━━━━━━━━━━━") + lines.append(f"📋 共扫描 {total_scanned} 只,筛出 {len(results)} 只") + return "\n".join(lines) + + +if __name__ == "__main__": + market = sys.argv[1] if len(sys.argv) > 1 else "all" + + if market == "cn": + results = scan_a_share() + output = format_result("A股", "🇨🇳", results, len(A_SHARE_POOL)) + if output.strip(): + print(output) + elif market == "hk": + results = scan_with_longport("hk") + output = format_result("港股", "🇭🇰", results, len(STOCK_POOLS["hk"]["tickers"])) + if output.strip(): + print(output) + elif market == "us": + results = scan_with_longport("us") + output = format_result("美股", "🇺🇸", results, len(STOCK_POOLS["us"]["tickers"])) + if output.strip(): + print(output) + elif market == "all": + outputs = [] + outputs.append(format_result("港股", "🇭🇰", scan_with_longport("hk"), len(STOCK_POOLS["hk"]["tickers"]))) + outputs.append(format_result("美股", "🇺🇸", scan_with_longport("us"), len(STOCK_POOLS["us"]["tickers"]))) + outputs.append(format_result("A股", "🇨🇳", scan_a_share(), len(A_SHARE_POOL))) + # 过滤空结果 + outputs = [o for o in outputs if o.strip()] + if outputs: + print("\n\n".join(outputs)) + # 无候选时不输出任何内容 + else: + print(f"用法: python3 {sys.argv[0]} [hk|us|cn|all]") diff --git a/dividend-scanner/scripts/scan_cn.sh b/dividend-scanner/scripts/scan_cn.sh index 1701ec1..33dcac3 100755 --- a/dividend-scanner/scripts/scan_cn.sh +++ b/dividend-scanner/scripts/scan_cn.sh @@ -1,3 +1,4 @@ #!/bin/bash -cd /home/openclaw/.hermes/scripts +# 改成从当前目录跑 (skill 仓库下, 用相对路径) +cd "$(dirname "$0")" python3 scan_cn.py diff --git a/dividend-scanner/scripts/scan_hk.sh b/dividend-scanner/scripts/scan_hk.sh index b9ecef8..ffbf629 100755 --- a/dividend-scanner/scripts/scan_hk.sh +++ b/dividend-scanner/scripts/scan_hk.sh @@ -1,3 +1,3 @@ #!/bin/bash -cd /home/openclaw/.hermes/scripts +cd "$(dirname "$0")" python3 dca_scanner.py hk diff --git a/dividend-scanner/scripts/scan_us.sh b/dividend-scanner/scripts/scan_us.sh index 5e385ec..0ce2ca8 100755 --- a/dividend-scanner/scripts/scan_us.sh +++ b/dividend-scanner/scripts/scan_us.sh @@ -1,3 +1,3 @@ #!/bin/bash -cd /home/openclaw/.hermes/scripts +cd "$(dirname "$0")" python3 dca_scanner.py us diff --git a/lottery-hk/scripts/__pycache__/lottery.cpython-311.pyc b/lottery-hk/scripts/__pycache__/lottery.cpython-311.pyc deleted file mode 100644 index 9f593fc645013cfbe81285cc8e514b0b0a9a84ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26773 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