funhi-chart-library
v1.0.1
Published
Professional multi-timeframe trading chart library with real-time updates and drawing tools
Maintainers
Readme
@funhi/chart-library
Professional multi-timeframe trading chart library for React applications. Built with TypeScript and powered by Lightweight Charts.
Features
✨ Multi-Timeframe Support
- 1m, 5m, 15m, 1h, 4h, 1d timeframes
- Automatic aggregation from 1-minute candles
- Smooth timeframe switching
📊 Professional Charting
- Candlestick charts with volume
- Real-time price updates
- Gap filling for continuous display
- Customizable appearance
🎨 Customizable
- Light and dark themes
- Custom watermarks
- Configurable timeframes
- Flexible styling
⚡ Performance Optimized
- Efficient candle aggregation
- Smart data loading
- Minimal re-renders
- Hardware-accelerated rendering
🔌 Easy Integration
- Simple API
- TypeScript support
- Flexible data providers
- No vendor lock-in
Installation
npm install @funhi/chart-libraryor
yarn add @funhi/chart-libraryor
pnpm add @funhi/chart-libraryQuick Start
import { FunhiTradingChart } from '@funhi/chart-library';
import type { CandleData } from '@funhi/chart-library';
function App() {
const [marketCap, setMarketCap] = useState(50000);
// Provide candle data
const handleLoadCandles = async (mint: string, timeFrame: number, limit: number): Promise<CandleData[]> => {
const response = await fetch(`/api/candles/${mint}?timeFrame=${timeFrame}&limit=${limit}`);
const data = await response.json();
return data.candles;
};
// Save candles (optional)
const handleSaveCandles = (mint: string, candles: CandleData[], timeFrame: number) => {
fetch(`/api/candles/${mint}`, {
method: 'POST',
body: JSON.stringify({ candles, timeFrame })
});
};
return (
<FunhiTradingChart
mint="7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
ticker="FUNHI"
tokenName="Funhi Token"
realtimeMarketCap={marketCap}
onLoadCandles={handleLoadCandles}
onSaveCandles={handleSaveCandles}
height={500}
/>
);
}API Reference
Props
Required Props
| Prop | Type | Description |
|------|------|-------------|
| mint | string | Token mint address (identifier) |
Optional Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| ticker | string | - | Token ticker symbol (e.g., "FUNHI") |
| tokenName | string | - | Full token name |
| height | number | 384 | Chart height in pixels |
| width | string | '100%' | Chart width (CSS value) |
| realtimeMarketCap | number | - | Real-time market cap for live updates |
| isConnected | boolean | true | Connection status indicator |
| theme | 'light' \| 'dark' | 'light' | Chart theme |
| showWatermark | boolean | true | Show token pair watermark |
| showTimeframes | boolean | true | Show timeframe selector |
| showStatus | boolean | true | Show connection status |
| className | string | '' | Additional CSS classes for chart |
| containerClassName | string | '' | Additional CSS classes for container |
Callback Props
| Prop | Type | Description |
|------|------|-------------|
| onLoadCandles | (mint: string, timeFrame: number, limit: number) => Promise<CandleData[]> | Load historical candles |
| onSaveCandles | (mint: string, candles: CandleData[], timeFrame: number) => void | Save candles to storage |
| onMarketCapUpdate | (marketCap: number) => void | Called when market cap updates |
Advanced Props
| Prop | Type | Description |
|------|------|-------------|
| availableTimeframes | TimeFrame[] | Custom timeframe configuration |
Types
interface CandleData {
time: number; // Unix timestamp
open: number; // Opening price
high: number; // Highest price
low: number; // Lowest price
close: number; // Closing price
volume: number; // Trading volume
}
interface TimeFrame {
label: string; // Display label (e.g., "5m")
value: string; // Value identifier
seconds: number; // Duration in seconds
}Examples
Basic Usage
import { FunhiTradingChart } from '@funhi/chart-library';
<FunhiTradingChart
mint="7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
ticker="FUNHI"
/>With Real-Time Updates
import { FunhiTradingChart } from '@funhi/chart-library';
import { useWebSocket } from './hooks/useWebSocket';
function TokenChart({ mint, ticker }: { mint: string; ticker: string }) {
const { marketCap, isConnected } = useWebSocket(mint);
return (
<FunhiTradingChart
mint={mint}
ticker={ticker}
realtimeMarketCap={marketCap}
isConnected={isConnected}
/>
);
}With Custom Data Provider
import { FunhiTradingChart } from '@funhi/chart-library';
import axios from 'axios';
function CustomChart() {
const loadCandles = async (mint: string, timeFrame: number, limit: number) => {
const { data } = await axios.get(`https://api.example.com/ohlcv/${mint}`, {
params: { timeFrame, limit }
});
return data.candles;
};
const saveCandles = (mint: string, candles: CandleData[], timeFrame: number) => {
axios.post(`https://api.example.com/ohlcv/${mint}`, {
candles,
timeFrame
});
};
return (
<FunhiTradingChart
mint="ABC123..."
ticker="TOKEN"
onLoadCandles={loadCandles}
onSaveCandles={saveCandles}
/>
);
}Dark Theme
<FunhiTradingChart
mint="ABC123..."
ticker="DARK"
theme="dark"
height={600}
/>Custom Timeframes
const customTimeframes = [
{ label: '1m', value: '1m', seconds: 60 },
{ label: '5m', value: '5m', seconds: 300 },
{ label: '1h', value: '1h', seconds: 3600 }
];
<FunhiTradingChart
mint="ABC123..."
ticker="CUSTOM"
availableTimeframes={customTimeframes}
/>Minimal Configuration
// Just the chart, no controls
<FunhiTradingChart
mint="ABC123..."
showTimeframes={false}
showStatus={false}
showWatermark={false}
height={300}
/>Data Flow
1-Minute Candles as Source of Truth
The library uses 1-minute candles as the single source of truth:
Real-time Price Update
↓
Create/Update 1-Minute Candle
↓
Store in oneMinuteCandles array
↓
Auto-aggregate to current timeframe (5m, 1h, etc.)
↓
Display aggregated candlesTimeframe Switching
User clicks "1h" button
↓
Calculate required 1m candles (200 hours × 60 minutes = 12,000)
↓
Load 1m candles via onLoadCandles callback
↓
Aggregate to 1-hour candles
↓
Display 200 1-hour candlesBackend Integration
Required Backend Endpoint
Your backend should provide an endpoint to fetch candles:
// GET /api/ohlcv/candles/:mint?timeFrame=60&limit=200
{
"success": true,
"data": {
"mint": "ABC123...",
"timeFrame": 60,
"candles": [
{
"time": 1697740800,
"open": 50000,
"high": 51000,
"low": 49000,
"close": 50500,
"volume": 1000
}
// ... more candles
],
"count": 200
}
}Database Schema
Store only 1-minute candles in your database:
CREATE TABLE ohlcv (
id SERIAL PRIMARY KEY,
mint VARCHAR(44) NOT NULL,
timestamp INTEGER NOT NULL,
open DECIMAL(20, 8) NOT NULL,
high DECIMAL(20, 8) NOT NULL,
low DECIMAL(20, 8) NOT NULL,
close DECIMAL(20, 8) NOT NULL,
volume DECIMAL(20, 8) DEFAULT 0,
timeFrame INTEGER NOT NULL DEFAULT 60,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(mint, timestamp, timeFrame)
);
CREATE INDEX idx_ohlcv_mint_timeframe ON ohlcv(mint, timeFrame, timestamp DESC);The library will aggregate 1m candles client-side for other timeframes.
Real-Time Updates
Using WebSocket
import { FunhiTradingChart } from '@funhi/chart-library';
import { useEffect, useState } from 'react';
import io from 'socket.io-client';
function RealtimeChart({ mint, ticker }: { mint: string; ticker: string }) {
const [marketCap, setMarketCap] = useState(0);
const [connected, setConnected] = useState(false);
useEffect(() => {
const socket = io('wss://your-api.com');
socket.on('connect', () => setConnected(true));
socket.on('disconnect', () => setConnected(false));
socket.on('chartUpdate', (data: any) => {
if (data.mint === mint) {
setMarketCap(data.marketCap);
}
});
socket.emit('subscribe', { mint });
return () => {
socket.emit('unsubscribe', { mint });
socket.disconnect();
};
}, [mint]);
return (
<FunhiTradingChart
mint={mint}
ticker={ticker}
realtimeMarketCap={marketCap}
isConnected={connected}
/>
);
}Using REST API Polling
import { FunhiTradingChart } from '@funhi/chart-library';
import { useEffect, useState } from 'react';
function PollingChart({ mint, ticker }: { mint: string; ticker: string }) {
const [marketCap, setMarketCap] = useState(0);
useEffect(() => {
const fetchPrice = async () => {
const response = await fetch(`/api/price/${mint}`);
const data = await response.json();
setMarketCap(data.marketCap);
};
// Poll every 10 seconds
const interval = setInterval(fetchPrice, 10000);
fetchPrice(); // Initial fetch
return () => clearInterval(interval);
}, [mint]);
return (
<FunhiTradingChart
mint={mint}
ticker={ticker}
realtimeMarketCap={marketCap}
/>
);
}Styling
Using Tailwind CSS
The chart uses Tailwind CSS classes. Make sure Tailwind is configured in your project:
// Your component
<div className="max-w-7xl mx-auto p-4">
<FunhiTradingChart
mint="ABC123..."
ticker="TOKEN"
containerClassName="shadow-lg rounded-lg"
/>
</div>Custom Styling
<FunhiTradingChart
mint="ABC123..."
ticker="TOKEN"
className="border-2 border-blue-500"
containerClassName="bg-gray-100 p-4 rounded-xl"
height={600}
width="90%"
/>Advanced Usage
Custom Data Aggregation
import { candleAggregation, CandleData } from '@funhi/chart-library';
// Aggregate 1m candles to 5m manually
const oneMinuteCandles: CandleData[] = [...]; // Your 1m data
const fiveMinuteCandles = candleAggregation.aggregateCandles(oneMinuteCandles, 300);
// Fill gaps
const filledCandles = candleAggregation.fillGaps(fiveMinuteCandles, 300);Validation
import { candleAggregation } from '@funhi/chart-library';
const isValid = candleAggregation.isValidCandle({
time: 1697740800,
open: 50000,
high: 51000,
low: 49000,
close: 50500,
volume: 1000
});Browser Support
- Chrome/Edge: Latest 2 versions
- Firefox: Latest 2 versions
- Safari: Latest 2 versions
- Mobile browsers: iOS Safari 14+, Chrome Android
Performance
Metrics
- Initial Load: < 200ms
- Timeframe Switch: < 100ms (cached data)
- Real-time Update: < 16ms (60 FPS)
- Memory Usage: ~5-25 MB depending on timeframe and data
Optimization Tips
Limit Historical Data: Don't load more than needed
// Good: Load only what's necessary onLoadCandles={(mint, timeFrame, limit) => fetchCandles(mint, timeFrame, Math.min(limit, 500))}Debounce Real-time Updates: For high-frequency data
const debouncedMarketCap = useMemo(() => debounce(marketCap, 1000), [marketCap]);Lazy Load: Use dynamic imports
const FunhiChart = dynamic(() => import('@funhi/chart-library'), { ssr: false });
TypeScript
The library is written in TypeScript and provides full type definitions:
import type {
CandleData,
TimeFrame,
FunhiTradingChartProps
} from '@funhi/chart-library';
const MyComponent: React.FC = () => {
const candles: CandleData[] = [];
const timeframes: TimeFrame[] = [];
// Full type safety
return <FunhiTradingChart mint="..." />;
};Troubleshooting
Chart not displaying
Issue: Chart container is not visible
Solution: Ensure parent container has defined dimensions
<div style={{ width: '100%', height: '500px' }}>
<FunhiTradingChart mint="..." />
</div>No data showing
Issue: Chart shows "No market data available"
Solutions:
- Verify
onLoadCandlescallback is provided and returns data - Check that
realtimeMarketCapis updating - Inspect browser console for errors
Timeframe switching not working
Issue: All timeframes show same number of candles
Solution: Ensure onLoadCandles returns enough 1-minute candles:
- 5m timeframe: ~1,000 1m candles needed for 200 5m candles
- 1h timeframe: ~12,000 1m candles needed for 200 1h candles
Contributing
We welcome contributions! Please see our Contributing Guide.
License
MIT © Funhi Team
Support
- Documentation: https://github.com/funhi/chart-library
- Issues: https://github.com/funhi/chart-library/issues
- Discord: https://discord.gg/funhi
Changelog
v1.0.0 (2025-10-18)
- Initial release
- Multi-timeframe support (1m, 5m, 15m, 1h, 4h, 1d)
- Real-time market cap updates
- Light and dark themes
- Watermark support
- Automatic candle aggregation
- Gap filling for continuous display
- TypeScript support
Credits
Built with:
- Lightweight Charts by TradingView
- React
- TypeScript
Made with ❤️ by the Funhi Team
