1. stale_detector新增自选股买入区偏离自动重评: - 每轮扫描watchlist_stocks, price偏离买入区中心>15%自动触发per_stock_reassess - 之前只标记[STRATEGY_STALE]不输出,改为标记+触发重评两步完成 - 策略完毕直接输出结果,不再等下次cron通知 2. 000850华茂股份全面重评: - 核心价值:纺织是壳,金融股权投资才是核心(国泰海通/广发/徽商银行) - PB=0.78破净, 7月3日分红3675万占年净利17.85% - 7/16临时股东会催化剂 - 结论:3.70~3.90区间可建仓1~2%,止损3.50,止盈4.30 - 修复:之前说'观望不建仓'是错的,低估了破净安全垫 3. watchlist_stocks DB加000850记录
88 lines
2.9 KiB
Python
88 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""生成策略评估摘要"""
|
|
import json
|
|
|
|
with open('/home/hmo/web-dashboard/data/decisions.json') as f:
|
|
dec = json.load(f)
|
|
with open('/home/hmo/MoFin/data/portfolio.json') as f:
|
|
pf = json.load(f)
|
|
|
|
holdings = pf.get('holdings', [])
|
|
cash = pf.get('cash', 321271)
|
|
hk_rate = 0.867
|
|
code_to_h = {h['code']: h for h in holdings}
|
|
|
|
decisions = dec.get('decisions', [])
|
|
hold_entries = [s for s in decisions if s.get('shares', 0) > 0]
|
|
wl_entries = [s for s in decisions if s.get('shares', 0) == 0]
|
|
|
|
hk_total_cny = 0
|
|
a_total = 0
|
|
for h in holdings:
|
|
mv = h['shares'] * h['price']
|
|
if h.get('currency') == 'HKD':
|
|
hk_total_cny += mv * hk_rate
|
|
else:
|
|
a_total += mv
|
|
total_mv = hk_total_cny + a_total
|
|
total_assets = total_mv + cash
|
|
position_pct = total_mv / total_assets * 100
|
|
|
|
weak_count = sum(1 for s in hold_entries if s.get('stock_category') in ('弱势','深套'))
|
|
|
|
print(f'总市值: {total_mv:.0f} CNY (HK${hk_total_cny:.0f} A¥{a_total:.0f})')
|
|
print(f'总资产: {total_assets:.0f} CNY')
|
|
print(f'仓位: {position_pct:.1f}% 现金: {cash:.0f}')
|
|
print(f'持仓: {len(hold_entries)}只 弱势/深套: {weak_count}只 ({weak_count/len(hold_entries)*100:.0f}%)')
|
|
print(f'自选: {len(wl_entries)}只')
|
|
print()
|
|
|
|
print('【持仓详情】')
|
|
for s in hold_entries:
|
|
code = s['code']
|
|
name = s['name']
|
|
shares = s['shares']
|
|
cost = s.get('cost', 0)
|
|
sl = s.get('stop_loss', 0)
|
|
tp = s.get('take_profit', 0)
|
|
cat = s.get('stock_category', '?')
|
|
sig = s.get('timing_signal', '?')
|
|
|
|
h = code_to_h.get(code)
|
|
price = h['price'] if h else 0
|
|
if h and h.get('currency') == 'HKD':
|
|
mv_val = h['shares'] * h['price'] * hk_rate
|
|
else:
|
|
mv_val = h['shares'] * h['price'] if h else 0
|
|
pl_pct = (price - cost) / cost * 100 if cost else 0
|
|
pct = mv_val / total_assets * 100
|
|
sl_dist = (price / sl - 1) * 100 if sl > 0 else 0
|
|
tp_dist = (tp / price - 1) * 100 if tp > 0 else 0
|
|
|
|
flags = []
|
|
if sl_dist < 5:
|
|
if pl_pct > 5:
|
|
flags.append('利润保护')
|
|
else:
|
|
flags.append(f'⚠️近止损({sl_dist:.0f}%)')
|
|
if tp_dist < 5 and tp_dist > 0:
|
|
flags.append('近止盈')
|
|
if cat in ('弱势','深套'):
|
|
flags.append(f'[{cat}]')
|
|
|
|
flag_str = ' '.join(flags) if flags else ''
|
|
print(f' {code} {name:10s} ¥{price:>7.2f} 浮{pl_pct:+.1f}% 仓{pct:.1f}% 损{sl}({sl_dist:.0f}%) 盈{tp}({tp_dist:.0f}%) {flag_str}')
|
|
|
|
print()
|
|
print('【自选关注】')
|
|
for s in wl_entries:
|
|
code = s['code']
|
|
name = s['name']
|
|
el = s.get('entry_low', 0)
|
|
eh = s.get('entry_high', 0)
|
|
sl = s.get('stop_loss', 0)
|
|
price = s.get('price', 0)
|
|
sig = s.get('timing_signal', '?')
|
|
in_zone = '✅在买入区' if el and eh and price and el <= price <= eh else ''
|
|
print(f' {code} {name:10s} ¥{price:>7.2f} 买区{el}~{eh} 损{sl} 信号{sig} {in_zone}')
|