Skip to content

Commit f7b5575

Browse files
committed
feat: expand commodity list with safety checks and sync build
1 parent bf66865 commit f7b5575

4 files changed

Lines changed: 46 additions & 35 deletions

File tree

commodity-crisis/src/App.tsx

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,28 @@ const LEVERAGE = 10;
77
const TICK_MS = 1000;
88
const NEWS_INTERVAL_TICKS = 15;
99

10-
type AssetType = 'OIL' | 'TBOND';
10+
type AssetType = 'OIL' | 'GOLD' | 'SILVER' | 'BR' | 'RU' | 'MA' | 'UR' | 'TBOND';
1111
type ViewMode = 'auto' | 'mobile' | 'desktop';
1212

1313
const ASSET_CONFIG: Record<AssetType, { name: string, basePrice: number, minPrice: number, maxPrice: number, vol: number, icon: string }> = {
1414
OIL: { name: '原油', basePrice: 75, minPrice: 20, maxPrice: 150, vol: 0.0025, icon: '🛢️' },
15+
GOLD: { name: '黄金', basePrice: 2000, minPrice: 1200, maxPrice: 3000, vol: 0.00012, icon: '✨' },
16+
SILVER: { name: '白银', basePrice: 30, minPrice: 15, maxPrice: 60, vol: 0.0015, icon: '🥈' },
17+
BR: { name: '合成胶', basePrice: 12000, minPrice: 8000, maxPrice: 20000, vol: 0.002, icon: '🧪' },
18+
RU: { name: '天然胶', basePrice: 13000, minPrice: 8000, maxPrice: 25000, vol: 0.002, icon: '🌲' },
19+
MA: { name: '甲醇', basePrice: 2500, minPrice: 1500, maxPrice: 4000, vol: 0.002, icon: '🔥' },
20+
UR: { name: '尿素', basePrice: 2200, minPrice: 1500, maxPrice: 3500, vol: 0.0018, icon: '🌾' },
1521
TBOND: { name: '国债', basePrice: 100, minPrice: 80, maxPrice: 120, vol: 0.0005, icon: '📜' },
1622
};
1723

1824
const NEWS_POOL = [
19-
{ text: "中东局势升级,供应担忧缓解。", impact: { OIL: 0.012, TBOND: -0.001 } },
20-
{ text: "美联储维持现状,暗示政策转向仍需时日。", impact: { OIL: -0.004, TBOND: -0.004 } },
21-
{ text: "避险需求回落,市场情绪回暖。", impact: { OIL: 0.002, TBOND: -0.002 } },
22-
{ text: "原油库存超预期增加,需求端疲软。", impact: { OIL: -0.015, TBOND: 0.001 } },
23-
{ text: "地缘政治溢价消退,油价震荡回调。", impact: { OIL: -0.008, TBOND: 0 } },
25+
{ text: "中东局势升级,原油供应担忧加剧。", impact: { OIL: 0.015, GOLD: 0.005, SILVER: 0.003, BR: 0.002 } as any },
26+
{ text: "美联储维持利率不变,贵金属高位震荡。", impact: { GOLD: -0.002, SILVER: -0.004, TBOND: -0.001 } as any },
27+
{ text: "主产区降雨充沛,天然橡胶供应前景改善。", impact: { RU: -0.012, BR: -0.005 } as any },
28+
{ text: "甲醇港口库存大幅下降,现货挺价意愿强。", impact: { MA: 0.01, UR: 0.003 } as any },
29+
{ text: "化肥出口政策收紧,国内尿素供应增加。", impact: { UR: -0.015, MA: -0.002 } as any },
30+
{ text: "丁二烯价格走高,合成胶成本支撑转强。", impact: { BR: 0.008, RU: 0.003 } as any },
31+
{ text: "全球避险情绪回落,国债收益率小幅回升。", impact: { GOLD: -0.005, SILVER: -0.008, TBOND: -0.003 } as any },
2432
];
2533

