Spaces:
Running
Running
File size: 4,745 Bytes
3be03dd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | import {
ResponsiveContainer,
ComposedChart,
Line,
XAxis,
YAxis,
Tooltip,
ReferenceLine,
CartesianGrid,
} from 'recharts'
function CustomTooltip({ active, payload, label }) {
if (!active || !payload?.length) return null
const price = payload.find(p => p.dataKey === 'price')
return (
<div style={{
background: 'rgba(15,15,30,0.95)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 8,
padding: '0.5rem 0.75rem',
fontSize: '0.78rem',
}}>
<div style={{ color: 'var(--text-muted)', marginBottom: 4 }}>{label}</div>
{price && (
<div style={{ color: 'var(--text)', fontWeight: 600 }}>
${Number(price.value).toFixed(2)}
</div>
)}
</div>
)
}
export default function PriceChart({ data }) {
const { price_series, ma20, ma50, bb_upper, bb_lower, current_price } = data
if (!price_series || Object.keys(price_series).length === 0) return null
const chartData = Object.entries(price_series).map(([date, price]) => ({
date: date.slice(5),
price: Number(price),
ma20: ma20 ? Number(ma20) : undefined,
ma50: ma50 ? Number(ma50) : undefined,
bbUpper: bb_upper ? Number(bb_upper) : undefined,
bbLower: bb_lower ? Number(bb_lower) : undefined,
}))
const prices = chartData.map(d => d.price)
const lastPrice = prices[prices.length - 1]
const isUp = lastPrice >= prices[0]
const lineColor = isUp ? 'var(--buy)' : 'var(--sell)'
const allValues = [
...prices,
bb_upper ? Number(bb_upper) : null,
bb_lower ? Number(bb_lower) : null,
].filter(Boolean)
const minY = Math.min(...allValues) * 0.995
const maxY = Math.max(...allValues) * 1.005
const change = lastPrice - prices[0]
const changePct = ((change / prices[0]) * 100).toFixed(2)
return (
<div className="card price-chart-card">
<div className="price-chart-header">
<h3>30-Day Price</h3>
<span className="price-chart-change" style={{ color: lineColor }}>
{change >= 0 ? '+' : ''}{changePct}%
</span>
</div>
<div style={{ width: '100%', height: 200 }}>
<ResponsiveContainer>
<ComposedChart data={chartData} margin={{ top: 4, right: 4, left: 0, bottom: 0 }}>
<CartesianGrid stroke="rgba(255,255,255,0.04)" vertical={false} />
<XAxis
dataKey="date"
tick={{ fill: 'var(--text-faint)', fontSize: 10 }}
tickLine={false}
axisLine={false}
interval="preserveStartEnd"
/>
<YAxis
domain={[minY, maxY]}
tick={{ fill: 'var(--text-faint)', fontSize: 10 }}
tickLine={false}
axisLine={false}
tickFormatter={v => `$${v.toFixed(0)}`}
width={50}
/>
<Tooltip content={<CustomTooltip />} />
{/* Bollinger band lines */}
{chartData[0]?.bbUpper && (
<Line dataKey="bbUpper" stroke="rgba(255,255,255,0.08)" strokeWidth={1}
dot={false} isAnimationActive={false} strokeDasharray="2 4" />
)}
{chartData[0]?.bbLower && (
<Line dataKey="bbLower" stroke="rgba(255,255,255,0.08)" strokeWidth={1}
dot={false} isAnimationActive={false} strokeDasharray="2 4" />
)}
{/* MA50 */}
{ma50 && (
<Line dataKey="ma50" stroke="rgba(255,204,68,0.55)" strokeWidth={1.5}
dot={false} isAnimationActive={false} strokeDasharray="4 2" />
)}
{/* MA20 */}
{ma20 && (
<Line dataKey="ma20" stroke="rgba(68,170,255,0.65)" strokeWidth={1.5}
dot={false} isAnimationActive={false} strokeDasharray="4 2" />
)}
{/* Price */}
<Line dataKey="price" stroke={lineColor} strokeWidth={2}
dot={false} activeDot={{ r: 4, fill: lineColor, stroke: 'none' }}
isAnimationActive={false} />
{/* Current price dashed line */}
<ReferenceLine y={lastPrice} stroke={lineColor}
strokeOpacity={0.25} strokeDasharray="2 5" />
</ComposedChart>
</ResponsiveContainer>
</div>
<div className="price-chart-legend">
<span style={{ color: lineColor }}>— Price</span>
{ma20 && <span style={{ color: 'rgba(68,170,255,0.65)' }}>-- MA20</span>}
{ma50 && <span style={{ color: 'rgba(255,204,68,0.55)' }}>-- MA50</span>}
{bb_upper && <span style={{ color: 'rgba(255,255,255,0.3)' }}>-- Bollinger</span>}
</div>
</div>
)
}
|