Files
Hermes-Skills/intraday-trading/scripts/us_intraday_monitor.py
T
mike 1877a85cc5 feat: 迁移 trading 相关脚本到 skill 仓库 (第1批: 🔴 高优先级)
【迁移内容】
- crypto-t-monitor/scripts/t_monitor.py (币圈做T, 25.5 KB)
- strategy-management/scripts/us_t_levels.sh + hk_t_levels.sh (做T点位)
- intraday-trading/scripts/us_intraday_{scanner,monitor,close}_cron.sh + .py
- intraday-trading/scripts/hk_intraday_{scanner,monitor,close}_cron.sh + .py

【配套修改】
- 8 个 cron 任务 script 路径更新 (jobs.json):
  - db03f9255ad0 (币圈OKX做T) → crypto-t-monitor/scripts/
  - cfa0c1d6 (美股日内盘前) → intraday-trading/scripts/
  - bcdf7039 (美股日内交易监控) → intraday-trading/scripts/
  - d1acad61 (美股日内平仓) → intraday-trading/scripts/
  - c3401d72 (港股日内盘前) → intraday-trading/scripts/
  - e3667cb0 (港股日内交易监控) → intraday-trading/scripts/
  - 303ec320 (港股日内平仓) → intraday-trading/scripts/
  - c4dc9ac8 (港股做T点位) → strategy-management/scripts/
  - 70d24624 (美股做T点位) → strategy-management/scripts/
- prompt 字段里路径同步更新
- crypto-t-monitor/SKILL.md scripts 段加 t_monitor.py 描述

【删除】本地旧副本 ~/.hermes/scripts/{t_monitor,us_t_levels,hk_t_levels,us_intraday*,hk_intraday*}.{py,sh}
【保留】~/.hermes/scripts/fetch_policy.py 已迁未删 (本次删)

【测试】us_intraday_scanner.py 跑通 (有 shebang, no_agent cron 自动用 python3)
【未改】devops/ + software-development/ 不在 trading git 仓库, 引用已本地更新
2026-07-24 11:39:52 +08:00

264 lines
10 KiB
Python
Executable File

