Files

105 lines
4.0 KiB
Markdown

# LongPort HTTP Endpoint Fallback (2026-08-02)
## Problem
Cron scripts that use the LongPort Python SDK fail with:
```
longport.OpenApiException: error sending request for url
(https://openapi.longportapp.cn/v1/socket/token): client error (Connect)
```
The script set `LONGBRIDGE_HTTP_URL`, but the SDK silently ignores it and uses the default `openapi.longportapp.cn`. When the .cn endpoint is down (Aliyun load balancer 502), the cron fails.
## Root Cause
The SDK's `Config.from_env()` reads **`LONGPORT_HTTP_URL`**, NOT `LONGBRIDGE_HTTP_URL`. The CLI uses `LONGBRIDGE_*`; the Python SDK uses `LONGPORT_*`. This is a frequent bug because:
1. The CLI's documentation uses `LONGBRIDGE_*`
2. The Python wheel's type stubs (`.pyi`) document `LONGPORT_*`
3. Scripts often copy bashrc exports (`LONGBRIDGE_*`) and assume they work
## Fix Pattern
Always set **both** env vars in cron wrappers (they don't conflict):
```bash
# In the wrapper script (e.g. cn_dividend_buy_timing_cron.sh):
export LONGPORT_HTTP_URL=https://openapi.longportapp.cn
export LONGPORT_QUOTE_WS_URL=wss://openapi-quote.longportapp.cn
export LONGPORT_TRADE_WS_URL=wss://openapi-trade.longportapp.cn
export LONGPORT_LANGUAGE=zh-CN
```
## Fallback Pattern (Python init)
When the .cn endpoint is down, try the .com endpoint:
```python
def get_longbridge():
"""带重试的长桥 SDK init - 优先 .cn (国内), fallback .com (海外)"""
import time
load_longbridge_env()
configs = [
{
'http': 'https://openapi.longportapp.cn',
'quote_ws': 'wss://openapi-quote.longportapp.cn',
'trade_ws': 'wss://openapi-trade.longportapp.cn',
},
{
'http': 'https://openapi.longbridge.com',
'quote_ws': 'wss://openapi-quote.longbridge.com/v2',
'trade_ws': 'wss://openapi-trade.longbridge.com/v2',
},
]
from longport import openapi
last_err = None
for i, c in enumerate(configs):
try:
os.environ['LONGPORT_HTTP_URL'] = c['http']
os.environ['LONGPORT_QUOTE_WS_URL'] = c['quote_ws']
os.environ['LONGPORT_TRADE_WS_URL'] = c['trade_ws']
os.environ.setdefault('LONGPORT_LANGUAGE', 'zh-CN')
cfg = openapi.Config.from_env()
ctx = openapi.QuoteContext(config=cfg)
try:
ctx.quote(['600000.SH']) # test connection
except Exception as e:
last_err = e
if i < len(configs) - 1:
print(f"[longbridge] {c['http']} quote 失败: {e}, 重试下一节点", flush=True)
time.sleep(1)
continue
else:
return ctx, openapi.Period
print(f"[longbridge] 使用 {c['http']} ✓", flush=True)
return ctx, openapi.Period
except Exception as e:
last_err = e
print(f"[longbridge] {c['http']} init 失败: {e}", flush=True)
if i < len(configs) - 1:
time.sleep(1)
continue
raise last_err or RuntimeError("长桥 SDK init 全部失败")
```
## Test
2026-08-02: cron `8000d3cfed23 cn-dividend-buy-timing` failed at 11:30 with `OpenApiException: client error (Connect)`. After applying the fix:
- `.cn` (Aliyun) endpoint came back online — script ran successfully
- `longrun_http_url=https://openapi.longportapp.cn` printed ✓
## Open Questions
- Are `.cn` and `.com` endpoints actually interchangeable? E.g. do quotes for the same symbol return identical data? The fallback assumes yes; not verified.
- Could there be a subtle difference between the SDK's hardcoded `openapi.longportapp.cn` and the user-configured one? E.g. token validation, rate limits, etc.?
## Files Touched
- `~/.hermes/scripts/cn_dividend_buy_timing.py` — get_longbridge() with fallback
- `~/.hermes/skills/trading/lottery-hk/scripts/lottery_4frame.py` — same idempotent init
- `~/.hermes/scripts/cn_dividend_buy_timing_cron.sh` — wrapper sets LONGPORT_* env vars