654 lines
25 KiB
Python
654 lines
25 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
XTZH AI 定价引擎 v2.0
|
||
=======================
|
||
NAC 宪法合规定价模型
|
||
|
||
核心公式:
|
||
P_XTZH (USD) = (w_fx × V_fx + w_au × V_au + w_com × V_com) / 10000
|
||
|
||
三层价值(均以 USD 计,通过 SDR 换算):
|
||
V_fx = 1 SDR 的 USD 等价值(货币篮子加权)
|
||
V_au = 黄金 SDR 等价价值 × XTZH 黄金份额
|
||
V_com = 大宗商品 SDR 等价价值加权均值 × XTZH 商品份额
|
||
|
||
动态权重(VIX/DXY 作为调节杠杆,不参与价格计算):
|
||
基准:w_fx=4000, w_au=1000, w_com=5000(基点,总和=10000)
|
||
VIX 高 → w_au ↑,w_com ↓(避险模式)
|
||
DXY 高 → w_fx 微调
|
||
约束:w_au ∈ [500, 2000],w_fx ∈ [2000, 6000]
|
||
|
||
作者:NAC 系统
|
||
版本:2.0.0
|
||
"""
|
||
|
||
import json
|
||
import logging
|
||
import time
|
||
import urllib.request
|
||
from datetime import datetime, timezone
|
||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||
|
||
# ============================================================================
|
||
# 日志配置
|
||
# ============================================================================
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s [XTZH-PRICING-v2] %(levelname)s %(message)s',
|
||
handlers=[
|
||
logging.StreamHandler(),
|
||
logging.FileHandler('/opt/nac/xtzh-pricing/xtzh_pricing.log', encoding='utf-8')
|
||
]
|
||
)
|
||
logger = logging.getLogger('xtzh-pricing')
|
||
|
||
# ============================================================================
|
||
# NAC 宪法常量(来自 constants.rs)
|
||
# ============================================================================
|
||
|
||
# 基准权重(基点,总和 = 10000)
|
||
W_FX_BASE = 4000 # 货币层基准权重 40%
|
||
W_AU_BASE = 1000 # 黄金层基准权重 10%
|
||
W_COM_BASE = 5000 # 商品层基准权重 50%
|
||
WEIGHT_SUM = 10000 # 权重总和约束
|
||
|
||
# 黄金层权重动态范围
|
||
W_AU_MIN = 500 # 最低 5%
|
||
W_AU_MAX = 2000 # 最高 20%
|
||
|
||
# 黄金信任锚覆盖率 125%
|
||
GOLD_TRUST_ANCHOR_COVERAGE = 1.25
|
||
|
||
# SDR 货币篮子权重(IMF 2022-2026)
|
||
SDR_BASKET = {
|
||
'USD': 0.4332, # 43.32%
|
||
'EUR': 0.2923, # 29.23%
|
||
'CNY': 0.1059, # 10.59%
|
||
'JPY': 0.0784, # 7.84%
|
||
'GBP': 0.0802, # 8.02%
|
||
}
|
||
|
||
# XTZH 黄金份额:每 1 XTZH 对应的黄金盎司数
|
||
# 基于 NAC 宪法:黄金储备覆盖率 125%,黄金层权重 10%
|
||
# 设定:1 XTZH 对应 0.0005 盎司黄金(历史 SDR 黄金等价)
|
||
XTZH_GOLD_SHARE_OZ = 0.0005 # 盎司/XTZH
|
||
|
||
# 大宗商品篮子权重(商品层内部权重,总和 = 1.0)
|
||
COMMODITY_BASKET = {
|
||
'wti_oil': 0.30, # WTI 原油 30%
|
||
'copper': 0.20, # 铜 20%
|
||
'wheat': 0.12, # 小麦 12%
|
||
'corn': 0.10, # 玉米 10%
|
||
'soybean': 0.10, # 大豆 10%
|
||
'natural_gas':0.10, # 天然气 10%
|
||
'silver': 0.08, # 白银 8%
|
||
}
|
||
|
||
# 商品基准价格(USD,用于计算 SDR 等价份额)
|
||
# 每单位商品对应的 SDR 等价基准
|
||
COMMODITY_BASE_PRICES = {
|
||
'wti_oil': 75.0, # USD/桶
|
||
'copper': 4.2, # USD/磅
|
||
'wheat': 550.0, # USD/蒲式耳(美分/蒲式耳 ÷ 100 × 5000)
|
||
'corn': 450.0, # USD/蒲式耳
|
||
'soybean': 1200.0, # USD/蒲式耳
|
||
'natural_gas': 3.0, # USD/百万英热
|
||
'silver': 25.0, # USD/盎司
|
||
}
|
||
|
||
# ============================================================================
|
||
# 数据缓存
|
||
# ============================================================================
|
||
_cache = {
|
||
'data': None,
|
||
'timestamp': 0,
|
||
'ttl': 300 # 5 分钟缓存
|
||
}
|
||
|
||
# ============================================================================
|
||
# 数据获取函数
|
||
# ============================================================================
|
||
|
||
def fetch_url(url, timeout=10):
|
||
"""获取 URL 内容,返回解析后的 JSON"""
|
||
try:
|
||
req = urllib.request.Request(url, headers={
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||
'Accept': 'application/json'
|
||
})
|
||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||
return json.loads(resp.read().decode('utf-8'))
|
||
except Exception as e:
|
||
logger.warning("获取 " + url + " 失败: " + str(e))
|
||
return None
|
||
|
||
|
||
def fetch_yahoo_price(symbol):
|
||
"""从 Yahoo Finance 获取期货/指数价格"""
|
||
url = "https://query1.finance.yahoo.com/v8/finance/chart/" + symbol + "?interval=1d&range=1d"
|
||
data = fetch_url(url)
|
||
if data:
|
||
try:
|
||
return float(data['chart']['result'][0]['meta']['regularMarketPrice'])
|
||
except Exception:
|
||
pass
|
||
return None
|
||
|
||
|
||
def fetch_sdr_rates():
|
||
"""
|
||
获取 SDR 汇率(1 XDR = ? 各货币)
|
||
返回:{'USD': 1.3745, 'EUR': 1.1663, 'CNY': 9.4448, 'GBP': 1.0171, 'JPY': 215.27}
|
||
"""
|
||
data = fetch_url("https://open.er-api.com/v6/latest/XDR")
|
||
if data and data.get('rates'):
|
||
r = data['rates']
|
||
result = {
|
||
'USD': r.get('USD', 1.3745),
|
||
'EUR': r.get('EUR', 1.1663),
|
||
'GBP': r.get('GBP', 1.0171),
|
||
'JPY': r.get('JPY', 215.27),
|
||
'CNY': r.get('CNY', 9.4448),
|
||
'updated': data.get('time_last_update_utc', '')
|
||
}
|
||
logger.info("SDR汇率: 1 SDR = USD:" + str(round(result['USD'],4)) +
|
||
" EUR:" + str(round(result['EUR'],4)) +
|
||
" CNY:" + str(round(result['CNY'],4)))
|
||
return result
|
||
# 备用值(2026-02-26 实测)
|
||
logger.warning("SDR汇率获取失败,使用备用数据")
|
||
return {'USD': 1.3745, 'EUR': 1.1663, 'GBP': 1.0171, 'JPY': 215.27, 'CNY': 9.4448, 'updated': 'fallback'}
|
||
|
||
|
||
def fetch_gold_price():
|
||
"""
|
||
获取黄金价格(USD/盎司)
|
||
主源:Yahoo Finance GC=F
|
||
备源:Binance PAXG
|
||
"""
|
||
# 主源
|
||
price = fetch_yahoo_price("GC=F")
|
||
if price and price > 0:
|
||
logger.info("黄金价格 (Yahoo GC=F): $" + str(price) + "/oz")
|
||
return price, "Yahoo Finance GC=F"
|
||
|
||
# 备源:Binance PAXG
|
||
data = fetch_url("https://api.binance.com/api/v3/ticker/price?symbol=PAXGUSDT")
|
||
if data and data.get('price'):
|
||
price = float(data['price'])
|
||
logger.info("黄金价格 (Binance PAXG): $" + str(price) + "/oz")
|
||
return price, "Binance PAXG"
|
||
|
||
logger.warning("黄金价格获取失败,使用备用值 $2650")
|
||
return 2650.0, "fallback"
|
||
|
||
|
||
def fetch_commodity_prices():
|
||
"""
|
||
获取大宗商品价格(USD)
|
||
返回:{'wti_oil': 65.53, 'copper': 6.023, 'wheat': 569.0, ...}
|
||
"""
|
||
symbols = {
|
||
'wti_oil': 'CL=F',
|
||
'copper': 'HG=F',
|
||
'wheat': 'ZW=F',
|
||
'corn': 'ZC=F',
|
||
'soybean': 'ZS=F',
|
||
'natural_gas': 'NG=F',
|
||
'silver': 'SI=F',
|
||
}
|
||
prices = {}
|
||
for name, sym in symbols.items():
|
||
price = fetch_yahoo_price(sym)
|
||
if price and price > 0:
|
||
prices[name] = price
|
||
logger.info("商品价格 " + name + " (" + sym + "): " + str(price))
|
||
else:
|
||
prices[name] = COMMODITY_BASE_PRICES[name]
|
||
logger.warning("商品价格 " + name + " 获取失败,使用基准值 " + str(COMMODITY_BASE_PRICES[name]))
|
||
return prices
|
||
|
||
|
||
def fetch_risk_indicators():
|
||
"""
|
||
获取风险调节指标(仅用于动态权重调节,不参与价格计算)
|
||
VIX:恐慌指数
|
||
DXY:美元指数
|
||
"""
|
||
vix = fetch_yahoo_price("^VIX")
|
||
dxy = fetch_yahoo_price("DX-Y.NYB")
|
||
if not dxy:
|
||
dxy = fetch_yahoo_price("DX=F")
|
||
result = {
|
||
'vix': vix if vix else 20.0,
|
||
'dxy': dxy if dxy else 104.0,
|
||
}
|
||
logger.info("风险指标 (权重杠杆): VIX=" + str(result['vix']) + " DXY=" + str(result['dxy']))
|
||
return result
|
||
|
||
# ============================================================================
|
||
# 三层价值计算
|
||
# ============================================================================
|
||
|
||
def calc_fx_layer(sdr_rates):
|
||
"""
|
||
货币层价值 V_fx(USD)
|
||
|
||
V_fx = 1 SDR 的 USD 等价值
|
||
即:以 IMF SDR 货币篮子权重,加权计算 1 SDR 对应的 USD 价值
|
||
|
||
由于 SDR/USD 汇率已经是货币篮子的综合结果,
|
||
直接取 1 SDR = sdr_rates['USD'] USD
|
||
"""
|
||
v_fx = sdr_rates['USD'] # 1 SDR 的 USD 等价值
|
||
logger.info("货币层 V_fx = $" + str(round(v_fx, 6)) + " USD (1 SDR)")
|
||
return v_fx
|
||
|
||
|
||
def calc_gold_layer(gold_price_usd, sdr_rates):
|
||
"""
|
||
黄金层价值 V_au(USD)
|
||
|
||
V_au = 黄金 SDR 等价价值 × XTZH 黄金份额 × 黄金信任锚覆盖率
|
||
|
||
计算步骤:
|
||
1. 黄金 SDR 等价价值(USD/oz)= gold_price_usd(直接以 USD 计)
|
||
2. 每 1 XTZH 对应 XTZH_GOLD_SHARE_OZ 盎司黄金
|
||
3. 黄金层价值 = gold_price_usd × XTZH_GOLD_SHARE_OZ × 1.25(覆盖率)
|
||
|
||
注意:黄金层权重 10% 已在最终加权公式中体现,
|
||
这里计算的是"如果 100% 都是黄金层,每 1 XTZH 值多少 USD"
|
||
"""
|
||
# 黄金 SDR 等价(USD/oz)
|
||
gold_sdr_value = gold_price_usd / sdr_rates['USD'] # 黄金价格的 SDR 等价
|
||
|
||
# 每 1 XTZH 对应的黄金 SDR 价值(USD)
|
||
v_au = gold_price_usd * XTZH_GOLD_SHARE_OZ * GOLD_TRUST_ANCHOR_COVERAGE
|
||
|
||
logger.info("黄金层 V_au: 金价=$" + str(round(gold_price_usd,2)) +
|
||
"/oz × " + str(XTZH_GOLD_SHARE_OZ) + "oz × " +
|
||
str(GOLD_TRUST_ANCHOR_COVERAGE) + " = $" + str(round(v_au,6)) + " USD")
|
||
return v_au
|
||
|
||
|
||
def calc_commodity_layer(commodity_prices, sdr_rates):
|
||
"""
|
||
商品层价值 V_com(USD)
|
||
|
||
V_com = Σ (商品权重 × 商品价格 SDR 等价 / 基准价格 SDR 等价) × 1 SDR USD 等价
|
||
|
||
计算步骤:
|
||
1. 每种商品的当前价格与基准价格的比值(价格偏离因子)
|
||
2. 以商品篮子权重加权求和,得到综合商品价格指数
|
||
3. 乘以 1 SDR 的 USD 等价值,得到商品层价值
|
||
|
||
逻辑:商品层代表"以大宗商品衡量的 1 SDR 购买力"
|
||
当商品价格整体上涨时,XTZH 的商品层价值随之上升
|
||
"""
|
||
sdr_in_usd = sdr_rates['USD']
|
||
|
||
# 计算各商品价格偏离因子(当前价格 / 基准价格)
|
||
commodity_index = 0.0
|
||
detail = {}
|
||
for name, weight in COMMODITY_BASKET.items():
|
||
current_price = commodity_prices.get(name, COMMODITY_BASE_PRICES[name])
|
||
base_price = COMMODITY_BASE_PRICES[name]
|
||
price_ratio = current_price / base_price # 价格偏离因子
|
||
contribution = weight * price_ratio
|
||
commodity_index += contribution
|
||
detail[name] = {
|
||
'current': round(current_price, 4),
|
||
'base': base_price,
|
||
'ratio': round(price_ratio, 4),
|
||
'weight': weight,
|
||
'contribution': round(contribution, 6)
|
||
}
|
||
logger.info(" 商品 " + name + ": $" + str(round(current_price,4)) +
|
||
" / 基准$" + str(base_price) + " = " + str(round(price_ratio,4)) +
|
||
" × " + str(weight) + " = " + str(round(contribution,6)))
|
||
|
||
# 商品层价值 = 1 SDR USD 等价 × 综合商品价格指数
|
||
v_com = sdr_in_usd * commodity_index
|
||
|
||
logger.info("商品层 V_com: 综合指数=" + str(round(commodity_index,6)) +
|
||
" × 1SDR=$" + str(round(sdr_in_usd,4)) +
|
||
" = $" + str(round(v_com,6)) + " USD")
|
||
return v_com, commodity_index, detail
|
||
|
||
|
||
# ============================================================================
|
||
# 动态权重计算(VIX/DXY 作为调节杠杆)
|
||
# ============================================================================
|
||
|
||
def calc_dynamic_weights(risk_indicators):
|
||
"""
|
||
动态权重计算
|
||
|
||
VIX 和 DXY 作为权重调节杠杆,不参与价格计算
|
||
|
||
规则:
|
||
- VIX > 30:高风险,黄金权重上调(避险需求增加)
|
||
- VIX < 15:低风险,黄金权重下调
|
||
- DXY > 110:美元强势,货币层权重微减(美元强则其他货币弱)
|
||
- DXY < 95:美元弱势,货币层权重微增
|
||
|
||
约束:
|
||
- w_au ∈ [500, 2000](5% ~ 20%)
|
||
- w_fx ∈ [2000, 6000](20% ~ 60%)
|
||
- w_fx + w_au + w_com = 10000
|
||
"""
|
||
vix = risk_indicators.get('vix', 20.0)
|
||
dxy = risk_indicators.get('dxy', 104.0)
|
||
|
||
# 1. 黄金权重调节(VIX 杠杆)
|
||
if vix > 30:
|
||
# 高风险:每超过 30 增加 30bp,最高 2000bp
|
||
w_au = min(W_AU_MAX, W_AU_BASE + int((vix - 30) * 30))
|
||
elif vix < 15:
|
||
# 低风险:每低于 15 减少 25bp,最低 500bp
|
||
w_au = max(W_AU_MIN, W_AU_BASE - int((15 - vix) * 25))
|
||
else:
|
||
w_au = W_AU_BASE
|
||
|
||
# 2. 货币层权重调节(DXY 杠杆)
|
||
if dxy > 110:
|
||
fx_adj = -200 # 美元强势,货币层权重减 200bp
|
||
elif dxy < 95:
|
||
fx_adj = +200 # 美元弱势,货币层权重增 200bp
|
||
else:
|
||
# 线性插值:DXY 在 95-110 之间线性调整
|
||
fx_adj = int((104.0 - dxy) * 15)
|
||
|
||
w_fx = W_FX_BASE + fx_adj
|
||
w_fx = max(2000, min(6000, w_fx))
|
||
|
||
# 3. 商品层补足剩余权重
|
||
w_com = WEIGHT_SUM - w_fx - w_au
|
||
w_com = max(2000, min(7000, w_com))
|
||
|
||
# 4. 归一化(确保总和精确等于 10000)
|
||
total = w_fx + w_au + w_com
|
||
if total != WEIGHT_SUM:
|
||
w_com += (WEIGHT_SUM - total)
|
||
|
||
logger.info("动态权重 (VIX=" + str(round(vix,1)) + " DXY=" + str(round(dxy,1)) +
|
||
"): FX=" + str(w_fx/100) + "% AU=" + str(w_au/100) +
|
||
"% COM=" + str(w_com/100) + "%")
|
||
return w_fx, w_au, w_com
|
||
|
||
|
||
# ============================================================================
|
||
# XTZH AI 定价引擎(核心函数)
|
||
# ============================================================================
|
||
|
||
def calculate_xtzh_mint_price():
|
||
"""
|
||
计算 XTZH 当日铸造价格(统一价格)
|
||
|
||
公式:
|
||
P_XTZH (USD) = (w_fx × V_fx + w_au × V_au + w_com × V_com) / 10000
|
||
|
||
其中:
|
||
V_fx = 货币层价值(1 SDR 的 USD 等价值)
|
||
V_au = 黄金层价值(黄金 SDR 等价 × XTZH 黄金份额 × 覆盖率)
|
||
V_com = 商品层价值(大宗商品 SDR 等价加权均值)
|
||
w_fx, w_au, w_com = 动态权重(VIX/DXY 调节,总和 10000bp)
|
||
|
||
返回:统一铸造价格(USD/XTZH)及详细计算过程
|
||
"""
|
||
logger.info("=" * 60)
|
||
logger.info("开始计算 XTZH 当日铸造价格 v2.0")
|
||
logger.info("=" * 60)
|
||
|
||
# ── 步骤 1:获取实时数据 ──────────────────────────────────
|
||
sdr_rates = fetch_sdr_rates()
|
||
gold_price_usd, gold_source = fetch_gold_price()
|
||
commodity_prices = fetch_commodity_prices()
|
||
risk_indicators = fetch_risk_indicators()
|
||
|
||
# ── 步骤 2:计算三层价值 ──────────────────────────────────
|
||
logger.info("--- 计算三层价值 ---")
|
||
v_fx = calc_fx_layer(sdr_rates)
|
||
v_au = calc_gold_layer(gold_price_usd, sdr_rates)
|
||
v_com, commodity_index, commodity_detail = calc_commodity_layer(commodity_prices, sdr_rates)
|
||
|
||
# ── 步骤 3:计算动态权重 ──────────────────────────────────
|
||
logger.info("--- 计算动态权重 (VIX/DXY 杠杆) ---")
|
||
w_fx, w_au, w_com = calc_dynamic_weights(risk_indicators)
|
||
|
||
# ── 步骤 4:加权计算最终统一铸造价格 ─────────────────────
|
||
price_usd = (w_fx * v_fx + w_au * v_au + w_com * v_com) / WEIGHT_SUM
|
||
|
||
# ── 步骤 5:换算各货币价格 ────────────────────────────────
|
||
sdr_in_usd = sdr_rates['USD']
|
||
price_sdr = price_usd / sdr_in_usd # XTZH/SDR
|
||
price_cny = price_usd * sdr_rates['CNY'] / sdr_in_usd
|
||
price_eur = price_usd * sdr_rates['EUR'] / sdr_in_usd
|
||
price_gbp = price_usd * sdr_rates['GBP'] / sdr_in_usd
|
||
price_jpy = price_usd * sdr_rates['JPY'] / sdr_in_usd
|
||
|
||
# ── 步骤 6:铸造比例 ──────────────────────────────────────
|
||
mint_ratio_usd = 1.0 / price_usd # 1 USD 可铸造多少 XTZH
|
||
|
||
# ── 步骤 7:黄金储备支撑值 ───────────────────────────────
|
||
gold_backing_per_xtzh = gold_price_usd * XTZH_GOLD_SHARE_OZ * GOLD_TRUST_ANCHOR_COVERAGE
|
||
|
||
logger.info("=" * 60)
|
||
logger.info("XTZH 铸造价格计算完成")
|
||
logger.info(" 统一价格: $" + str(round(price_usd,6)) + " USD = " + str(round(price_sdr,6)) + " SDR")
|
||
logger.info(" 权重: FX=" + str(w_fx/100) + "% AU=" + str(w_au/100) + "% COM=" + str(w_com/100) + "%")
|
||
logger.info(" 三层价值: V_fx=$" + str(round(v_fx,4)) + " V_au=$" + str(round(v_au,4)) + " V_com=$" + str(round(v_com,4)))
|
||
logger.info("=" * 60)
|
||
|
||
return {
|
||
# ── 核心输出:统一铸造价格 ──
|
||
"price_usd": round(price_usd, 6), # 统一铸造价格(USD)
|
||
"price_sdr": round(price_sdr, 6), # SDR 等价
|
||
"price_cny": round(price_cny, 6), # 人民币等价
|
||
"price_eur": round(price_eur, 6), # 欧元等价
|
||
"price_gbp": round(price_gbp, 6), # 英镑等价
|
||
"price_jpy": round(price_jpy, 4), # 日元等价
|
||
|
||
# ── 铸造比例 ──
|
||
"mint_ratio_usd": round(mint_ratio_usd, 6), # 1 USD 可铸造 XTZH 数量
|
||
|
||
# ── 黄金储备支撑 ──
|
||
"gold_backing_usd": round(gold_backing_per_xtzh, 6), # 每 1 XTZH 的黄金支撑值(USD)
|
||
|
||
# ── 三层价值(透明计算过程) ──
|
||
"layers": {
|
||
"fx_layer": {
|
||
"value_usd": round(v_fx, 6),
|
||
"weight_bp": w_fx,
|
||
"weight_pct": round(w_fx / 100, 2),
|
||
"description": "货币层:1 SDR 的 USD 等价值",
|
||
"sdr_usd_rate": round(sdr_in_usd, 6)
|
||
},
|
||
"gold_layer": {
|
||
"value_usd": round(v_au, 6),
|
||
"weight_bp": w_au,
|
||
"weight_pct": round(w_au / 100, 2),
|
||
"description": "黄金层:黄金 SDR 等价 × XTZH 黄金份额 × 125% 覆盖率",
|
||
"gold_price_usd": round(gold_price_usd, 2),
|
||
"gold_source": gold_source,
|
||
"xtzh_gold_share_oz": XTZH_GOLD_SHARE_OZ,
|
||
"coverage_ratio": GOLD_TRUST_ANCHOR_COVERAGE
|
||
},
|
||
"commodity_layer": {
|
||
"value_usd": round(v_com, 6),
|
||
"weight_bp": w_com,
|
||
"weight_pct": round(w_com / 100, 2),
|
||
"description": "商品层:大宗商品 SDR 等价加权均值",
|
||
"commodity_index": round(commodity_index, 6),
|
||
"commodities": commodity_detail
|
||
}
|
||
},
|
||
|
||
# ── 权重调节杠杆(风险指标) ──
|
||
"weight_levers": {
|
||
"vix": round(risk_indicators.get('vix', 20.0), 2),
|
||
"dxy": round(risk_indicators.get('dxy', 104.0), 3),
|
||
"description": "VIX/DXY 仅作为动态权重调节杠杆,不参与价格计算"
|
||
},
|
||
|
||
# ── SDR 汇率 ──
|
||
"sdr_rates": {
|
||
"usd": round(sdr_rates['USD'], 6),
|
||
"eur": round(sdr_rates['EUR'], 6),
|
||
"cny": round(sdr_rates['CNY'], 6),
|
||
"gbp": round(sdr_rates['GBP'], 6),
|
||
"jpy": round(sdr_rates['JPY'], 4),
|
||
"updated": sdr_rates.get('updated', '')
|
||
},
|
||
|
||
# ── 模型信息 ──
|
||
"model_info": {
|
||
"version": "2.0.0",
|
||
"algorithm": "NAC Constitutional Weighted Pricing v2",
|
||
"formula": "P_XTZH = (w_fx×V_fx + w_au×V_au + w_com×V_com) / 10000",
|
||
"gold_trust_anchor_coverage": "125%",
|
||
"weight_constraint": "w_fx + w_au + w_com = 10000bp",
|
||
"gold_weight_range": "5.00% - 20.00%",
|
||
"commodity_basket": "WTI(30%) + Cu(20%) + Wheat(12%) + Corn(10%) + Soy(10%) + NG(10%) + Ag(8%)"
|
||
},
|
||
|
||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||
"next_update": "每5分钟更新一次"
|
||
}
|
||
|
||
|
||
# ============================================================================
|
||
# 缓存管理
|
||
# ============================================================================
|
||
|
||
def get_cached_price():
|
||
"""获取缓存的价格数据(5分钟 TTL)"""
|
||
now = time.time()
|
||
if _cache['data'] is None or (now - _cache['timestamp']) > _cache['ttl']:
|
||
logger.info("缓存过期,重新计算 XTZH 价格...")
|
||
try:
|
||
_cache['data'] = calculate_xtzh_mint_price()
|
||
_cache['timestamp'] = now
|
||
except Exception as e:
|
||
logger.error("价格计算失败: " + str(e))
|
||
if _cache['data'] is None:
|
||
# 返回基础备用值
|
||
_cache['data'] = {
|
||
"price_usd": 1.3745,
|
||
"price_sdr": 1.0,
|
||
"price_cny": 9.4448,
|
||
"price_eur": 1.1663,
|
||
"error": str(e),
|
||
"timestamp": datetime.now(timezone.utc).isoformat()
|
||
}
|
||
return _cache['data']
|
||
|
||
|
||
# ============================================================================
|
||
# HTTP 服务器
|
||
# ============================================================================
|
||
|
||
class XTZHPricingHandler(BaseHTTPRequestHandler):
|
||
def log_message(self, format, *args):
|
||
logger.info("HTTP " + str(args[0]) + " " + str(args[1]) + " " + str(args[2]))
|
||
|
||
def send_cors_headers(self):
|
||
self.send_header('Access-Control-Allow-Origin', '*')
|
||
self.send_header('Access-Control-Allow-Methods', 'GET, OPTIONS')
|
||
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||
self.send_header('Content-Type', 'application/json; charset=utf-8')
|
||
|
||
def do_OPTIONS(self):
|
||
self.send_response(200)
|
||
self.send_cors_headers()
|
||
self.end_headers()
|
||
|
||
def do_GET(self):
|
||
path = self.path.split('?')[0]
|
||
|
||
if path == '/health':
|
||
self.send_response(200)
|
||
self.send_cors_headers()
|
||
self.end_headers()
|
||
resp = json.dumps({
|
||
"status": "ok",
|
||
"service": "xtzh-pricing",
|
||
"version": "2.0.0",
|
||
"algorithm": "NAC Constitutional Weighted Pricing v2"
|
||
})
|
||
self.wfile.write(resp.encode())
|
||
|
||
elif path in ['/price', '/api/xtzh/price', '/api/v1/xtzh/price']:
|
||
data = get_cached_price()
|
||
self.send_response(200)
|
||
self.send_cors_headers()
|
||
self.end_headers()
|
||
resp = json.dumps({"success": True, "data": data}, ensure_ascii=False)
|
||
self.wfile.write(resp.encode('utf-8'))
|
||
|
||
elif path in ['/price/simple', '/api/xtzh/price/simple']:
|
||
# 简化版:只返回统一铸造价格和各货币等价
|
||
data = get_cached_price()
|
||
simple = {
|
||
"price_usd": data.get("price_usd"),
|
||
"price_sdr": data.get("price_sdr"),
|
||
"price_cny": data.get("price_cny"),
|
||
"price_eur": data.get("price_eur"),
|
||
"price_gbp": data.get("price_gbp"),
|
||
"price_jpy": data.get("price_jpy"),
|
||
"mint_ratio_usd": data.get("mint_ratio_usd"),
|
||
"gold_backing_usd": data.get("gold_backing_usd"),
|
||
"gold_price_usd": data.get("layers", {}).get("gold_layer", {}).get("gold_price_usd"),
|
||
"weights": {
|
||
"fx": data.get("layers", {}).get("fx_layer", {}).get("weight_pct"),
|
||
"au": data.get("layers", {}).get("gold_layer", {}).get("weight_pct"),
|
||
"com": data.get("layers", {}).get("commodity_layer", {}).get("weight_pct"),
|
||
},
|
||
"timestamp": data.get("timestamp")
|
||
}
|
||
self.send_response(200)
|
||
self.send_cors_headers()
|
||
self.end_headers()
|
||
resp = json.dumps({"success": True, "data": simple}, ensure_ascii=False)
|
||
self.wfile.write(resp.encode('utf-8'))
|
||
|
||
elif path in ['/price/refresh', '/api/xtzh/price/refresh']:
|
||
# 强制刷新缓存
|
||
_cache['timestamp'] = 0
|
||
data = get_cached_price()
|
||
self.send_response(200)
|
||
self.send_cors_headers()
|
||
self.end_headers()
|
||
resp = json.dumps({"success": True, "data": data, "refreshed": True}, ensure_ascii=False)
|
||
self.wfile.write(resp.encode('utf-8'))
|
||
|
||
else:
|
||
self.send_response(404)
|
||
self.send_cors_headers()
|
||
self.end_headers()
|
||
resp = json.dumps({
|
||
"error": "Not found",
|
||
"endpoints": ["/health", "/price", "/price/simple", "/price/refresh"]
|
||
})
|
||
self.wfile.write(resp.encode())
|
||
|
||
|
||
def run_server(port=9559):
|
||
"""启动 HTTP 服务器"""
|
||
server = HTTPServer(('0.0.0.0', port), XTZHPricingHandler)
|
||
logger.info("XTZH AI 定价服务 v2.0 启动,端口: " + str(port))
|
||
logger.info("API: http://0.0.0.0:" + str(port) + "/price")
|
||
logger.info("算法: NAC Constitutional Weighted Pricing v2")
|
||
logger.info("公式: P_XTZH = (w_fx×V_fx + w_au×V_au + w_com×V_com) / 10000")
|
||
|
||
# 预热缓存
|
||
logger.info("预热缓存,首次计算 XTZH 价格...")
|
||
get_cached_price()
|
||
|
||
server.serve_forever()
|
||
|
||
|
||
if __name__ == '__main__':
|
||
run_server(9559)
|