2634
const App: React.FC = () => {
@@ -33,16 +41,14 @@ const App: React.FC = () => {
3341
const [isLiquated, setIsLiquated] = useState(false);
3442
const [utilizationRate, setUtilizationRate] = useState(0.8);
3543

36-
const pricesRef = useRef<Record<AssetType, number>>({
37-
OIL: ASSET_CONFIG.OIL.basePrice,
38-
TBOND: ASSET_CONFIG.TBOND.basePrice
39-
});
44+
const pricesRef = useRef<Record<AssetType, number>>(
45+
Object.fromEntries(Object.entries(ASSET_CONFIG).map(([k, v]) => [k, v.basePrice])) as any
46+
);
4047
const [prices, setPrices] = useState<Record<AssetType, number>>(pricesRef.current);
4148

42-
const [priceHistory, setPriceHistory] = useState<Record<AssetType, number[]>>({
43-
OIL: [ASSET_CONFIG.OIL.basePrice],
44-
TBOND: [ASSET_CONFIG.TBOND.basePrice]
45-
});
49+
const [priceHistory, setPriceHistory] = useState<Record<AssetType, number[]>>(
50+
Object.fromEntries(Object.entries(ASSET_CONFIG).map(([k, v]) => [k, [v.basePrice]])) as any
51+
);
4652

4753
const [user, setUser] = useState<{username: string, token: string} | null>(null);
4854
const [showAuthModal, setShowAuthModal] = useState(false);
@@ -51,11 +57,15 @@ const App: React.FC = () => {
5157
const [authForm, setAuthForm] = useState({ username: '', password: '', isLogin: true });
5258
const [authMsg, setAuthMsg] = useState('');
5359

54-
const activeImpactsRef = useRef<Record<AssetType, number>>({ OIL: 0, TBOND: 0 });
55-
const lastDirectionsRef = useRef<Record<AssetType, number>>({ OIL: 0, TBOND: 0 });
60+
const activeImpactsRef = useRef<Record<AssetType, number>>(
61+
Object.fromEntries(Object.keys(ASSET_CONFIG).map(k => [k, 0])) as any
62+
);
63+
const lastDirectionsRef = useRef<Record<AssetType, number>>(
64+
Object.fromEntries(Object.keys(ASSET_CONFIG).map(k => [k, 0])) as any
65+
);
5666

5767
useEffect(() => {
58-
console.log("期货风云核心逻辑版本: 3.1 - 精简版 (OIL & TBOND)");
68+
console.log("期货风云核心逻辑版本: 3.2 - 多品种扩充版");
5969
const saved = localStorage.getItem('cc_user');
6070
if (saved) {
6171
const parsed = JSON.parse(saved);
@@ -146,7 +156,7 @@ const App: React.FC = () => {
146156
const randomNoise = (Math.random() - 0.5) * 2.5 * config.vol;
147157
const momentum = lastDirectionsRef.current[asset] * 0.3 * config.vol;
148158
const reversion = ((config.basePrice - prevP) / config.basePrice) * 0.002;
149-
const newsPower = activeImpactsRef.current[asset] * (0.7 + Math.random() * 0.6);
159+
const newsPower = (activeImpactsRef.current[asset] || 0) * (0.7 + Math.random() * 0.6);
150160

151161
let delta = randomNoise + momentum + reversion + newsPower;
152162
delta = Math.max(-0.005, Math.min(0.005, delta));
@@ -166,7 +176,7 @@ const App: React.FC = () => {
166176
setPriceHistory(prevH => {
167177
const nextH = { ...prevH };
168178
(Object.keys(ASSET_CONFIG) as AssetType[]).forEach(asset => {
169-
nextH[asset] = [...prevH[asset].slice(-49), newPrices[asset]];
179+
nextH[asset] = [...(prevH[asset] || [ASSET_CONFIG[asset].basePrice]).slice(-49), newPrices[asset]];
170180
});
171181
return nextH;
172182
});
@@ -178,10 +188,11 @@ const App: React.FC = () => {
178188
const newEvent = { ...rawNews, id: Date.now(), type };
179189
setNews(prevN => [newEvent, ...prevN].slice(0, 5));
180190

181-
(Object.keys(newEvent.impact) as AssetType[]).forEach(asset => {
191+
(Object.keys(ASSET_CONFIG) as AssetType[]).forEach(asset => {
192+
let impact = (newEvent.impact && (newEvent.impact as any)[asset]) || 0;
182193
let factor = type === 'INVERSE' ? -0.7 : (type === 'NONE' ? 0.05 : 1);
183194
setTimeout(() => {
184-
activeImpactsRef.current[asset] += (newEvent.impact[asset as AssetType] * factor) / 3;
195+
activeImpactsRef.current[asset] += (impact * factor) / 3;
185196
}, Math.random() * 2000);
186197
});
187198
}
@@ -201,7 +212,7 @@ const App: React.FC = () => {
201212

202213
const priceChangePercent = useMemo(() => {
203214
const current = prices[activeAsset];
204-
const initial = priceHistory[activeAsset][0] || ASSET_CONFIG[activeAsset].basePrice;
215+
const initial = (priceHistory[activeAsset] ? priceHistory[activeAsset][0] : ASSET_CONFIG[activeAsset].basePrice) || ASSET_CONFIG[activeAsset].basePrice;
205216
const pc = ((current / initial - 1) * 100);
206217
return isFinite(pc) ? pc.toFixed(2) : "0.00";
207218
}, [prices, priceHistory, activeAsset]);
@@ -264,15 +275,15 @@ const App: React.FC = () => {
264275
</div>
265276
<div className="chart-price-box">
266277
<span className="current-price">{prices[activeAsset].toLocaleString(undefined, {minimumFractionDigits: 2})}</span>
267-
<span className={`price-change ${prices[activeAsset] >= priceHistory[activeAsset][0] ? 'up' : 'down'}`}>
278+
<span className={`price-change ${prices[activeAsset] >= (priceHistory[activeAsset] ? priceHistory[activeAsset][0] : prices[activeAsset]) ? 'up' : 'down'}`}>
268279
{priceChangePercent}%
269280
</span>
270281
</div>
271282
</div>
272283
<div className="chart-container mini-chart">
273284
<svg width="100%" height="100%" viewBox="0 0 500 300" preserveAspectRatio="none">
274-
<polyline fill="none" stroke={priceHistory[activeAsset][priceHistory[activeAsset].length-1] >= priceHistory[activeAsset][0] ? "#26a69a" : "#ef5350"} strokeWidth="2"
275-
points={priceHistory[activeAsset].map((p, i) => `${(i / (priceHistory[activeAsset].length - 1)) * 500},${300 - ((p - (Math.min(...priceHistory[activeAsset])*0.995)) / (Math.max(...priceHistory[activeAsset])*1.005 - Math.min(...priceHistory[activeAsset])*0.995 + 0.001)) * 300}`).join(' ')} />
285+
<polyline fill="none" stroke={(priceHistory[activeAsset] && priceHistory[activeAsset][priceHistory[activeAsset].length-1] >= priceHistory[activeAsset][0]) ? "#26a69a" : "#ef5350"} strokeWidth="2"
286+
points={(priceHistory[activeAsset] || [prices[activeAsset]]).map((p, i) => `${(i / (Math.max(1, (priceHistory[activeAsset] ? priceHistory[activeAsset].length : 1) - 1))) * 500},${300 - ((p - (Math.min(...(priceHistory[activeAsset] || [p]))*0.995)) / (Math.max(...(priceHistory[activeAsset] || [p]))*1.005 - Math.min(...(priceHistory[activeAsset] || [p]))*0.995 + 0.001)) * 300}`).join(' ')} />
276287
</svg>
277288
</div>
278289
</div>

0 commit comments

Comments
 (0)