fix: HK stocks store HKD, calc_total_assets converts to CNY at aggregation
This commit is contained in:
+15
-7
@@ -33,8 +33,10 @@ for h in pf.get('holdings', []):
|
||||
is_hk = is_hk_stock(code)
|
||||
flag = 'HK' if is_hk else 'A '
|
||||
|
||||
# 检查币种
|
||||
if is_hk and curr != 'CNY':
|
||||
# 检查币种:港股应为 HKD,A股应为 CNY
|
||||
if is_hk and curr != 'HKD':
|
||||
hk_cny_issues.append(f"{code} {name}: currency={curr} (应为HKD)")
|
||||
if not is_hk and curr != 'CNY':
|
||||
hk_cny_issues.append(f"{code} {name}: currency={curr} (应为CNY)")
|
||||
|
||||
# 检查 cost=0 的股票
|
||||
@@ -55,8 +57,11 @@ print(f" stored cash: {pf.get('cash')}")
|
||||
print(f" stored frozen_cash: {pf.get('frozen_cash')}")
|
||||
print(f" stored total_pnl: {pf.get('total_pnl')}")
|
||||
|
||||
calc_ta = total_mv + (pf.get('cash') or 0) + (pf.get('frozen_cash') or 0)
|
||||
print(f" calculated total_assets: {calc_ta:.2f}")
|
||||
# 用 mo_models 计算(已处理 HKD→CNY)
|
||||
calc_ta = calc_total_assets(pf)
|
||||
calc_mv = calc_total_mv(pf.get('holdings', []))
|
||||
print(f" calculated total_mv (CNY): {calc_mv:.2f}")
|
||||
print(f" calculated total_assets (CNY): {calc_ta:.2f}")
|
||||
|
||||
if abs((pf.get('total_assets') or 0) - calc_ta) > 1:
|
||||
errors.append(f"total_assets mismatch: stored={pf.get('total_assets')} vs calc={calc_ta:.2f}")
|
||||
@@ -68,12 +73,15 @@ hkds = 0
|
||||
none_curr = 0
|
||||
for d in dec.get('decisions', []):
|
||||
c = d.get('currency', '')
|
||||
code = d.get('code', '')
|
||||
if c == 'CNY': cnys += 1
|
||||
elif c == 'HKD': hkds += 1
|
||||
else: none_curr += 1
|
||||
print(f" CNY: {cnys} HKD: {hkds} None: {none_curr}")
|
||||
if hkds > 0:
|
||||
errors.append(f"Decisions: {hkds} entries still have currency=HKD")
|
||||
# 港股应标 HKD,A 股应标 CNY
|
||||
if is_hk_stock(str(code)) and c != 'HKD':
|
||||
errors.append(f"决策 {code} currency={c} (应为HKD)")
|
||||
elif not is_hk_stock(str(code)) and c != 'CNY':
|
||||
errors.append(f"决策 {code} currency={c} (应为CNY)")
|
||||
|
||||
# ── 4. 错误汇总 ──
|
||||
print(f"\n{'='*40}")
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""回滚:港股 holdings/decisions cost 从 CNY 转回 HKD,currency=HKD"""
|
||||
import sqlite3, sys
|
||||
sys.path.insert(0, '/home/hmo/MoFin')
|
||||
from mo_models import is_hk_stock, get_hk_rate, calc_total_assets, calc_total_mv, calc_position_pct
|
||||
from datetime import datetime
|
||||
|
||||
rate = get_hk_rate()
|
||||
print(f"HK_RATE: {rate}")
|
||||
|
||||
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
|
||||
|
||||
# ── 1. holdings: 港股 currency=HKD, cost 转回 HKD ──
|
||||
rows = db.execute("SELECT code, name, cost, price, shares, currency FROM holdings WHERE is_active=1").fetchall()
|
||||
fixed_cost = 0
|
||||
for r in rows:
|
||||
code, name, cost, price, shares, curr = r
|
||||
if is_hk_stock(str(code)):
|
||||
# 成本从 CNY 转回 HKD: cost_cny / rate
|
||||
cost_hkd = round(((cost or 0)) / rate, 2) if (cost or 0) else 0
|
||||
db.execute("UPDATE holdings SET cost=?, currency='HKD' WHERE code=?", (cost_hkd, code))
|
||||
print(f" HOLD {code} {name}: cost {cost or 0:.2f}CNY → {cost_hkd:.2f}HKD, curr={curr}→HKD")
|
||||
fixed_cost += 1
|
||||
|
||||
# ── 2. holding_strategies: currency=HKD, cost/price/stop_loss/take_profit 转 HKD ──
|
||||
rows = db.execute("SELECT code, name, cost, price, stop_loss, take_profit, currency FROM holding_strategies WHERE status IN ('active','updated')").fetchall()
|
||||
fixed_strat = 0
|
||||
for r in rows:
|
||||
code, name, cost, price, sl, tp, curr = r
|
||||
if is_hk_stock(str(code)):
|
||||
cost_hkd = round(((cost or 0)) / rate, 2) if (cost or 0) else 0
|
||||
price_hkd = round(((price or 0)) / rate, 2) if (price or 0) else 0
|
||||
sl_hkd = round(((sl or 0)) / rate, 2) if (sl or 0) else 0
|
||||
tp_hkd = round(((tp or 0)) / rate, 2) if (tp or 0) else 0
|
||||
db.execute("UPDATE holding_strategies SET cost=?, price=?, stop_loss=?, take_profit=?, currency='HKD' WHERE code=?",
|
||||
(cost_hkd, price_hkd, sl_hkd, tp_hkd, code))
|
||||
print(f" STRAT {code} {name}: c {cost or 0}→{cost_hkd} p {price or 0}→{price_hkd} curr={curr}→HKD")
|
||||
fixed_strat += 1
|
||||
|
||||
db.commit()
|
||||
|
||||
# ── 3. portfolio_summary: 用 calc_total_assets 重算(已处理 HKD→CNY)──
|
||||
rows = db.execute("SELECT code, name, shares, cost, price, currency FROM holdings WHERE is_active=1").fetchall()
|
||||
holdings = [dict(zip(['code','name','shares','cost','price','currency'], r)) for r in rows]
|
||||
sr = db.execute("SELECT cash, frozen_cash FROM portfolio_summary WHERE id=1").fetchone()
|
||||
pf = {'holdings': holdings, 'cash': sr[0] or 0, 'frozen_cash': sr[1] or 0}
|
||||
|
||||
mv_cny = calc_total_mv(holdings)
|
||||
ta_cny = calc_total_assets(pf)
|
||||
pp = calc_position_pct(pf)
|
||||
|
||||
# P&L: 港股按 HKD 算个股 P&L,汇总时转 CNY
|
||||
total_pnl = 0
|
||||
for h in holdings:
|
||||
p = h['price'] or 0
|
||||
c = h['cost'] or 0
|
||||
s = h['shares'] or 0
|
||||
if is_hk_stock(str(h['code'])):
|
||||
total_pnl += (p - c) * s * rate # HKD P&L → CNY
|
||||
else:
|
||||
total_pnl += (p - c) * s
|
||||
|
||||
db.execute("UPDATE portfolio_summary SET total_mv=?, total_assets=?, total_pnl=?, position_pct=?, updated_at=? WHERE id=1",
|
||||
(round(mv_cny,2), round(ta_cny,2), round(total_pnl,2), pp, datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
|
||||
|
||||
print(f"\nportfolio_summary: mv_cny={mv_cny:.2f} ta_cny={ta_cny:.2f} pnl_cny={total_pnl:.2f} pos={pp}%")
|
||||
print(f"\nDone. Fixed {fixed_cost} holdings + {fixed_strat} strategies.")
|
||||
db.commit()
|
||||
db.close()
|
||||
Reference in New Issue
Block a user