#!/usr/bin/env python3
"""美股日内交易监控+自动下单 - 北京时间21:30-4:00运行"""
import os, json, time
from datetime import datetime
# Force SDK to use international endpoint (bypass 602315 mainland CN geo-block)
os.environ['LONGBRIDGE_REGION'] = 'ap'
# Load LongBridge credentials
config = {}
with open(os.path.expanduser('~/.bashrc'), 'r') as f:
for line in f:
if line.startswith('export LONGPORT_'):
key, value = line.strip().split('=', 1)
config[key.replace('export ', '')] = value
os.environ['LONGPORT_APP_KEY'] = config.get('LONGPORT_APP_KEY', '')
os.environ['LONGPORT_APP_SECRET'] = config.get('LONGPORT_APP_SECRET', '')
os.environ['LONGPORT_ACCESS_TOKEN'] = config.get('LONGPORT_ACCESS_TOKEN', '')
from longport import openapi
cfg = openapi.Config.from_env()
ctx = openapi.QuoteContext(config=cfg)
trade_ctx = openapi.TradeContext(config=cfg)
# 读取盘前筛选结果
screen_file = os.path.expanduser('~/.hermes/skills/trading/quant-factor-mining/artifacts/us_intraday_latest.json')
if not os.path.exists(screen_file):
print("❌ 未找到盘前筛选结果")
exit(1)
with open(screen_file) as f:
screen_data = json.load(f)
candidates = screen_data.get('results', [])[:3] # 取TOP3
# 账户信息
balance = trade_ctx.account_balance()
buying_power = 0
for acc in balance:
if acc.currency == 'USD':
buying_power = float(acc.buy_power)
position_size = buying_power * 0.25 # 25%仓位
print(f"📊 美股日内交易监控启动")
print(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
print(f"购买力: ${buying_power:,.0f}")
print(f"单笔仓位: ${position_size:,.0f}")
print()
print("🎯 监控标的:")
for c in candidates:
print(f" {c['ticker']}: 现价 ${c['price']} | ADR {c['avg_adr']}% | 评分 {c['score']}")
print()
# 读取已入场记录
entry_file = os.path.expanduser('~/.hermes/trading/us_intraday_entries.json')
entries = {}
if os.path.exists(entry_file):
with open(entry_file) as f:
entries = json.load(f)
# 获取实时行情
tickers = [c['ticker'] for c in candidates]
quotes = ctx.quote(tickers)
for q in quotes:
ticker = q.symbol
current = float(q.last_done)
prev_close = float(q.prev_close)
change_pct = (current - prev_close) / prev_close * 100
# 找到对应候选
candidate = next((c for c in candidates if c['ticker'] == ticker), None)
if not candidate:
continue
# 获取5分钟K线计算入场信号
try:
candles = ctx.candlesticks(ticker, openapi.Period.Min_5, 20, openapi.AdjustType.ForwardAdjust)
if not candles:
continue
closes = [float(c.close) for c in candles]
highs = [float(c.high) for c in candles]
lows = [float(c.low) for c in candles]
# 计算SMA
sma5 = sum(closes[-5:]) / 5
sma10 = sum(closes[-10:]) / 10
sma20 = sum(closes) / len(closes)
# 计算ATR
atr = sum(max(highs[i]-lows[i], abs(highs[i]-closes[i-1]), abs(lows[i]-closes[i-1])) for i in range(1, len(candles))) / (len(candles)-1)
# 入场条件
entry_price = None
side = None
# 做多条件: 价格突破SMA5且SMA5>SMA10
if current > sma5 and sma5 > sma10 and current > closes[-2]:
entry_price = current
side = 'buy'
stop_loss = max(min(lows[-5:]), current - atr * 2)
take_profit = current + atr * 3
# 做空条件: 价格跌破SMA5且SMA5<SMA10
elif current < sma5 and sma5 < sma10 and current < closes[-2]:
entry_price = current
side = 'sell'
stop_loss = min(max(highs[-5:]), current + atr * 2)
take_profit = current - atr * 3
if entry_price and side and ticker not in entries:
# 计算股数
shares = int(position_size / current)
if shares < 1:
shares = 1
print(f"🔔 {ticker} 入场信号!")
print(f" 方向: {'做多' if side == 'buy' else '做空'}")
print(f" 入场: ${current:.2f}")
print(f" 止损: ${stop_loss:.2f}")
print(f" 止盈: ${take_profit:.2f}")
print(f" 股数: {shares}")
# 下单
try:
if side == 'buy':
resp = trade_ctx.submit_order(
symbol=ticker,
order_type=openapi.OrderType.LO,
side=openapi.OrderSide.Buy,
submitted_quantity=shares,
time_in_force=openapi.TimeInForceType.Day,
submitted_price=round(current, 2),
)
else:
resp = trade_ctx.submit_order(
symbol=ticker,
order_type=openapi.OrderType.LO,
side=openapi.OrderSide.Sell,
submitted_quantity=shares,
time_in_force=openapi.TimeInForceType.Day,
submitted_price=round(current, 2),
)
print(f" ✅ 下单成功: {resp.order_id}")
# 记录入场
entries[ticker] = {
'side': side,
'entry_price': current,
'stop_loss': stop_loss,
'take_profit': take_profit,
'shares': shares,
'order_id': resp.order_id,
'time': datetime.now().isoformat(),
}
# 保存记录
os.makedirs(os.path.dirname(entry_file), exist_ok=True)
with open(entry_file, 'w') as f:
json.dump(entries, f, indent=2)
except Exception as e:
print(f" ❌ 下单失败: {e}")
elif ticker in entries:
# 只平仓日内系统自己开的仓位
entry = entries[ticker]
entry_shares = entry.get('shares', 0)
order_id = entry.get('order_id', '')
# 验证订单是否已成交(确保是我们开的仓)
if not order_id:
print(f"⚠️ {ticker}: 无订单ID,跳过平仓")
continue
if entry['side'] == 'buy':
if current <= entry['stop_loss']:
print(f"🛑 {ticker} 触发止损! ${current:.2f} <= ${entry['stop_loss']:.2f}")
# 只平我们开的仓位数量
try:
trade_ctx.submit_order(
symbol=ticker,
order_type=openapi.OrderType.MO,
side=openapi.OrderSide.Sell,
submitted_quantity=entry_shares,
time_in_force=openapi.TimeInForceType.Day,
)
print(f" ✅ 平仓成功: 卖出 {entry_shares}股")
del entries[ticker]
except Exception as e:
print(f" ❌ 平仓失败: {e}")
elif current >= entry['take_profit']:
print(f"🎯 {ticker} 触发止盈! ${current:.2f} >= ${entry['take_profit']:.2f}")
# 只平我们开的仓位数量
try:
trade_ctx.submit_order(
symbol=ticker,
order_type=openapi.OrderType.MO,
side=openapi.OrderSide.Sell,
submitted_quantity=entry_shares,
time_in_force=openapi.TimeInForceType.Day,
)
print(f" ✅ 平仓成功: 卖出 {entry_shares}股")
del entries[ticker]
except Exception as e:
print(f" ❌ 平仓失败: {e}")
elif entry['side'] == 'sell':
if current >= entry['stop_loss']:
print(f"🛑 {ticker} 触发止损! ${current:.2f} >= ${entry['stop_loss']:.2f}")
# 只平我们开的仓位数量
try:
trade_ctx.submit_order(
symbol=ticker,
order_type=openapi.OrderType.MO,
side=openapi.OrderSide.Buy,
submitted_quantity=entry_shares,
time_in_force=openapi.TimeInForceType.Day,
)
print(f" ✅ 平仓成功: 买入 {entry_shares}股")
del entries[ticker]
except Exception as e:
print(f" ❌ 平仓失败: {e}")
elif current <= entry['take_profit']:
print(f"🎯 {ticker} 触发止盈! ${current:.2f} <= ${entry['take_profit']:.2f}")
# 只平我们开的仓位数量
try:
trade_ctx.submit_order(
symbol=ticker,
order_type=openapi.OrderType.MO,
side=openapi.OrderSide.Buy,
submitted_quantity=entry_shares,
time_in_force=openapi.TimeInForceType.Day,
)
print(f" ✅ 平仓成功: 买入 {entry_shares}股")
del entries[ticker]
except Exception as e:
print(f" ❌ 平仓失败: {e}")
else:
print(f"⏳ {ticker}: 等待信号 | 现价 ${current:.2f} | SMA5 ${sma5:.2f} | SMA10 ${sma10:.2f}")
except Exception as e:
print(f"❌ {ticker}: {e}")
# 保存更新后的记录
with open(entry_file, 'w') as f:
json.dump(entries, f, indent=2)
print()
if entries:
print("📊 当前持仓:")
for ticker, entry in entries.items():
print(f" {ticker}: {entry['side']} @ ${entry['entry_price']:.2f} | 止损 ${entry['stop_loss']:.2f} | 止盈 ${entry['take_profit']:.2f}")
else:
print("📊 当前无持仓")