Implement working canvas-based trading chart
- Created SimpleTradingChart component using HTML5 Canvas - Renders proper candlestick chart with sample SOL/USDC data - Includes grid lines, price labels, and proper styling - Replaced problematic lightweight-charts with working solution - Updated trading page to use the new working chart component Fixes chart loading issues by using native HTML5 Canvas instead of external library dependencies.
This commit is contained in:
11
.github/instructions/double_check.instructions.md
vendored
Normal file
11
.github/instructions/double_check.instructions.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
applyTo: '**'
|
||||||
|
---
|
||||||
|
whenever you make changes to the code, please ensure to double-check everything to avoid any issues. This includes:
|
||||||
|
- Reviewing the code for syntax errors
|
||||||
|
- Testing the functionality to ensure it works as expected
|
||||||
|
- Checking for any potential security vulnerabilities
|
||||||
|
- Verifying that all dependencies are up to date
|
||||||
|
- Ensuring that the code adheres to the project's coding standards
|
||||||
|
- Running any automated tests to confirm that existing features are not broken
|
||||||
|
- Documenting any changes made for future reference
|
||||||
12
app/canvas-chart/page.tsx
Normal file
12
app/canvas-chart/page.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
'use client'
|
||||||
|
import React from 'react'
|
||||||
|
import SimpleTradingChart from '../../components/SimpleTradingChart'
|
||||||
|
|
||||||
|
export default function SimpleChartPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gray-900 p-8">
|
||||||
|
<h1 className="text-white text-3xl mb-6">Simple Canvas Chart Test</h1>
|
||||||
|
<SimpleTradingChart symbol="SOL/USDC" positions={[]} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import TradeExecutionPanel from '../../components/TradeExecutionPanel.js'
|
|||||||
import PositionsPanel from '../../components/PositionsPanel.js'
|
import PositionsPanel from '../../components/PositionsPanel.js'
|
||||||
import PendingOrdersPanel from '../../components/PendingOrdersPanel.js'
|
import PendingOrdersPanel from '../../components/PendingOrdersPanel.js'
|
||||||
import TradesHistoryPanel from '../../components/TradesHistoryPanel.js'
|
import TradesHistoryPanel from '../../components/TradesHistoryPanel.js'
|
||||||
import TradingChart from '../../components/TradingChart'
|
import SimpleTradingChart from '../../components/SimpleTradingChart'
|
||||||
|
|
||||||
export default function TradingPage() {
|
export default function TradingPage() {
|
||||||
return (
|
return (
|
||||||
@@ -22,7 +22,7 @@ export default function TradingPage() {
|
|||||||
<span>1D</span>
|
<span>1D</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TradingChart symbol="SOL/USDC" positions={[]} />
|
<SimpleTradingChart symbol="SOL/USDC" positions={[]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
|
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
|
||||||
|
|||||||
178
components/SimpleTradingChart.tsx
Normal file
178
components/SimpleTradingChart.tsx
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
interface SimpleCandlestickData {
|
||||||
|
time: string
|
||||||
|
open: number
|
||||||
|
high: number
|
||||||
|
low: number
|
||||||
|
close: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SimpleTradingChartProps {
|
||||||
|
symbol?: string
|
||||||
|
positions?: any[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SimpleTradingChart({ symbol = 'SOL/USDC', positions = [] }: SimpleTradingChartProps) {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||||
|
const [data, setData] = useState<SimpleCandlestickData[]>([])
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
|
// Generate sample data
|
||||||
|
useEffect(() => {
|
||||||
|
const generateData = () => {
|
||||||
|
const data: SimpleCandlestickData[] = []
|
||||||
|
const basePrice = 166.5
|
||||||
|
let currentPrice = basePrice
|
||||||
|
const today = new Date()
|
||||||
|
|
||||||
|
for (let i = 29; i >= 0; i--) {
|
||||||
|
const date = new Date(today)
|
||||||
|
date.setDate(date.getDate() - i)
|
||||||
|
const timeString = date.toISOString().split('T')[0]
|
||||||
|
|
||||||
|
const change = (Math.random() - 0.5) * 4
|
||||||
|
const open = currentPrice
|
||||||
|
const close = currentPrice + change
|
||||||
|
const high = Math.max(open, close) + Math.random() * 2
|
||||||
|
const low = Math.min(open, close) - Math.random() * 2
|
||||||
|
|
||||||
|
data.push({
|
||||||
|
time: timeString,
|
||||||
|
open: Number(open.toFixed(2)),
|
||||||
|
high: Number(high.toFixed(2)),
|
||||||
|
low: Number(low.toFixed(2)),
|
||||||
|
close: Number(close.toFixed(2)),
|
||||||
|
})
|
||||||
|
|
||||||
|
currentPrice = close
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
setData(generateData())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Draw the chart on canvas
|
||||||
|
useEffect(() => {
|
||||||
|
if (!canvasRef.current || data.length === 0) return
|
||||||
|
|
||||||
|
const canvas = canvasRef.current
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
if (!ctx) return
|
||||||
|
|
||||||
|
// Set canvas size
|
||||||
|
canvas.width = 800
|
||||||
|
canvas.height = 400
|
||||||
|
|
||||||
|
// Clear canvas
|
||||||
|
ctx.fillStyle = '#1a1a1a'
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
// Calculate chart dimensions
|
||||||
|
const padding = 50
|
||||||
|
const chartWidth = canvas.width - 2 * padding
|
||||||
|
const chartHeight = canvas.height - 2 * padding
|
||||||
|
|
||||||
|
// Find price range
|
||||||
|
const prices = data.flatMap(d => [d.open, d.high, d.low, d.close])
|
||||||
|
const minPrice = Math.min(...prices)
|
||||||
|
const maxPrice = Math.max(...prices)
|
||||||
|
const priceRange = maxPrice - minPrice
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
const getX = (index: number) => padding + (index / (data.length - 1)) * chartWidth
|
||||||
|
const getY = (price: number) => padding + ((maxPrice - price) / priceRange) * chartHeight
|
||||||
|
|
||||||
|
// Draw grid
|
||||||
|
ctx.strokeStyle = 'rgba(42, 46, 57, 0.5)'
|
||||||
|
ctx.lineWidth = 1
|
||||||
|
|
||||||
|
// Horizontal grid lines
|
||||||
|
for (let i = 0; i <= 5; i++) {
|
||||||
|
const y = padding + (i / 5) * chartHeight
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(padding, y)
|
||||||
|
ctx.lineTo(padding + chartWidth, y)
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical grid lines
|
||||||
|
for (let i = 0; i <= 10; i++) {
|
||||||
|
const x = padding + (i / 10) * chartWidth
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(x, padding)
|
||||||
|
ctx.lineTo(x, padding + chartHeight)
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw candlesticks
|
||||||
|
const candleWidth = Math.max(2, chartWidth / data.length * 0.8)
|
||||||
|
|
||||||
|
data.forEach((candle, index) => {
|
||||||
|
const x = getX(index)
|
||||||
|
const openY = getY(candle.open)
|
||||||
|
const closeY = getY(candle.close)
|
||||||
|
const highY = getY(candle.high)
|
||||||
|
const lowY = getY(candle.low)
|
||||||
|
|
||||||
|
const isGreen = candle.close > candle.open
|
||||||
|
const color = isGreen ? '#26a69a' : '#ef5350'
|
||||||
|
|
||||||
|
// Draw wick
|
||||||
|
ctx.strokeStyle = color
|
||||||
|
ctx.lineWidth = 1
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(x, highY)
|
||||||
|
ctx.lineTo(x, lowY)
|
||||||
|
ctx.stroke()
|
||||||
|
|
||||||
|
// Draw body
|
||||||
|
ctx.fillStyle = color
|
||||||
|
const bodyTop = Math.min(openY, closeY)
|
||||||
|
const bodyHeight = Math.abs(closeY - openY)
|
||||||
|
ctx.fillRect(x - candleWidth / 2, bodyTop, candleWidth, Math.max(bodyHeight, 1))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Draw price labels
|
||||||
|
ctx.fillStyle = '#ffffff'
|
||||||
|
ctx.font = '12px Arial'
|
||||||
|
ctx.textAlign = 'right'
|
||||||
|
|
||||||
|
for (let i = 0; i <= 5; i++) {
|
||||||
|
const price = maxPrice - (i / 5) * priceRange
|
||||||
|
const y = padding + (i / 5) * chartHeight
|
||||||
|
ctx.fillText(price.toFixed(2), padding - 10, y + 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw title
|
||||||
|
ctx.fillStyle = '#ffffff'
|
||||||
|
ctx.font = 'bold 16px Arial'
|
||||||
|
ctx.textAlign = 'left'
|
||||||
|
ctx.fillText(symbol, padding, 30)
|
||||||
|
|
||||||
|
}, [data, symbol])
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className="bg-gray-900 rounded-lg p-6 h-[600px] flex items-center justify-center">
|
||||||
|
<div className="text-red-400">Chart Error: {error}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-gray-900 rounded-lg p-6">
|
||||||
|
<canvas
|
||||||
|
ref={canvasRef}
|
||||||
|
className="border border-gray-700 rounded"
|
||||||
|
style={{ width: '100%', maxWidth: '800px', height: '400px' }}
|
||||||
|
/>
|
||||||
|
<div className="mt-4 text-sm text-gray-400">
|
||||||
|
Simple canvas-based candlestick chart • {data.length} data points
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user