diff --git a/deploy/profile-scripts/import_holding_xls.py b/deploy/profile-scripts/import_holding_xls.py index 4d8cd64a..f15975e9 100644 --- a/deploy/profile-scripts/import_holding_xls.py +++ b/deploy/profile-scripts/import_holding_xls.py @@ -78,21 +78,23 @@ def main(): rate_str = clean_cell(r[16]) rate = float(rate_str) if rate_str and rate_str != '--' else 0.8664 - # 港股:所有金额必须转为 CNY 再存储(mo_models 设计规范:portfolio 应全部存 CNY) + # 港股:个股一律存原币(HKD),不做 CNY 折算(v3 币种规范)。 + # mv 存 HKD 原值,汇总折算由 mo_models.calc_total_mv 统一负责。 if currency == 'HKD': - cost_price = round(cost_price_raw * rate, 2) - mv_cny = round(mkt_val_raw * rate, 2) - price_cny = round(price * rate, 2) + cost_price = round(cost_price_raw, 2) + mv_cny = round(mkt_val_raw, 2) + price_cny = round(price, 2) else: cost_price = round(cost_price_raw, 2) mv_cny = mkt_val_raw price_cny = price - total_mv_cny += mv_cny + # total_mv_cny 仅用于 --total/--mv 未传时的回退汇总:A股直接累计,港股按汇率粗算成 CNY + total_mv_cny += mv_cny if currency == 'CNY' else round(mkt_val_raw * rate, 2) holdings.append({ 'code': code, 'name': name, 'shares': shares, 'price': price_cny, 'cost_price': cost_price, - 'currency': 'CNY', 'market_val': mv_cny, + 'currency': currency, 'market_val': mv_cny, 'cost_amount_raw': cost_amount_raw, 'exchange_rate': rate, }) @@ -119,7 +121,7 @@ def main(): c.execute(''' INSERT INTO holdings (code, name, shares, cost, currency, position_pct, added_at, is_active) VALUES (?, ?, ?, ?, ?, ?, ?, 1) - ''', (h['code'], h['name'], h['shares'], h['cost_price'], 'CNY', + ''', (h['code'], h['name'], h['shares'], h['cost_price'], h['currency'], round(h['market_val'] / total_assets * 100, 2), datetime.now().strftime('%Y-%m-%d'))) c.execute(''' diff --git a/deploy/profile-scripts/mo_models.py b/deploy/profile-scripts/mo_models.py index 32fb72c6..3dee7b1b 100644 --- a/deploy/profile-scripts/mo_models.py +++ b/deploy/profile-scripts/mo_models.py @@ -144,14 +144,14 @@ def validate_portfolio(pf): f" (市值{calc_total_mv(holdings):.2f}+现金{pf.get('cash',0):.2f}+冻结{pf.get('frozen_cash',0):.2f})" ) - # 2. 币种一致性 + # 2. 币种一致性(v3 规范:个股存原币,港股=HKD;汇总才转 CNY) for h in holdings: code = str(h.get('code', '')) currency = h.get('currency', h.get('_currency', '')) - if is_hk_stock(code) and currency == 'HKD': + if is_hk_stock(code) and currency == 'CNY': issues.append( - f"⚠️ 港股{code}({h.get('name','?')}) currency=HKD," - f"portfolio.json 应全部存 CNY" + f"⚠️ 港股{code}({h.get('name','?')}) currency=CNY," + f"个股应存原币 HKD(汇总时由 to_cny 统一折算)" ) # 3. 零股检查 diff --git a/deploy/profile-scripts/refresh_macro_context.py b/deploy/profile-scripts/refresh_macro_context.py index f9facc1a..d7c75eea 100644 --- a/deploy/profile-scripts/refresh_macro_context.py +++ b/deploy/profile-scripts/refresh_macro_context.py @@ -34,8 +34,8 @@ def main(): "上证指数": fetch_index("sh000001", "上证指数"), "深证成指": fetch_index("sz399001", "深证成指"), "创业板指": fetch_index("sz399006", "创业板指"), - "恒生指数": fetch_index("szHSI", "恒生指数"), - "国企指数": fetch_index("szHSCEI", "国企指数"), + "恒生指数": fetch_index("hkHSI", "恒生指数"), + "国企指数": fetch_index("hkHSCEI", "国企指数"), } indices = {k: v for k, v in indices.items() if v} diff --git a/deploy/profile-scripts/strategy_lifecycle.py b/deploy/profile-scripts/strategy_lifecycle.py index c911ee1f..55ead001 100644 --- a/deploy/profile-scripts/strategy_lifecycle.py +++ b/deploy/profile-scripts/strategy_lifecycle.py @@ -18,7 +18,7 @@ from datetime import datetime import technical_analysis as ta import multi_timeframe as mtf from mo_data import read_portfolio, read_decisions, read_watchlist -from mo_models import is_hk_stock, to_cny, get_hk_rate +from mo_models import is_hk_stock from strategy_tree import detect_scenario # ─── 策略准入门禁 — 硬性质量红线 ─────────────────────────────── @@ -828,9 +828,8 @@ def batch_fetch_prices(codes): except: return 0.0 price_raw = f(3) - # 港股:腾讯 API 返回 HKD,需转 CNY - if is_hk_stock(orig_code) and price_raw > 0: - price_raw = to_cny(price_raw) + # 港股:腾讯 API 返回 HKD 原值,价格比较/止损止盈直接用原值 + # (仅市值/总资产汇总时由 mo_models.calc_total_assets 折算 CNY) all_results[orig_code] = { "price": price_raw, "close": f(4), "high": f(33), "low": f(34), "code": orig_code, @@ -843,7 +842,7 @@ def batch_fetch_prices(codes): def get_price_tencent(code): - """获取实时价格。优先 DB(price_monitor 维护),失败才拉腾讯。港股价格已是 CNY。""" + """获取实时价格。优先 DB(price_monitor 维护),失败才拉腾讯。港股价格存 HKD 原值。""" raw_code = str(code).split('_')[0] if not raw_code: return None @@ -865,9 +864,8 @@ def get_price_tencent(code): # Fallback: 腾讯 API try: - from mo_models import to_cny, is_hk_stock + from mo_models import is_hk_stock except ImportError: - to_cny = lambda v, r=None: v is_hk_stock = lambda c: len(str(c).strip()) == 5 and str(c).strip().isdigit() try: if is_hk_stock(raw_code): @@ -885,8 +883,6 @@ def get_price_tencent(code): except: return 0.0 price = f(3) - if is_hk_stock(raw_code) and price > 0: - price = to_cny(price) return { "price": price, "close": f(4), "high": f(33), "low": f(34), "code": raw_code, diff --git a/deploy/profile-scripts/strategy_summary.py b/deploy/profile-scripts/strategy_summary.py index 560d71d5..b93b22ce 100644 --- a/deploy/profile-scripts/strategy_summary.py +++ b/deploy/profile-scripts/strategy_summary.py @@ -1,13 +1,14 @@ #!/usr/bin/env python3 """生成策略评估摘要""" from mo_data import read_decisions, read_portfolio +from mo_models import get_hk_rate dec = read_decisions() pf = read_portfolio() holdings = pf.get('holdings', []) cash = pf.get('cash', 321271) -hk_rate = 0.867 +hk_rate = get_hk_rate() code_to_h = {h['code']: h for h in holdings} decisions = dec.get('decisions', [])