Merge development branch: improved UI, coin icons, and comprehensive documentation

- Add proper CoinGecko coin icons for BTC, ETH, SOL
- Clean up homepage layout and remove clutter
- Add comprehensive .github/copilot-instructions.md with full architecture documentation
- Include timeframe fixes, trading integration improvements, and Docker optimizations
- Maintain all trading functionality and AI analysis features
This commit is contained in:
mindesbunister
2025-07-17 12:01:15 +02:00
39 changed files with 3989 additions and 301 deletions

85
app/api-test/page.tsx Normal file
View File

@@ -0,0 +1,85 @@
'use client'
import React, { useEffect, useRef, useState } from 'react'
export default function ChartAPITest() {
const chartContainerRef = useRef<HTMLDivElement>(null)
const [logs, setLogs] = useState<string[]>([])
const addLog = (message: string) => {
console.log(message)
setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`])
}
useEffect(() => {
if (!chartContainerRef.current) return
const testLightweightCharts = async () => {
try {
addLog('Importing lightweight-charts...')
const LightweightCharts = await import('lightweight-charts')
addLog('Import successful')
// Log what's available in the import
addLog('Available exports: ' + Object.keys(LightweightCharts).join(', '))
const { createChart } = LightweightCharts
addLog('createChart function available: ' + (typeof createChart))
// Create chart
const chart = createChart(chartContainerRef.current!, {
width: 600,
height: 300,
})
addLog('Chart created')
// Log chart methods
const chartMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(chart))
addLog('Chart methods: ' + chartMethods.join(', '))
// Try to find the correct method for adding series
if ('addCandlestickSeries' in chart) {
addLog('addCandlestickSeries method found!')
} else if ('addCandles' in chart) {
addLog('addCandles method found!')
} else if ('addSeries' in chart) {
addLog('addSeries method found!')
} else {
addLog('No obvious candlestick method found')
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
addLog(`Error: ${errorMessage}`)
console.error('Error:', error)
}
}
testLightweightCharts()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-2xl mb-4">Lightweight Charts API Test</h1>
<div className="mb-4">
<div
ref={chartContainerRef}
className="bg-gray-800 border border-gray-600 rounded"
style={{ width: '600px', height: '300px' }}
/>
</div>
<div>
<h2 className="text-white text-lg mb-2">API Investigation Logs</h2>
<div className="bg-gray-800 p-4 rounded max-h-96 overflow-y-auto">
{logs.map((log, index) => (
<div key={index} className="text-gray-300 text-sm font-mono mb-1">
{log}
</div>
))}
</div>
</div>
</div>
)
}

View File

@@ -1,19 +1,70 @@
import { NextResponse } from 'next/server'
import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot-simple'
import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot'
import { aiAnalysisService } from '../../../lib/ai-analysis'
import { progressTracker } from '../../../lib/progress-tracker'
export async function POST(request) {
try {
const body = await request.json()
const { symbol, layouts, timeframes, selectedLayouts, analyze = true } = body
const { symbol, layouts, timeframe, timeframes, selectedLayouts, analyze = true } = body
console.log('📊 Enhanced screenshot request:', { symbol, layouts, timeframes, selectedLayouts })
console.log('📊 Enhanced screenshot request:', { symbol, layouts, timeframe, timeframes, selectedLayouts })
// Generate unique session ID for progress tracking
const sessionId = `analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
console.log('🔍 Created session ID:', sessionId)
// Create progress tracking session with initial steps
const initialSteps = [
{
id: 'init',
title: 'Initializing Analysis',
description: 'Starting AI-powered trading analysis...',
status: 'pending'
},
{
id: 'auth',
title: 'TradingView Authentication',
description: 'Logging into TradingView accounts',
status: 'pending'
},
{
id: 'navigation',
title: 'Chart Navigation',
description: 'Navigating to chart layouts',
status: 'pending'
},
{
id: 'loading',
title: 'Chart Data Loading',
description: 'Waiting for chart data and indicators',
status: 'pending'
},
{
id: 'capture',
title: 'Screenshot Capture',
description: 'Capturing high-quality screenshots',
status: 'pending'
},
{
id: 'analysis',
title: 'AI Analysis',
description: 'Analyzing screenshots with AI',
status: 'pending'
}
]
// Create the progress session
console.log('🔍 Creating progress session with steps:', initialSteps.length)
progressTracker.createSession(sessionId, initialSteps)
console.log('🔍 Progress session created successfully')
// Prepare configuration for screenshot service
const config = {
symbol: symbol || 'BTCUSD',
timeframe: timeframes?.[0] || '60', // Use first timeframe for now
timeframe: timeframe || timeframes?.[0] || '60', // Use single timeframe, fallback to first of array, then default
layouts: layouts || selectedLayouts || ['ai'],
sessionId, // Pass session ID for progress tracking
credentials: {
email: process.env.TRADINGVIEW_EMAIL,
password: process.env.TRADINGVIEW_PASSWORD
@@ -22,35 +73,32 @@ export async function POST(request) {
console.log('🔧 Using config:', config)
// Capture screenshots using the working service
const screenshots = await enhancedScreenshotService.captureWithLogin(config)
console.log('📸 Screenshots captured:', screenshots)
let screenshots = []
let analysis = null
// Perform AI analysis if requested and screenshots were captured
if (analyze && screenshots.length > 0) {
// Perform AI analysis if requested
if (analyze) {
try {
console.log('🤖 Starting AI analysis...')
// Extract just the filenames from full paths
const filenames = screenshots.map(path => path.split('/').pop())
if (filenames.length === 1) {
analysis = await aiAnalysisService.analyzeScreenshot(filenames[0])
} else {
analysis = await aiAnalysisService.analyzeMultipleScreenshots(filenames)
}
console.log('✅ AI analysis completed')
console.log('🤖 Starting automated capture and analysis...')
const result = await aiAnalysisService.captureAndAnalyzeWithConfig(config, sessionId)
screenshots = result.screenshots
analysis = result.analysis
console.log('✅ Automated capture and analysis completed')
} catch (analysisError) {
console.error('❌ AI analysis failed:', analysisError)
// Continue without analysis rather than failing the whole request
console.error('❌ Automated capture and analysis failed:', analysisError)
// Fall back to screenshot only
screenshots = await enhancedScreenshotService.captureWithLogin(config, sessionId)
}
} else {
// Capture screenshots only
screenshots = await enhancedScreenshotService.captureWithLogin(config, sessionId)
}
console.log('📸 Final screenshots:', screenshots)
const result = {
success: true,
sessionId, // Return session ID for progress tracking
timestamp: Date.now(),
symbol: config.symbol,
layouts: config.layouts,

View File

@@ -0,0 +1,58 @@
import { NextRequest, NextResponse } from 'next/server'
import { progressTracker } from '../../../../../lib/progress-tracker'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ sessionId: string }> }
) {
const { sessionId } = await params
// Create a readable stream for Server-Sent Events
const encoder = new TextEncoder()
const stream = new ReadableStream({
start(controller) {
// Send initial progress if session exists
const initialProgress = progressTracker.getProgress(sessionId)
if (initialProgress) {
const data = `data: ${JSON.stringify(initialProgress)}\n\n`
controller.enqueue(encoder.encode(data))
}
// Listen for progress updates
const progressHandler = (progress: any) => {
const data = `data: ${JSON.stringify(progress)}\n\n`
controller.enqueue(encoder.encode(data))
}
// Listen for completion
const completeHandler = () => {
const data = `data: ${JSON.stringify({ type: 'complete' })}\n\n`
controller.enqueue(encoder.encode(data))
controller.close()
}
// Subscribe to events
progressTracker.on(`progress:${sessionId}`, progressHandler)
progressTracker.on(`progress:${sessionId}:complete`, completeHandler)
// Cleanup on stream close
request.signal.addEventListener('abort', () => {
progressTracker.off(`progress:${sessionId}`, progressHandler)
progressTracker.off(`progress:${sessionId}:complete`, completeHandler)
controller.close()
})
}
})
return new NextResponse(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'Cache-Control'
}
})
}

52
app/api/progress/route.ts Normal file
View File

@@ -0,0 +1,52 @@
import { progressTracker } from '../../../lib/progress-tracker'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const sessionId = searchParams.get('sessionId')
if (!sessionId) {
return new Response('Session ID required', { status: 400 })
}
const stream = new ReadableStream({
start(controller) {
// Send initial progress if session exists
const currentProgress = progressTracker.getProgress(sessionId)
if (currentProgress) {
const data = `data: ${JSON.stringify(currentProgress)}\n\n`
controller.enqueue(new TextEncoder().encode(data))
}
// Listen for progress updates
const progressHandler = (progress: any) => {
const data = `data: ${JSON.stringify(progress)}\n\n`
controller.enqueue(new TextEncoder().encode(data))
}
const completeHandler = () => {
const data = `data: ${JSON.stringify({ type: 'complete' })}\n\n`
controller.enqueue(new TextEncoder().encode(data))
controller.close()
}
progressTracker.on(`progress:${sessionId}`, progressHandler)
progressTracker.on(`progress:${sessionId}:complete`, completeHandler)
// Cleanup on close
return () => {
progressTracker.off(`progress:${sessionId}`, progressHandler)
progressTracker.off(`progress:${sessionId}:complete`, completeHandler)
}
}
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
}
})
}

