fix: 币种规范v3对齐——个股存HKD原币,汇总才转CNY。import_holding_xls存HKD原值;strategy_lifecycle删拉价即转CNY(价格比较与行情HKD原值单位一致);strategy_summary删硬编码0.867走get_hk_rate;mo_models validate对齐v3;refresh_macro_context修恒指前缀szHSI→hkHSI
This commit is contained in:
@@ -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('''
|
||||
|
||||
@@ -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. 零股检查
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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', [])
|
||||
|
||||
Reference in New Issue
Block a user