fix: market indicators JSONB parsing + fallback layer scores
This commit is contained in:
parent
340d8eb3a1
commit
9525b06710
@ -48,7 +48,7 @@ interface LatestIndicator {
|
||||
}
|
||||
|
||||
interface MarketIndicatorValue {
|
||||
value: number;
|
||||
value: Record<string, unknown>;
|
||||
ts: number;
|
||||
}
|
||||
|
||||
@ -115,32 +115,41 @@ function MarketIndicatorsCards({ symbol }: { symbol: Symbol }) {
|
||||
|
||||
if (!data) return <div className="text-center text-slate-400 text-sm py-3">等待市场指标数据...</div>;
|
||||
|
||||
const ls = Number(data.long_short_ratio?.value ?? 1);
|
||||
const top = Number(data.top_trader_position?.value ?? 0.5);
|
||||
const oi = Number(data.open_interest_hist?.value ?? 0);
|
||||
const premium = Number(data.coinbase_premium?.value ?? 0);
|
||||
// value是JSONB对象,需要取具体字段
|
||||
const lsVal = data.long_short_ratio?.value as Record<string, string> | undefined;
|
||||
const topVal = data.top_trader_position?.value as Record<string, string> | undefined;
|
||||
const oiVal = data.open_interest_hist?.value as Record<string, string> | undefined;
|
||||
const premVal = data.coinbase_premium?.value as Record<string, unknown> | undefined;
|
||||
|
||||
const longPct = Number(lsVal?.longAccount ?? 0.5) * 100;
|
||||
const shortPct = Number(lsVal?.shortAccount ?? 0.5) * 100;
|
||||
const topLong = Number(topVal?.longAccount ?? 0.5) * 100;
|
||||
const topShort = Number(topVal?.shortAccount ?? 0.5) * 100;
|
||||
const oiValue = Number(oiVal?.sumOpenInterestValue ?? 0);
|
||||
const oiDisplay = oiValue >= 1e9 ? `$${(oiValue / 1e9).toFixed(2)}B` : oiValue >= 1e6 ? `$${(oiValue / 1e6).toFixed(0)}M` : `$${oiValue.toFixed(0)}`;
|
||||
const premium = Number(premVal?.premium_pct ?? 0);
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-2">
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-3">
|
||||
<p className="text-xs text-slate-400">多空比 (L/S)</p>
|
||||
<p className="text-sm text-slate-800 mt-1">Long: {(ls / (1 + ls) * 100).toFixed(1)}%</p>
|
||||
<p className="text-sm text-slate-600">Short: {(100 - (ls / (1 + ls) * 100)).toFixed(1)}%</p>
|
||||
<p className="text-sm text-slate-800 mt-1">Long: {longPct.toFixed(1)}%</p>
|
||||
<p className="text-sm text-slate-600">Short: {shortPct.toFixed(1)}%</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-3">
|
||||
<p className="text-xs text-slate-400">大户持仓</p>
|
||||
<p className="text-sm text-slate-800 mt-1">大户做多: {(top * 100).toFixed(1)}%</p>
|
||||
<p className="text-sm text-slate-600">方向: {top >= 0.55 ? "多头占优" : top <= 0.45 ? "空头占优" : "中性"}</p>
|
||||
<p className="text-sm text-slate-800 mt-1">大户做多: {topLong.toFixed(1)}%</p>
|
||||
<p className="text-sm text-slate-600">方向: {topLong >= 55 ? "多头占优" : topLong <= 45 ? "空头占优" : "中性"}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-3">
|
||||
<p className="text-xs text-slate-400">OI变化</p>
|
||||
<p className="text-sm text-slate-800 mt-1">{pct(oi, 2)}</p>
|
||||
<p className="text-sm text-slate-600">活跃度: {oi >= 0.03 ? "高" : oi > 0 ? "中" : "低"}</p>
|
||||
<p className="text-xs text-slate-400">持仓量 (OI)</p>
|
||||
<p className="text-sm text-slate-800 mt-1">{oiDisplay}</p>
|
||||
<p className="text-sm text-slate-600">{Number(oiVal?.sumOpenInterest ?? 0).toFixed(0)} BTC</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-3">
|
||||
<p className="text-xs text-slate-400">Coinbase Premium</p>
|
||||
<p className={`text-sm mt-1 ${premium >= 0 ? "text-emerald-600" : "text-red-500"}`}>{premium >= 0 ? "+" : ""}{pct(premium, 2)}</p>
|
||||
<p className="text-sm text-slate-600">机构: {premium > 0.0005 ? "偏多" : premium < -0.0005 ? "偏空" : "中性"}</p>
|
||||
<p className={`text-sm mt-1 font-mono ${premium >= 0 ? "text-emerald-600" : "text-red-500"}`}>{premium >= 0 ? "+" : ""}{premium.toFixed(4)}%</p>
|
||||
<p className="text-sm text-slate-600">机构: {premium > 0.005 ? "偏多" : premium < -0.005 ? "偏空" : "中性"}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -262,11 +271,11 @@ function IndicatorCards({ symbol }: { symbol: Symbol }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 space-y-2">
|
||||
<LayerScore label="方向层" score={Number(data.factors?.direction?.score ?? 0)} max={45} colorClass="bg-blue-600" />
|
||||
<LayerScore label="拥挤层" score={Number(data.factors?.crowding?.score ?? 0)} max={20} colorClass="bg-violet-600" />
|
||||
<LayerScore label="环境层" score={Number(data.factors?.environment?.score ?? 0)} max={15} colorClass="bg-emerald-600" />
|
||||
<LayerScore label="确认层" score={Number(data.factors?.confirmation?.score ?? 0)} max={15} colorClass="bg-amber-500" />
|
||||
<LayerScore label="辅助层" score={Number(data.factors?.auxiliary?.score ?? 0)} max={5} colorClass="bg-slate-500" />
|
||||
<LayerScore label="方向层" score={data.factors?.direction?.score ?? Math.min(Math.round(data.score * 0.45), 45)} max={45} colorClass="bg-blue-600" />
|
||||
<LayerScore label="拥挤层" score={data.factors?.crowding?.score ?? Math.min(Math.round(data.score * 0.20), 20)} max={20} colorClass="bg-violet-600" />
|
||||
<LayerScore label="环境层" score={data.factors?.environment?.score ?? Math.min(Math.round(data.score * 0.15), 15)} max={15} colorClass="bg-emerald-600" />
|
||||
<LayerScore label="确认层" score={data.factors?.confirmation?.score ?? Math.min(Math.round(data.score * 0.15), 15)} max={15} colorClass="bg-amber-500" />
|
||||
<LayerScore label="辅助层" score={data.factors?.auxiliary?.score ?? Math.min(Math.round(data.score * 0.05), 5)} max={5} colorClass="bg-slate-500" />
|
||||
</div>
|
||||
{data.signal && (
|
||||
<div className="mt-2 grid grid-cols-3 gap-2 text-xs text-slate-600">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user