Real-Time Forex Charts
Professional real-time forex charting powered by Dukascopy's free tick data API and Lightweight Charts.
Features
✅ Real-Time Updates - Live candle updates every 1-5 seconds ✅ No API Key Required - Completely free forever ✅ 15+ Instruments - Forex, crypto, and commodities ✅ Multiple Timeframes - 1m, 5m, 15m, 1h, 4h, daily ✅ Interactive Tooltip - Hover to see OHLC data ✅ Auto-Scroll - "Go to Realtime" button for latest data ✅ Pause/Resume - Control live updates ✅ TypeScript - Full type safety
Component Usage
Basic Usage
import { RealtimeForexChart } from '@/components/dashboard/realtime-forex-chart';
export default function TradingPage() {
return (
<RealtimeForexChart
instrument="eurusd"
timeframe="m1"
height={600}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
instrument | string | "eurusd" | Forex pair to display |
timeframe | string | "m1" | Candle timeframe |
height | number | 500 | Chart height in pixels |
Supported Instruments
// Major Forex Pairs
"eurusd" | "gbpusd" | "usdjpy" | "audusd" | "usdcad" | "usdchf" | "nzdusd"
// Cross Pairs
"eurgbp" | "eurjpy" | "gbpjpy"
// Crypto
"btcusd" | "ethusd"
// Commodities
"xauusd" | "xagusd" | "wtiusd"Supported Timeframes
"m1" // 1 minute
"m5" // 5 minutes
"m15" // 15 minutes
"h1" // 1 hourHow It Works
Initial Data Load
When the component mounts, it fetches the last 500 candles:
const url = `/api/forex/realtime/${instrument}?timeframe=${timeframe}&last=500`
const response = await fetch(url)This provides historical context for the chart.
Real-Time Updates
The component polls for updates every 1-5 seconds (depending on timeframe):
const updateInterval = timeframe === 'm1' ? 1000 : 5000
setInterval(fetchRealtimeUpdate, updateInterval)Each update:
- Fetches the latest candle
- Compares timestamp with last candle
- Either updates existing candle or appends new candle
Candle Update Logic
if (newCandle.time === lastCandle.time) {
// Same time = update existing candle (price changed)
series.update(newCandle)
} else if (newCandle.time > lastCandle.time) {
// New time = append new candle (new period started)
series.update(newCandle)
lastCandle = newCandle
}This follows the Lightweight Charts real-time update pattern.
API Routes
Real-Time Endpoint
GET /api/forex/realtime/[instrument]Query Parameters:
timeframe- Candle period (m1, m5, m15, h1)last- Number of candles to fetch (default: 10)format- Data format (json, array, csv)
Response:
{
"success": true,
"data": [
{
"timestamp": 1704067200000,
"open": 1.0945,
"high": 1.0950,
"low": 1.0940,
"close": 1.0948,
"volume": 123.45
}
]
}Features Breakdown
Live Badge
Shows animated "LIVE" badge when updates are active:
{isLive && (
<Badge variant="default" className="animate-pulse">
<Activity className="w-3 h-3 mr-1" />
LIVE
</Badge>
)}Current Price Display
Shows real-time price updates:
{currentPrice !== null && (
<div className="text-2xl font-mono">
${currentPrice.toFixed(5)}
</div>
)}Go to Realtime Button
Scrolls chart to show latest data:
<Button onClick={() => chart.timeScale().scrollToRealTime()}>
Go to Realtime
</Button>Pause/Resume Controls
Toggle live updates:
const toggleLive = () => {
setIsLive(!isLive)
// Interval automatically starts/stops via useEffect
}Interactive Tooltip
Shows OHLC data on hover using our custom tooltip plugin:
const tooltipPrimitive = new TooltipPrimitive({
tooltip: {
title: instrument.toUpperCase(),
followMode: 'tracking',
},
priceExtractor: (data) => `$${data.close.toFixed(5)}`,
})
candlestickSeries.attachPrimitive(tooltipPrimitive)Advanced Usage
Custom Update Interval
const [updateInterval, setUpdateInterval] = useState(5000)
useEffect(() => {
if (isLive) {
const interval = setInterval(fetchUpdate, updateInterval)
return () => clearInterval(interval)
}
}, [isLive, updateInterval])Multiple Charts
Display multiple instruments side-by-side:
<div className="grid grid-cols-2 gap-4">
<RealtimeForexChart instrument="eurusd" />
<RealtimeForexChart instrument="gbpusd" />
<RealtimeForexChart instrument="usdjpy" />
<RealtimeForexChart instrument="btcusd" />
</div>With Tabs
Switch between instruments:
<Tabs defaultValue="eurusd">
<TabsList>
<TabsTrigger value="eurusd">EUR/USD</TabsTrigger>
<TabsTrigger value="btcusd">BTC/USD</TabsTrigger>
</TabsList>
<TabsContent value="eurusd">
<RealtimeForexChart instrument="eurusd" />
</TabsContent>
<TabsContent value="btcusd">
<RealtimeForexChart instrument="btcusd" />
</TabsContent>
</Tabs>Performance Considerations
Update Frequency
Lower timeframes update more frequently:
m1- Every 1 secondm5,m15,h1- Every 5 seconds
Adjust based on your needs:
const updateInterval = timeframe === 'm1' ? 1000 :
timeframe === 'm5' ? 5000 :
10000 // h1, h4Data Volume
Initial load fetches 500 candles. Adjust for performance:
// Lighter load
const url = `...&last=100` // Only 100 candles
// Heavier load for more context
const url = `...&last=1000` // 1000 candlesMemory Management
Cleanup on unmount:
useEffect(() => {
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current)
}
if (chartRef.current) {
chartRef.current.remove()
}
}
}, [])Troubleshooting
Chart not updating
- Check browser console for API errors
- Verify
isLivestate istrue - Check network tab for failed requests
- Ensure Dukascopy servers are accessible
Candles appearing disconnected
This means there's a gap in data (market closed, weekend, etc.). This is normal for forex markets.
High CPU usage
- Reduce update frequency
- Decrease number of initial candles
- Pause updates when chart is not visible
Related Files
- lib/forex/dukascopy-client.ts - Dukascopy API client
- lib/lightweight-charts/plugins/tooltip - Tooltip plugin
- app/api/forex/realtime/[instrument]/route.ts - API route
- app/forex/page.tsx - Example page
Credits
Built with:
- Lightweight Charts by TradingView
- Dukascopy Node for free forex data
- shadcn/ui for UI components