#!/usr/bin/env python3 """ 每日持仓做T分析 - 交易日早盘前推送 分析持仓股票的技术面,给出做T建议+性价比(含真实手续费) 用法: python3 daily_t_analysis.py 输出: 持仓分析报告(含支撑/阻力/ATR/做T方案/性价比评级) """ import os, sys, json, math from datetime import datetime # Load LongPort creds from bashrc with open(os.path.expanduser('~/.bashrc')) as f: for line in f: line = line.strip() 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] from longport import openapi def F(val, dec=2): return f'{val:.{dec}f}' def calc_hk_fee(amount): """港股手续费:佣金0.03%(min3) + 印花税0.1%(整数) + 征费0.00278% + 交收费0.002%(min2,max100)""" commission = max(3, amount * 0.0003) stamp = math.ceil(amount * 0.001) levy = amount * 0.0000278 trading_fee = amount * 0.0000565 settle = max(2, min(100, amount * 0.00002)) return commission + stamp + levy + trading_fee + settle def calc_us_fee(amount, qty): """美股手续费:佣金$0 + SEC费0.00278%(卖) + FINRA $0.000166/股(卖)""" sec_fee = amount * 0.0000278 finra = max(0.01, qty * 0.000166) return sec_fee + finra def analyze(): cfg = openapi.Config.from_env() trade_ctx = openapi.TradeContext(config=cfg) quote_ctx = openapi.QuoteContext(config=cfg) positions = [] symbols_list = [] resp = trade_ctx.stock_positions() for ch in resp.channels: for pos in ch.positions: if int(pos.quantity) > 0: positions.append({ 'symbol': pos.symbol, 'qty': int(pos.quantity), 'avail': int(pos.available_quantity), 'cost': float(pos.cost_price), }) symbols_list.append(pos.symbol) if not positions: return "📊 无持仓,无需做T分析" # Get lot sizes lot_sizes = {} try: infos = quote_ctx.static_info(symbols_list) for info in infos: lot_sizes[info.symbol] = info.lot_size except: for s in symbols_list: lot_sizes[s] = 1 lines = [f"📊 每日做T分析 | {datetime.now().strftime('%Y-%m-%d')}\n"] for p in positions: sym = p['symbol'] lot_size = lot_sizes.get(sym, 1) try: candles = quote_ctx.candlesticks(sym, openapi.Period.Day, 20, openapi.AdjustType.NoAdjust) closes = [float(c.close) for c in candles] highs = [float(c.high) for c in candles] lows = [float(c.low) for c in candles] sma5 = sum(closes[-5:]) / 5 sma10 = sum(closes[-10:]) / 10 sma20 = sum(closes) / len(closes) current = closes[-1] atr_sum = 0 for i in range(1, min(15, len(candles))): tr = max(highs[-i]-lows[-i], abs(highs[-i]-closes[-i-1]), abs(lows[-i]-closes[-i-1])) atr_sum += tr atr = atr_sum / min(14, len(candles)-1) support = min(lows[-5:]) resistance = max(highs[-5:]) cost = p['cost'] qty = p['qty'] avail = p['avail'] pnl_pct = (current - cost) / cost * 100 pnl_emoji = '🟢' if pnl_pct >= 0 else '🔴' if current > sma5 > sma10 > sma20: trend = "📈多头" elif current < sma5 < sma10 < sma20: trend = "📉空头" elif current > sma10: trend = "↗️偏多" else: trend = "↘️偏弱" atr_pct = atr / current * 100 is_worth = atr_pct > 1.5 is_hk = '.HK' in sym ccy = 'HKD' if is_hk else 'USD' d = 3 if is_hk else 2 buy_zone = min(support, sma20) + atr * 0.2 sell_zone = max(resistance, sma10) - atr * 0.2 t_profit_per_share = sell_zone - buy_zone raw_t_qty = max(1, int(avail * 0.2)) t_qty = max(lot_size, (raw_t_qty // lot_size) * lot_size) if t_qty > avail: t_qty = (avail // lot_size) * lot_size capital_used = buy_zone * t_qty expected_profit = t_profit_per_share * t_qty return_rate = (expected_profit / capital_used * 100) if capital_used > 0 else 0 stop_loss = current - atr * 1.5 risk_per_share = buy_zone - stop_loss risk_total = risk_per_share * t_qty rr = (expected_profit / risk_total) if risk_total > 0 else 0 if is_hk: buy_fee = calc_hk_fee(buy_zone * t_qty) sell_fee = calc_hk_fee(sell_zone * t_qty) else: buy_fee = calc_us_fee(buy_zone * t_qty, t_qty) sell_fee = calc_us_fee(sell_zone * t_qty, t_qty) fee = buy_fee + sell_fee net_profit = expected_profit - fee if rr >= 3 and return_rate >= 1.5: rating = "⭐⭐⭐ 高" elif rr >= 2 and return_rate >= 1: rating = "⭐⭐ 中" elif rr >= 1.5 and return_rate >= 0.5: rating = "⭐ 低" else: rating = "❌ 不建议" lines.append(f"{'━' * 30}") lines.append(f"📌 {sym} | {qty}股({qty//lot_size}手) | 成本{F(cost, d)}{ccy}") lines.append(f"现价{F(current, d)} | {pnl_emoji}{pnl_pct:+.1f}% | {trend} | ATR{F(atr, d)}({atr_pct:.1f}%)") lines.append(f"支撑{F(support, d)} | 阻力{F(resistance, d)}") if is_worth and t_qty >= lot_size: lines.append(f"🎯 低吸{F(buy_zone, d)} → 高抛{F(sell_zone, d)} | {t_qty}股({t_qty//lot_size}手)") lines.append(f"📐 性价比: {rating}") lines.append(f"• 预期利润: {F(net_profit, 1)}{ccy} | 收益率: {return_rate:.1f}%") lines.append(f"• 盈亏比: {rr:.1f}:1 | 手续费: {F(fee, 1)}{ccy}(买{F(buy_fee,1)}+卖{F(sell_fee,1)})") lines.append(f"• 止损: {F(stop_loss, d)} | 最大亏损: {F(risk_total, 1)}{ccy}") elif not is_worth: lines.append(f"💡 波动太小,暂不建议做T | 性价比: {rating}") else: lines.append(f"⚠️ 不足1手({lot_size}股),无法做T") except Exception as e: lines.append(f"❌ {sym}: {e}") lines.append(f"\n⏰ 港股9:30-16:00 | 美股21:30-04:00 (北京时间)") return '\n'.join(lines) if __name__ == '__main__': result = analyze() print(result)