@@ -7,20 +7,28 @@ const LEVERAGE = 10;
77const TICK_MS = 1000 ;
88const NEWS_INTERVAL_TICKS = 15 ;
99
10- type AssetType = 'OIL' | 'TBOND' ;
10+ type AssetType = 'OIL' | 'GOLD' | 'SILVER' | 'BR' | 'RU' | 'MA' | 'UR' | ' TBOND';
1111type ViewMode = 'auto' | 'mobile' | 'desktop' ;
1212
1313const 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
1824const 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
2634const 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