View File

@@ -7,6 +7,7 @@ export async function POST(request) {
symbol,
side,
amount,
amountUSD,
stopLoss,
takeProfit,
useRealDEX = false,
@@ -17,10 +18,16 @@ export async function POST(request) {
toCoin
} = body
// For Docker environment, use internal port 3000. For dev, use the host header
const host = request.headers.get('host') || 'localhost:3000'
const isDocker = process.env.DOCKER_ENV === 'true'
const baseUrl = isDocker ? 'http://localhost:3000' : `http://${host}`
console.log('🔄 Execute DEX trade request:', {
symbol,
side,
amount,
amountUSD,
stopLoss,
takeProfit,
useRealDEX,
@@ -64,13 +71,14 @@ export async function POST(request) {
console.log('🔍 Validating wallet balance before DEX trade...')
try {
const validationResponse = await fetch('http://localhost:3000/api/trading/validate', {
const validationResponse = await fetch(`${baseUrl}/api/trading/validate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
symbol,
side,
amount,
amountUSD,
tradingMode: 'SPOT',
fromCoin,
toCoin
@@ -194,7 +202,7 @@ export async function POST(request) {
// Add trade to history with clear spot swap indication
try {
// Use localhost for internal container communication
await fetch('http://localhost:3000/api/trading/history', {
await fetch(`${baseUrl}/api/trading/history`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View File

@@ -0,0 +1,311 @@
import { NextResponse } from 'next/server'
export async function POST(request) {
try {
const body = await request.json()
const {
symbol,
side,
amount,
leverage = 1,
stopLoss,
takeProfit,
useRealDEX = false
} = body
console.log('🔥 Drift Perpetuals trade request:', {
symbol,
side,
amount,
leverage,
stopLoss,
takeProfit,
useRealDEX
})
// Validate inputs
if (!symbol || !side || !amount) {
return NextResponse.json(
{
success: false,
error: 'Missing required fields: symbol, side, amount'
},
{ status: 400 }
)
}
if (!['BUY', 'SELL', 'LONG', 'SHORT'].includes(side.toUpperCase())) {
return NextResponse.json(
{
success: false,
error: 'Invalid side. Must be LONG/SHORT or BUY/SELL'
},
{ status: 400 }
)
}
if (amount <= 0) {
return NextResponse.json(
{
success: false,
error: 'Amount must be greater than 0'
},
{ status: 400 }
)
}
if (leverage < 1 || leverage > 10) {
return NextResponse.json(
{
success: false,
error: 'Leverage must be between 1x and 10x'
},
{ status: 400 }
)
}
if (!useRealDEX) {
// Simulation mode
console.log('🎮 Executing SIMULATED Drift perpetual trade')
const currentPrice = symbol === 'SOL' ? 166.75 : symbol === 'BTC' ? 121819 : 3041.66
const leveragedAmount = amount * leverage
const entryFee = leveragedAmount * 0.001 // 0.1% opening fee
const liquidationPrice = side.toUpperCase().includes('LONG') || side.toUpperCase() === 'BUY'
? currentPrice * (1 - 0.9 / leverage) // Approximate liquidation price
: currentPrice * (1 + 0.9 / leverage)
await new Promise(resolve => setTimeout(resolve, 1200))
return NextResponse.json({
success: true,
trade: {
txId: `drift_sim_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`,
orderId: `drift_order_${Date.now()}`,
symbol: symbol.toUpperCase(),
side: side.toUpperCase(),
positionSize: amount,
leverage: leverage,
leveragedAmount: leveragedAmount,
entryPrice: currentPrice,
liquidationPrice: liquidationPrice,
entryFee: entryFee,
timestamp: Date.now(),
status: 'OPEN',
platform: 'Drift Protocol (Simulation)',
stopLoss: stopLoss,
takeProfit: takeProfit,
monitoring: !!(stopLoss || takeProfit),
pnl: 0
},
message: `${side.toUpperCase()} perpetual position opened: $${amount} at ${leverage}x leverage - SIMULATED`
})
}
// Real Drift trading implementation
console.log('💰 Executing REAL Drift perpetual trade')
// Import Drift SDK components
const { DriftClient, initialize, MarketType, PositionDirection, OrderType } = await import('@drift-labs/sdk')
const { Connection, Keypair } = await import('@solana/web3.js')
const { Wallet } = await import('@coral-xyz/anchor')
// Initialize connection and wallet
const connection = new Connection(
process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com',
'confirmed'
)
if (!process.env.SOLANA_PRIVATE_KEY) {
return NextResponse.json({
success: false,
error: 'Drift trading not configured - missing SOLANA_PRIVATE_KEY'
}, { status: 400 })
}
const privateKeyArray = JSON.parse(process.env.SOLANA_PRIVATE_KEY)
const keypair = Keypair.fromSecretKey(new Uint8Array(privateKeyArray))
const wallet = new Wallet(keypair)
console.log('🚀 Initializing Drift client...')
// Initialize Drift SDK
const env = 'mainnet-beta'
const sdkConfig = initialize({ env })
const driftClient = new DriftClient({
connection,
wallet,
programID: sdkConfig.DRIFT_PROGRAM_ID,
opts: {
commitment: 'confirmed',
},
})
await driftClient.subscribe()
try {
// Get market index for the symbol
const marketIndex = symbol === 'SOL' ? 0 : symbol === 'BTC' ? 1 : 0 // SOL-PERP is typically index 0
// Determine position direction
const direction = side.toUpperCase().includes('LONG') || side.toUpperCase() === 'BUY'
? PositionDirection.LONG
: PositionDirection.SHORT
// Calculate position size in base asset units
const currentPrice = 166.75 // Get from oracle in production
const baseAssetAmount = (amount * leverage) / currentPrice * 1e9 // Convert to lamports for SOL
console.log('📊 Trade parameters:', {
marketIndex,
direction: direction === PositionDirection.LONG ? 'LONG' : 'SHORT',
baseAssetAmount: baseAssetAmount.toString(),
leverage
})
// Place market order
const orderParams = {
orderType: OrderType.MARKET,
marketType: MarketType.PERP,
direction,
baseAssetAmount: Math.floor(baseAssetAmount),
marketIndex,
}
console.log('🎯 Placing Drift market order...')
const txSig = await driftClient.placeOrder(orderParams)
console.log('✅ Drift order placed:', txSig)
// Set up stop loss and take profit if specified
let stopLossOrderId = null
let takeProfitOrderId = null
if (stopLoss) {
try {
const stopLossParams = {
orderType: OrderType.LIMIT,
marketType: MarketType.PERP,
direction: direction === PositionDirection.LONG ? PositionDirection.SHORT : PositionDirection.LONG,
baseAssetAmount: Math.floor(baseAssetAmount),
price: stopLoss * 1e6, // Price in 6 decimal format
marketIndex,
triggerPrice: stopLoss * 1e6,
triggerCondition: direction === PositionDirection.LONG ? 'below' : 'above',
}
const slTxSig = await driftClient.placeOrder(stopLossParams)
stopLossOrderId = slTxSig
console.log('🛑 Stop loss order placed:', slTxSig)
} catch (slError) {
console.warn('⚠️ Stop loss order failed:', slError.message)
}
}
if (takeProfit) {
try {
const takeProfitParams = {
orderType: OrderType.LIMIT,
marketType: MarketType.PERP,
direction: direction === PositionDirection.LONG ? PositionDirection.SHORT : PositionDirection.LONG,
baseAssetAmount: Math.floor(baseAssetAmount),
price: takeProfit * 1e6, // Price in 6 decimal format
marketIndex,
triggerPrice: takeProfit * 1e6,
triggerCondition: direction === PositionDirection.LONG ? 'above' : 'below',
}
const tpTxSig = await driftClient.placeOrder(takeProfitParams)
takeProfitOrderId = tpTxSig
console.log('🎯 Take profit order placed:', tpTxSig)
} catch (tpError) {
console.warn('⚠️ Take profit order failed:', tpError.message)
}
}
// Calculate liquidation price
const liquidationPrice = direction === PositionDirection.LONG
? currentPrice * (1 - 0.9 / leverage)
: currentPrice * (1 + 0.9 / leverage)
const result = {
success: true,
trade: {
txId: txSig,
orderId: `drift_${Date.now()}`,
symbol: symbol.toUpperCase(),
side: direction === PositionDirection.LONG ? 'LONG' : 'SHORT',
positionSize: amount,
leverage: leverage,
leveragedAmount: amount * leverage,
entryPrice: currentPrice,
liquidationPrice: liquidationPrice,
entryFee: (amount * leverage) * 0.001,
timestamp: Date.now(),
status: 'PENDING',
platform: 'Drift Protocol',
dex: 'DRIFT_REAL',
stopLoss: stopLoss,
takeProfit: takeProfit,
stopLossOrderId: stopLossOrderId,
takeProfitOrderId: takeProfitOrderId,
monitoring: !!(stopLoss || takeProfit),
pnl: 0
},
message: `${direction === PositionDirection.LONG ? 'LONG' : 'SHORT'} perpetual position opened: $${amount} at ${leverage}x leverage`,
warnings: [
`⚠️ Liquidation risk at $${liquidationPrice.toFixed(4)}`,
'📊 Position requires active monitoring',
'💰 Real funds at risk'
]
}
return NextResponse.json(result)
} finally {
// Clean up
try {
await driftClient.unsubscribe()
} catch (e) {
console.warn('⚠️ Cleanup warning:', e.message)
}
}
} catch (error) {
console.error('❌ Drift perpetual trade execution error:', error)
return NextResponse.json(
{
success: false,
error: 'Internal server error',
message: `Failed to execute Drift perpetual trade: ${error.message}`,
details: error.message
},
{ status: 500 }
)
}
}
export async function GET() {
return NextResponse.json({
message: 'Drift Protocol Perpetuals Trading API',
endpoints: {
'POST /api/trading/execute-drift': 'Execute real perpetual trades via Drift Protocol',
},
status: 'Active',
features: [
'Real leveraged perpetual trading (1x-10x)',
'Long/Short positions with liquidation risk',
'Stop Loss & Take Profit orders',
'Real-time position tracking',
'Automatic margin management'
],
requirements: [
'SOLANA_PRIVATE_KEY environment variable',
'Sufficient USDC collateral in Drift account',
'Active Drift user account'
],
note: 'This API executes real trades with real money and liquidation risk. Use with caution.'
})
}

View File

@@ -0,0 +1,230 @@
import { NextResponse } from 'next/server'
import { jupiterDEXService } from '@/lib/jupiter-dex-service'
import { jupiterTriggerService } from '@/lib/jupiter-trigger-service'
export async function POST(request) {
try {
const body = await request.json()
const {
symbol,
side,
amount,
leverage = 1,
stopLoss,
takeProfit,
useRealDEX = false
} = body
console.log('🚀 Jupiter Leveraged Spot Trade request:', {
symbol,
side,
amount,
leverage,
stopLoss,
takeProfit,
useRealDEX
})
// Validate inputs
if (!symbol || !side || !amount) {
return NextResponse.json(
{
success: false,
error: 'Missing required fields: symbol, side, amount'
},
{ status: 400 }
)
}
if (!['BUY', 'SELL'].includes(side.toUpperCase())) {
return NextResponse.json(
{
success: false,
error: 'Invalid side. Must be BUY or SELL'
},
{ status: 400 }
)
}
if (amount <= 0) {
return NextResponse.json(
{
success: false,
error: 'Amount must be greater than 0'
},
{ status: 400 }
)
}
if (leverage < 1 || leverage > 10) {
return NextResponse.json(
{
success: false,
error: 'Leverage must be between 1x and 10x'
},
{ status: 400 }
)
}
if (!useRealDEX) {
// Simulation mode
console.log('🎮 Executing SIMULATED leveraged spot trade')
const currentPrice = symbol === 'SOL' ? 166.75 : symbol === 'BTC' ? 121819 : 3041.66
const leveragedAmount = amount * leverage
const estimatedTokens = side === 'BUY' ? leveragedAmount / currentPrice : leveragedAmount
await new Promise(resolve => setTimeout(resolve, 1200))
return NextResponse.json({
success: true,
trade: {
txId: `jupiter_leverage_sim_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`,
symbol: symbol.toUpperCase(),
side: side.toUpperCase(),
amount: leveragedAmount,
leverage: leverage,
originalAmount: amount,
estimatedTokens: estimatedTokens,
entryPrice: currentPrice,
timestamp: Date.now(),
status: 'FILLED',
platform: 'Jupiter DEX (Leveraged Spot)',
stopLoss: stopLoss,
takeProfit: takeProfit,
triggerOrders: stopLoss || takeProfit ? 'PENDING' : 'NONE'
},
message: `${side.toUpperCase()} ${leveragedAmount} USD worth of ${symbol} (${leverage}x leveraged spot trade) - SIMULATED`
})
}
// Real trading with Jupiter DEX + Trigger Orders
console.log('💰 Executing REAL leveraged spot trade via Jupiter DEX + Trigger Orders')
// Step 1: Execute the main trade with leveraged amount
const leveragedAmount = amount * leverage
const tradingPair = symbol === 'SOL' ? (side === 'BUY' ? 'USDC/SOL' : 'SOL/USDC') : 'SOL/USDC'
const tradeResult = await jupiterDEXService.executeTrade({
symbol,
side,
amount: leveragedAmount,
tradingPair,
quickSwap: false
})
if (!tradeResult.success) {
return NextResponse.json({
success: false,
error: `Main trade failed: ${tradeResult.error}`
}, { status: 400 })
}
console.log('✅ Main leveraged trade executed:', tradeResult.txId)
// Step 2: Calculate position size for trigger orders
const currentPrice = symbol === 'SOL' ? 166.75 : 3041.66 // Get from price API in production
const tokenAmount = side === 'BUY'
? leveragedAmount / currentPrice // If buying SOL, calculate SOL amount
: leveragedAmount // If selling, amount is already in the token
// Step 3: Create trigger orders for stop loss and take profit
let triggerResults = null
if (stopLoss || takeProfit) {
console.log('📋 Creating trigger orders for TP/SL...')
triggerResults = await jupiterTriggerService.createTradingOrders({
tokenSymbol: symbol,
amount: tokenAmount,
stopLoss: stopLoss,
takeProfit: takeProfit,
slippageBps: 50, // 0.5% slippage for trigger orders
expiredAt: Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60) // 30 days expiry
})
if (triggerResults.success) {
console.log('✅ Trigger orders created:', {
stopLoss: triggerResults.stopLossOrder,
takeProfit: triggerResults.takeProfitOrder
})
} else {
console.warn('⚠️ Trigger orders failed:', triggerResults.error)
}
}
// Step 4: Return comprehensive result
const result = {
success: true,
trade: {
txId: tradeResult.txId,
orderId: tradeResult.orderId,
symbol: symbol.toUpperCase(),
side: side.toUpperCase(),
amount: leveragedAmount,
leverage: leverage,
originalAmount: amount,
tokenAmount: tokenAmount,
entryPrice: currentPrice,
timestamp: Date.now(),
status: 'FILLED',
platform: 'Jupiter DEX (Leveraged Spot)',
dex: 'JUPITER_DEX_REAL',
stopLoss: stopLoss,
takeProfit: takeProfit
},
triggerOrders: triggerResults ? {
stopLossOrderId: triggerResults.stopLossOrder,
takeProfitOrderId: triggerResults.takeProfitOrder,
status: triggerResults.success ? 'CREATED' : 'FAILED',
error: triggerResults.error
} : null,
message: `${side.toUpperCase()} $${leveragedAmount} worth of ${symbol} executed successfully`,
explanation: [
`🔥 Leveraged Spot Trade: Used ${leverage}x leverage to trade $${leveragedAmount} instead of $${amount}`,
`💰 Main Trade: ${side === 'BUY' ? 'Bought' : 'Sold'} ~${tokenAmount.toFixed(6)} ${symbol} via Jupiter DEX`,
stopLoss ? `🛑 Stop Loss: Trigger order created at $${stopLoss}` : null,
takeProfit ? `🎯 Take Profit: Trigger order created at $${takeProfit}` : null,
`📈 This gives you ${leverage}x exposure to ${symbol} price movements using spot trading`
].filter(Boolean)
}
return NextResponse.json(result)
} catch (error) {
console.error('❌ Leveraged spot trade execution error:', error)
return NextResponse.json(
{
success: false,
error: 'Internal server error',
message: 'Failed to execute leveraged spot trade. Please try again.'
},
{ status: 500 }
)
}
}
export async function GET() {
return NextResponse.json({
message: 'Jupiter Leveraged Spot Trading API',
description: 'Leveraged trading using Jupiter DEX spot swaps + Trigger Orders for TP/SL',
endpoints: {
'POST /api/trading/execute-leverage': 'Execute leveraged spot trades with trigger orders',
},
features: [
'Leveraged spot trading (1x-10x)',
'Direct wallet trading (no deposits needed)',
'Jupiter Trigger Orders for Stop Loss & Take Profit',
'Real-time execution via Jupiter DEX',
'Automatic position monitoring'
],
advantages: [
'✅ No fund deposits required (unlike Drift)',
'✅ Real leverage effect through increased position size',
'✅ Professional stop loss & take profit via Jupiter Triggers',
'✅ Best execution through Jupiter routing',
'✅ Low fees (0.03% for stables, 0.1% others)'
],
note: 'Uses Jupiter DEX for main trades and Jupiter Trigger API for stop loss/take profit orders.'
})
}

View File

@@ -106,6 +106,11 @@ export async function POST(request) {
const body = await request.json()
const { action, orderId, ...orderData } = body
// For Docker environment, use internal port 3000. For dev, use the host header
const host = request.headers.get('host') || 'localhost:3000'
const isDocker = process.env.DOCKER_ENV === 'true'
const baseUrl = isDocker ? 'http://localhost:3000' : `http://${host}`
if (action === 'add') {
// Load existing orders
const pendingOrders = loadPendingOrders()
@@ -187,7 +192,7 @@ export async function POST(request) {
try {
// Execute the trade by calling the trading API
const tradeResponse = await fetch('http://localhost:3000/api/trading', {
const tradeResponse = await fetch(`${baseUrl}/api/trading`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View File

@@ -3,14 +3,19 @@ import { NextResponse } from 'next/server'
export async function POST(request) {
try {
const body = await request.json()
const { symbol, side, amount, price, tradingMode = 'SPOT', fromCoin, toCoin } = body
const { symbol, side, amount, amountUSD, price, tradingMode = 'SPOT', fromCoin, toCoin } = body
console.log(`🔍 Validating trade: ${side} ${amount} ${symbol}`)
console.log(`🔍 Validating trade: ${side} ${amount} ${symbol} (USD: ${amountUSD})`)
// For Docker environment, use internal port 3000. For dev, use the host header
const host = request.headers.get('host') || 'localhost:3000'
const isDocker = process.env.DOCKER_ENV === 'true'
const baseUrl = isDocker ? 'http://localhost:3000' : `http://${host}`
// Fetch real wallet balance from the wallet API
let walletBalance
try {
const walletResponse = await fetch('http://localhost:3000/api/wallet/balance')
const walletResponse = await fetch(`${baseUrl}/api/wallet/balance`)
const walletData = await walletResponse.json()
if (walletData.success && walletData.wallet) {
@@ -42,15 +47,16 @@ export async function POST(request) {
if (tradingMode === 'SPOT') {
if (side.toUpperCase() === 'BUY') {
// For BUY orders, need USDC or USD equivalent
const tradePrice = price || 166.5 // Use provided price or current SOL price
requiredBalance = amount * tradePrice
// For BUY orders, use the USD amount directly (not amount * price)
requiredBalance = amountUSD || (amount * (price || 166.5))
requiredCurrency = 'USD'
availableBalance = walletBalance.usdValue
console.log(`💰 BUY validation: Need $${requiredBalance} USD, Have $${availableBalance}`)
} else {
// For SELL orders, need the actual token
// For SELL orders, need the actual token amount
requiredBalance = amount
requiredCurrency = fromCoin || symbol
requiredCurrency = fromCoin || symbol.replace('USD', '')
// Find the token balance
const tokenPosition = walletBalance.positions.find(pos =>
@@ -59,14 +65,16 @@ export async function POST(request) {
)
availableBalance = tokenPosition ? tokenPosition.amount : walletBalance.solBalance
console.log(`💰 SELL validation: Need ${requiredBalance} ${requiredCurrency}, Have ${availableBalance}`)
}
} else if (tradingMode === 'PERP') {
// For perpetuals, only need margin
const leverage = 10 // Default leverage
const tradePrice = price || 166.5
requiredBalance = (amount * tradePrice) / leverage
requiredBalance = (amountUSD || (amount * (price || 166.5))) / leverage
requiredCurrency = 'USD'
availableBalance = walletBalance.usdValue
console.log(`💰 PERP validation: Need $${requiredBalance} USD margin, Have $${availableBalance}`)
}
console.log(`💰 Balance check: Need ${requiredBalance} ${requiredCurrency}, Have ${availableBalance}`)

12
app/canvas-chart/page.tsx Normal file
View 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>
)
}

96
app/cdn-test/page.tsx Normal file
View File

@@ -0,0 +1,96 @@
'use client'
import React, { useEffect, useRef, useState } from 'react'
export default function StandaloneTest() {
const chartContainerRef = useRef<HTMLDivElement>(null)
const [status, setStatus] = useState('Starting...')
useEffect(() => {
const initChart = async () => {
try {
setStatus('Testing CDN version...')
// Try using the CDN version instead
if (typeof window !== 'undefined' && !(window as any).LightweightCharts) {
setStatus('Loading CDN script...')
const script = document.createElement('script')
script.src = 'https://unpkg.com/lightweight-charts@5.0.8/dist/lightweight-charts.standalone.production.js'
script.onload = () => {
setStatus('CDN loaded, creating chart...')
createChartWithCDN()
}
script.onerror = () => {
setStatus('CDN load failed')
}
document.head.appendChild(script)
} else if ((window as any).LightweightCharts) {
setStatus('CDN already loaded, creating chart...')
createChartWithCDN()
}
} catch (error) {
console.error('Error:', error)
setStatus(`Error: ${error}`)
}
}
const createChartWithCDN = () => {
try {
if (!chartContainerRef.current) {
setStatus('No container')
return
}
const { createChart, CandlestickSeries } = (window as any).LightweightCharts
setStatus('Creating chart with CDN...')
const chart = createChart(chartContainerRef.current, {
width: 800,
height: 400,
layout: {
background: { color: '#1a1a1a' },
textColor: '#ffffff',
},
})
setStatus('Adding series...')
const series = chart.addSeries(CandlestickSeries, {
upColor: '#26a69a',
downColor: '#ef5350',
})
setStatus('Setting data...')
series.setData([
{ time: '2025-07-14', open: 100, high: 105, low: 95, close: 102 },
{ time: '2025-07-15', open: 102, high: 107, low: 98, close: 104 },
{ time: '2025-07-16', open: 104, high: 109, low: 101, close: 106 },
])
setStatus('Chart created successfully!')
} catch (error) {
console.error('CDN chart error:', error)
setStatus(`CDN Error: ${error}`)
}
}
initChart()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-3xl mb-4">CDN Chart Test</h1>
<div className="text-green-400 text-lg mb-4">Status: {status}</div>
<div
ref={chartContainerRef}
className="border-2 border-blue-500 bg-gray-800"
style={{
width: '800px',
height: '400px',
}}
/>
</div>
)
}

111
app/chart-debug/page.tsx Normal file
View File

@@ -0,0 +1,111 @@
'use client'
import React, { useEffect, useRef, useState } from 'react'
export default function ChartDebug() {
const chartContainerRef = useRef<HTMLDivElement>(null)
const [logs, setLogs] = useState<string[]>([])
const [chartCreated, setChartCreated] = useState(false)
const addLog = (message: string) => {
console.log(message)
setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`])
}
useEffect(() => {
if (!chartContainerRef.current) {
addLog('Chart container ref not available')
return
}
const initChart = async () => {
try {
addLog('Starting chart initialization...')
// Import lightweight-charts
const LightweightCharts = await import('lightweight-charts')
addLog('Lightweight charts imported successfully')
const { createChart } = LightweightCharts
addLog('createChart extracted')
// Create chart with minimal options
const chart = createChart(chartContainerRef.current!, {
width: 600,
height: 300,
})
addLog('Chart created successfully')
setChartCreated(true)
// Add candlestick series with the correct v5 API
const candlestickSeries = chart.addCandlestickSeries({
upColor: '#26a69a',
downColor: '#ef5350',
borderDownColor: '#ef5350',
borderUpColor: '#26a69a',
wickDownColor: '#ef5350',
wickUpColor: '#26a69a',
})
addLog('Candlestick series added')
// Very simple test data
const testData = [
{ time: '2023-01-01', open: 100, high: 110, low: 95, close: 105 },
{ time: '2023-01-02', open: 105, high: 115, low: 100, close: 110 },
{ time: '2023-01-03', open: 110, high: 120, low: 105, close: 115 },
{ time: '2023-01-04', open: 115, high: 125, low: 110, close: 120 },
{ time: '2023-01-05', open: 120, high: 130, low: 115, close: 125 },
]
addLog(`Setting data with ${testData.length} points`)
candlestickSeries.setData(testData)
addLog('Data set successfully - chart should be visible now')
// Cleanup function
return () => {
addLog('Cleaning up chart')
chart.remove()
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
addLog(`Error: ${errorMessage}`)
console.error('Chart error:', error)
}
}
initChart()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-2xl mb-4">Chart Debug Test</h1>
<div className="mb-4">
<h2 className="text-white text-lg mb-2">Status</h2>
<div className="text-gray-400">
Chart Created: {chartCreated ? '✅ Yes' : '❌ No'}
</div>
</div>
<div className="mb-4">
<h2 className="text-white text-lg mb-2">Chart Container</h2>
<div
ref={chartContainerRef}
className="bg-gray-800 border border-gray-600 rounded"
style={{ width: '600px', height: '300px' }}
/>
</div>
<div>
<h2 className="text-white text-lg mb-2">Debug Logs</h2>
<div className="bg-gray-800 p-4 rounded max-h-60 overflow-y-auto">
{logs.map((log, index) => (
<div key={index} className="text-gray-300 text-sm font-mono">
{log}
</div>
))}
</div>
</div>
</div>
)
}

14
app/chart-test/page.tsx Normal file
View File

@@ -0,0 +1,14 @@
'use client'
import React from 'react'
import TradingChart from '../../components/TradingChart'
export default function SimpleChartTest() {
return (
<div className="min-h-screen bg-gray-900 p-4">
<div className="max-w-7xl mx-auto">
<h1 className="text-2xl font-bold text-white mb-6">Chart Test</h1>
<TradingChart />
</div>
</div>
)
}

198
app/chart-trading/page.tsx Normal file
View File

@@ -0,0 +1,198 @@
'use client'
import React, { useState, useEffect } from 'react'
import TradingChart from '../../components/TradingChart'
import CompactTradingPanel from '../../components/CompactTradingPanel'
import PositionsPanel from '../../components/PositionsPanel'
export default function ChartTradingPage() {
const [currentPrice, setCurrentPrice] = useState(166.21)
const [positions, setPositions] = useState([])
const [selectedSymbol, setSelectedSymbol] = useState('SOL')
useEffect(() => {
fetchPositions()
const interval = setInterval(fetchPositions, 10000) // Update every 10 seconds
return () => clearInterval(interval)
}, [])
const fetchPositions = async () => {
try {
const response = await fetch('/api/trading/positions')
const data = await response.json()
if (data.success) {
setPositions(data.positions || [])
}
} catch (error) {
console.error('Failed to fetch positions:', error)
}
}
const handleTrade = async (tradeData: any) => {
try {
console.log('Executing trade:', tradeData)
// For perpetual trades, use the execute-perp endpoint
const response = await fetch('/api/trading/execute-perp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(tradeData)
})
const result = await response.json()
if (result.success) {
alert(`Trade executed successfully! ${result.message}`)
fetchPositions() // Refresh positions
} else {
alert(`Trade failed: ${result.error || result.message}`)
}
} catch (error) {
console.error('Trade execution error:', error)
alert('Trade execution failed. Please try again.')
}
}
const handlePriceUpdate = (price: number) => {
setCurrentPrice(price)
}
return (
<div className="h-screen bg-gray-900 flex flex-col">
{/* Top Bar */}
<div className="bg-gray-800 border-b border-gray-700 p-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-6">
<h1 className="text-xl font-bold text-white">Trading Terminal</h1>
{/* Symbol Selector */}
<div className="flex space-x-2">
{['SOL', 'BTC', 'ETH'].map(symbol => (
<button
key={symbol}
onClick={() => setSelectedSymbol(symbol)}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
selectedSymbol === symbol
? 'bg-blue-600 text-white'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
}`}
>
{symbol}
</button>
))}
</div>
</div>
{/* Market Status */}
<div className="flex items-center space-x-4 text-sm">
<div className="flex items-center space-x-2">
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
<span className="text-gray-300">Market Open</span>
</div>
<div className="text-gray-400">
Server Time: {new Date().toLocaleTimeString()}
</div>
</div>
</div>
</div>
{/* Main Trading Interface */}
<div className="flex-1 flex">
{/* Chart Area (70% width) */}
<div className="flex-1 p-4">
<TradingChart
symbol={selectedSymbol}
positions={positions}
onPriceUpdate={handlePriceUpdate}
/>
</div>
{/* Trading Panel (30% width) */}
<div className="w-96 border-l border-gray-700 p-4 space-y-4">
<CompactTradingPanel
symbol={selectedSymbol}
currentPrice={currentPrice}
onTrade={handleTrade}
/>
</div>
</div>
{/* Bottom Panel - Positions */}
<div className="border-t border-gray-700 bg-gray-800">
<div className="p-4">
<div className="flex items-center justify-between mb-4">
<div className="flex space-x-6">
<button className="text-white font-medium border-b-2 border-blue-500 pb-2">
Positions ({positions.length})
</button>
<button className="text-gray-400 hover:text-white pb-2">
Orders
</button>
<button className="text-gray-400 hover:text-white pb-2">
History
</button>
</div>
{positions.length > 0 && (
<div className="text-sm text-gray-400">
Total P&L: <span className="text-green-400">+$0.00</span>
</div>
)}
</div>
{/* Positions Table */}
<div className="max-h-48 overflow-y-auto">
{positions.length === 0 ? (
<div className="text-center py-8 text-gray-400">
No open positions
</div>
) : (
<div className="space-y-2">
{positions.map((position: any) => (
<div
key={position.id}
className="bg-gray-900 rounded-lg p-4 flex items-center justify-between"
>
<div className="flex items-center space-x-4">
<div className={`w-3 h-3 rounded-full ${
position.side === 'BUY' ? 'bg-green-400' : 'bg-red-400'
}`}></div>
<div>
<div className="text-white font-medium">
{position.symbol} {position.side}
</div>
<div className="text-sm text-gray-400">
Size: {position.amount} Entry: ${position.entryPrice?.toFixed(2)}
</div>
</div>
</div>
<div className="text-right">
<div className="text-white font-medium">
${position.totalValue?.toFixed(2) || '0.00'}
</div>
<div className={`text-sm ${
(position.unrealizedPnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'
}`}>
{(position.unrealizedPnl || 0) >= 0 ? '+' : ''}${(position.unrealizedPnl || 0).toFixed(2)}
</div>
</div>
<div className="flex space-x-2">
<button className="px-3 py-1 bg-gray-700 text-gray-300 rounded text-sm hover:bg-gray-600">
Modify
</button>
<button className="px-3 py-1 bg-red-600 text-white rounded text-sm hover:bg-red-700">
Close
</button>
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</div>
)
}

119
app/debug-chart/page.tsx Normal file
View File

@@ -0,0 +1,119 @@
'use client'
import React, { useEffect, useRef, useState } from 'react'
export default function DebugChart() {
const chartContainerRef = useRef<HTMLDivElement>(null)
const [logs, setLogs] = useState<string[]>([])
const [error, setError] = useState<string | null>(null)
const addLog = (message: string) => {
console.log(message)
setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`])
}
useEffect(() => {
addLog('Component mounted')
if (!chartContainerRef.current) {
addLog('ERROR: No chart container ref')
return
}
addLog('Chart container found')
const initChart = async () => {
try {
addLog('Starting chart initialization...')
addLog('Importing lightweight-charts...')
const LightweightChartsModule = await import('lightweight-charts')
addLog('Import successful')
addLog('Available exports: ' + Object.keys(LightweightChartsModule).join(', '))
const { createChart, ColorType, CrosshairMode } = LightweightChartsModule
addLog('Extracted createChart and other components')
addLog('Creating chart...')
const chart = createChart(chartContainerRef.current!, {
width: 600,
height: 300,
layout: {
textColor: '#ffffff',
background: { color: '#1a1a1a' },
},
})
addLog('Chart created successfully')
addLog('Adding candlestick series...')
const candlestickSeries = chart.addCandlestickSeries({
upColor: '#26a69a',
downColor: '#ef5350',
})
addLog('Series added successfully')
addLog('Generating test data...')
const data = [
{ time: '2025-07-10', open: 100, high: 110, low: 95, close: 105 },
{ time: '2025-07-11', open: 105, high: 115, low: 100, close: 110 },
{ time: '2025-07-12', open: 110, high: 120, low: 105, close: 115 },
{ time: '2025-07-13', open: 115, high: 125, low: 110, close: 118 },
{ time: '2025-07-14', open: 118, high: 128, low: 113, close: 122 },
{ time: '2025-07-15', open: 122, high: 132, low: 117, close: 125 },
{ time: '2025-07-16', open: 125, high: 135, low: 120, close: 130 },
]
addLog(`Generated ${data.length} data points`)
addLog('Setting data on series...')
candlestickSeries.setData(data)
addLog('Data set successfully - chart should be visible!')
addLog('Chart initialization complete')
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err)
const errorStack = err instanceof Error ? err.stack : 'No stack trace'
addLog(`ERROR: ${errorMessage}`)
console.error('Chart initialization error:', err)
setError(`${errorMessage}\n\nStack: ${errorStack}`)
}
}
initChart()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-2xl mb-4">Debug Chart Test</h1>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div>
<h2 className="text-white text-lg mb-2">Chart</h2>
<div
ref={chartContainerRef}
className="bg-gray-800 border border-gray-600 rounded"
style={{ width: '600px', height: '300px' }}
/>
</div>
<div>
<h2 className="text-white text-lg mb-2">Debug Logs</h2>
<div className="bg-gray-800 p-4 rounded h-80 overflow-y-auto">
{logs.map((log, index) => (
<div key={index} className="text-gray-300 text-sm font-mono mb-1">
{log}
</div>
))}
</div>
{error && (
<div className="mt-4 bg-red-900/20 border border-red-500 p-4 rounded">
<h3 className="text-red-400 font-semibold mb-2">Error Details:</h3>
<pre className="text-red-300 text-xs whitespace-pre-wrap">{error}</pre>
</div>
)}
</div>
</div>
</div>
)
}

72
app/direct-chart/page.tsx Normal file
View File

@@ -0,0 +1,72 @@
'use client'
import React, { useEffect } from 'react'
export default function DirectChart() {
useEffect(() => {
const container = document.getElementById('chart-container')
if (!container) return
const initChart = async () => {
try {
console.log('Starting direct chart...')
// Import with explicit .mjs extension
const chartModule = await import('lightweight-charts')
console.log('Module loaded:', chartModule)
const { createChart } = chartModule
console.log('Functions extracted')
const chart = createChart(container, {
width: 800,
height: 400,
layout: {
background: { color: '#1a1a1a' },
textColor: '#ffffff',
},
})
console.log('Chart created')
const series = chart.addCandlestickSeries({
upColor: '#26a69a',
downColor: '#ef5350',
})
console.log('Series added')
series.setData([
{ time: '2025-07-14', open: 100, high: 105, low: 95, close: 102 },
{ time: '2025-07-15', open: 102, high: 107, low: 98, close: 104 },
{ time: '2025-07-16', open: 104, high: 109, low: 101, close: 106 },
])
console.log('Data set - should be visible!')
} catch (error) {
console.error('Direct chart error:', error)
const statusDiv = document.getElementById('status')
if (statusDiv) {
statusDiv.textContent = `Error: ${error}`
statusDiv.className = 'text-red-400 text-lg mb-4'
}
}
}
// Add a small delay to ensure DOM is ready
setTimeout(initChart, 100)
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-3xl mb-4">Direct DOM Chart</h1>
<div id="status" className="text-yellow-400 text-lg mb-4">Loading...</div>
<div
id="chart-container"
className="border-2 border-green-500 bg-gray-800"
style={{
width: '800px',
height: '400px',
}}
/>
</div>
)
}

View File

@@ -0,0 +1,77 @@
'use client'
import React, { useEffect, useRef, useState } from 'react'
export default function MinimalChartTest() {
const chartContainerRef = useRef<HTMLDivElement>(null)
const [status, setStatus] = useState('Starting...')
const [error, setError] = useState<string | null>(null)
useEffect(() => {
if (!chartContainerRef.current) {
setStatus('No container ref')
return
}
const initChart = async () => {
try {
setStatus('Loading lightweight-charts...')
console.log('Starting chart init...')
const LightweightCharts = await import('lightweight-charts')
console.log('Lightweight charts loaded:', LightweightCharts)
setStatus('Charts library loaded')
const { createChart } = LightweightCharts
console.log('createChart:', typeof createChart)
setStatus('Creating chart...')
const chart = createChart(chartContainerRef.current!, {
width: 800,
height: 400,
})
console.log('Chart created:', chart)
setStatus('Chart created')
setStatus('Adding series...')
const series = chart.addCandlestickSeries({})
console.log('Series created:', series)
setStatus('Series added')
setStatus('Adding data...')
const data = [
{ time: '2025-01-01', open: 100, high: 110, low: 90, close: 105 },
{ time: '2025-01-02', open: 105, high: 115, low: 95, close: 110 },
{ time: '2025-01-03', open: 110, high: 120, low: 100, close: 115 },
]
series.setData(data)
console.log('Data set')
setStatus('Chart ready!')
} catch (err) {
console.error('Chart init error:', err)
const errorMsg = err instanceof Error ? err.message : String(err)
setError(errorMsg)
setStatus(`Error: ${errorMsg}`)
}
}
initChart()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-2xl mb-4">Minimal Chart Test</h1>
<div className="text-gray-300 mb-4">Status: {status}</div>
{error && (
<div className="text-red-400 mb-4 p-4 bg-red-900/20 rounded">
Error: {error}
</div>
)}
<div
ref={chartContainerRef}
className="bg-gray-800 border border-gray-600"
style={{ width: '800px', height: '400px' }}
/>
</div>
)
}

115
app/simple-chart/page.tsx Normal file
View File

@@ -0,0 +1,115 @@
'use client'
import React, { useEffect, useRef, useState } from 'react'
export default function SimpleChart() {
const chartContainerRef = useRef<HTMLDivElement>(null)
const [status, setStatus] = useState('Initializing...')
useEffect(() => {
if (!chartContainerRef.current) return
const initChart = async () => {
try {
setStatus('Loading lightweight-charts...')
console.log('Importing lightweight-charts...')
const LightweightCharts = await import('lightweight-charts')
console.log('Lightweight charts imported successfully')
setStatus('Creating chart...')
const { createChart, ColorType } = LightweightCharts
const chart = createChart(chartContainerRef.current!, {
layout: {
background: { type: ColorType.Solid, color: '#1a1a1a' },
textColor: '#ffffff',
},
width: chartContainerRef.current!.clientWidth || 800,
height: 400,
grid: {
vertLines: { color: 'rgba(42, 46, 57, 0.5)' },
horzLines: { color: 'rgba(42, 46, 57, 0.5)' },
},
})
setStatus('Adding candlestick series...')
console.log('Chart created, adding candlestick series...')
const candlestickSeries = chart.addCandlestickSeries({
upColor: '#26a69a',
downColor: '#ef5350',
borderDownColor: '#ef5350',
borderUpColor: '#26a69a',
wickDownColor: '#ef5350',
wickUpColor: '#26a69a',
})
// Generate sample data
const data = []
const baseTime = new Date(Date.now() - 100 * 60 * 1000) // 100 minutes ago
let price = 166.5
for (let i = 0; i < 100; i++) {
const currentTime = new Date(baseTime.getTime() + i * 60 * 1000) // 1 minute intervals
const timeString = currentTime.toISOString().split('T')[0] // YYYY-MM-DD format
const change = (Math.random() - 0.5) * 2 // Random price change
const open = price
const close = price + change
const high = Math.max(open, close) + Math.random() * 1
const low = Math.min(open, close) - Math.random() * 1
data.push({
time: timeString,
open: Number(open.toFixed(2)),
high: Number(high.toFixed(2)),
low: Number(low.toFixed(2)),
close: Number(close.toFixed(2)),
})
price = close
}
console.log('Setting chart data...', data.length, 'points')
candlestickSeries.setData(data)
setStatus('Chart loaded successfully!')
console.log('Chart created successfully!')
// Handle resize
const handleResize = () => {
if (chartContainerRef.current) {
chart.applyOptions({
width: chartContainerRef.current.clientWidth,
})
}
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
chart.remove()
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
console.error('Error creating chart:', error)
setStatus(`Error: ${errorMessage}`)
}
}
initChart()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-2xl mb-4">Lightweight Charts Test</h1>
<div className="text-gray-400 mb-4">Status: {status}</div>
<div
ref={chartContainerRef}
className="bg-gray-800 rounded w-full h-96"
style={{ minHeight: '400px' }}
/>
</div>
)
}

95
app/simple-test/page.tsx Normal file
View File

@@ -0,0 +1,95 @@
'use client'
import React, { useEffect, useRef, useState } from 'react'
export default function SimpleTest() {
const chartContainerRef = useRef<HTMLDivElement>(null)
const [status, setStatus] = useState('Initializing...')
useEffect(() => {
const initChart = async () => {
try {
setStatus('Importing library...')
// Test if we can import the library
const chartModule = await import('lightweight-charts')
setStatus('Library imported')
// Test if we can extract functions
const { createChart } = chartModule
setStatus('Functions extracted')
if (!chartContainerRef.current) {
setStatus('No container element')
return
}
setStatus('Creating chart...')
// Create chart with explicit dimensions
const chart = createChart(chartContainerRef.current, {
width: 800,
height: 400,
layout: {
background: { color: '#1a1a1a' },
textColor: '#ffffff',
},
})
setStatus('Chart created')
// Add series
const series = chart.addCandlestickSeries({
upColor: '#00ff00',
downColor: '#ff0000',
})
setStatus('Series added')
// Add simple data
series.setData([
{ time: '2025-07-14', open: 100, high: 105, low: 95, close: 102 },
{ time: '2025-07-15', open: 102, high: 107, low: 98, close: 104 },
{ time: '2025-07-16', open: 104, high: 109, low: 101, close: 106 },
])
setStatus('Data set - Chart should be visible!')
} catch (error) {
console.error('Error:', error)
setStatus(`Error: ${error}`)
}
}
initChart()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-3xl mb-4">Simple Chart Test</h1>
<div className="text-green-400 text-lg mb-4">Status: {status}</div>
<div className="bg-red-500 p-2 mb-4 text-white">
This red border should help us see if the container is properly sized
</div>
<div
ref={chartContainerRef}
className="border-4 border-yellow-500 bg-gray-800"
style={{
width: '800px',
height: '400px',
display: 'block',
position: 'relative'
}}
>
<div className="text-white p-4">
Container content - this should be replaced by the chart
</div>
</div>
<div className="text-gray-400 mt-4">
Container ref: {chartContainerRef.current ? 'Available' : 'Not available'}
</div>
</div>
)
}

12
app/test-chart/page.tsx Normal file
View File

@@ -0,0 +1,12 @@
'use client'
import React from 'react'
import WorkingTradingChart from '../../components/WorkingTradingChart'
export default function TestChartPage() {
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-3xl mb-6">Working Chart Test</h1>
<WorkingTradingChart symbol="SOL/USDC" positions={[]} />
</div>
)
}

16
app/test-trading/page.tsx Normal file
View File

@@ -0,0 +1,16 @@
'use client'
import React from 'react'
export default function SimpleTradingPage() {
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-3xl mb-6">Trading Dashboard</h1>
<div className="bg-gray-800 p-6 rounded-lg">
<h2 className="text-white text-xl mb-4">Chart Area</h2>
<div className="bg-gray-700 h-96 rounded flex items-center justify-center">
<span className="text-gray-300">Chart will load here</span>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,77 @@
'use client'
import React, { useEffect, useRef } from 'react'
export default function WorkingChart() {
const chartContainerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!chartContainerRef.current) return
const initChart = async () => {
try {
const { createChart } = await import('lightweight-charts')
const chart = createChart(chartContainerRef.current!, {
width: 800,
height: 400,
layout: {
textColor: '#ffffff',
background: { color: '#1a1a1a' },
},
})
const candlestickSeries = chart.addCandlestickSeries({
upColor: '#26a69a',
downColor: '#ef5350',
})
// Simple working data - last 30 days
const data = []
const today = new Date()
let price = 166.5
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 = price
const close = price + 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)),
})
price = close
}
candlestickSeries.setData(data)
return () => {
chart.remove()
}
} catch (error) {
console.error('Chart error:', error)
}
}
initChart()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-2xl mb-4">Working Chart Test</h1>
<div
ref={chartContainerRef}
className="bg-gray-800 border border-gray-600"
/>
</div>
)
}