✅ Restore working dashboard and TradingView analysis
- Fixed layout conflicts by removing minimal layout.tsx in favor of complete layout.js - Restored original AI Analysis page with full TradingView integration - Connected enhanced screenshot API to real TradingView automation service - Fixed screenshot gallery to handle both string and object formats - Added image serving API route for screenshot display - Resolved hydration mismatch issues with suppressHydrationWarning - All navigation pages working (Analysis, Trading, Automation, Settings) - TradingView automation successfully capturing screenshots from AI and DIY layouts - Docker Compose v2 compatibility ensured Working features: - Homepage with hero section and status cards - Navigation menu with Trading Bot branding - Real TradingView screenshot capture - AI-powered chart analysis - Multi-layout support (AI + DIY module) - Screenshot gallery with image serving - API endpoints for balance, status, screenshots, trading
This commit is contained in:
276
lib/drift-trading.ts.backup
Normal file
276
lib/drift-trading.ts.backup
Normal file
@@ -0,0 +1,276 @@
|
||||
import { Connection, PublicKey } from '@solana/web3.js';
|
||||
|
||||
// Types
|
||||
export interface TradingBalance {
|
||||
totalValue: number;
|
||||
availableBalance: number;
|
||||
marginUsed: number;
|
||||
unrealizedPnl: number;
|
||||
positions: {
|
||||
symbol: string;
|
||||
size: number;
|
||||
notionalValue: number;
|
||||
unrealizedPnl: number;
|
||||
side: 'long' | 'short';
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
symbol: string;
|
||||
side: 'long' | 'short';
|
||||
size: number;
|
||||
entryPrice: number;
|
||||
markPrice: number;
|
||||
unrealizedPnl: number;
|
||||
notionalValue: number;
|
||||
}
|
||||
|
||||
export interface OrderRequest {
|
||||
symbol: string;
|
||||
side: 'buy' | 'sell';
|
||||
type: 'market' | 'limit';
|
||||
amount: number;
|
||||
price?: number;
|
||||
}
|
||||
|
||||
export interface OrderResponse {
|
||||
success: boolean;
|
||||
orderId?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface TradeParams {
|
||||
symbol: string;
|
||||
side: 'BUY' | 'SELL';
|
||||
amount: number;
|
||||
orderType: 'MARKET' | 'LIMIT';
|
||||
price?: number;
|
||||
stopLoss?: number;
|
||||
takeProfit?: number;
|
||||
stopLossType?: string;
|
||||
takeProfitType?: string;
|
||||
}
|
||||
|
||||
export interface TradeResult {
|
||||
success: boolean;
|
||||
txId?: string;
|
||||
executedPrice?: number;
|
||||
executedAmount?: number;
|
||||
conditionalOrders?: any[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
class DriftTradingService {
|
||||
private connection: Connection;
|
||||
private readonly accountPDA = '7LonnWut5i3h36xyMA5jbwnGFbnzXUPY2dsPfNaSsrTk';
|
||||
|
||||
constructor() {
|
||||
this.connection = new Connection(
|
||||
process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com',
|
||||
'confirmed'
|
||||
);
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
console.log('🚀 Initializing Direct Drift Trading Service...');
|
||||
console.log('✅ Direct service ready - no SDK subscriptions needed');
|
||||
}
|
||||
|
||||
async login(): Promise<{ success: boolean; message?: string }> {
|
||||
console.log('🔐 Login to direct drift service...');
|
||||
// In direct mode, we don't need to login since we're just reading account data
|
||||
return {
|
||||
success: true,
|
||||
message: 'Direct service - no login required'
|
||||
};
|
||||
}
|
||||
|
||||
private async getAccountData(): Promise<Buffer> {
|
||||
try {
|
||||
const accountInfo = await this.connection.getAccountInfo(
|
||||
new PublicKey(this.accountPDA)
|
||||
);
|
||||
|
||||
if (!accountInfo) {
|
||||
throw new Error('Account not found');
|
||||
}
|
||||
|
||||
return accountInfo.data;
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to fetch account data:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private parseAccountData(data: Buffer) {
|
||||
try {
|
||||
// Extract USDC balance at offset 106
|
||||
const usdcBalance = data.readBigInt64LE(106);
|
||||
const usdcValue = Number(usdcBalance) / 1_000_000; // USDC has 6 decimals
|
||||
|
||||
// Extract SOL position at offset 432 (most current/accurate location)
|
||||
const solPosition = data.readBigInt64LE(432);
|
||||
const solAmount = Number(solPosition) / 1_000_000_000; // SOL has 9 decimals
|
||||
|
||||
// Estimate SOL price (you could fetch this from an oracle)
|
||||
const solPrice = 17.11; // Current approximate price
|
||||
const solValue = solAmount * solPrice;
|
||||
|
||||
// Calculate basic total from current positions
|
||||
let totalValue = usdcValue + solValue;
|
||||
|
||||
// If we have a very low total but no active positions, this might indicate
|
||||
// settlement lag where profits from closed positions aren't yet reflected
|
||||
if (totalValue < 10 && solAmount === 0) {
|
||||
console.log(`⚠️ Low balance detected with no positions - possible settlement lag`);
|
||||
console.log(` This often happens after position closure before profit settlement`);
|
||||
|
||||
// In this case, we might want to use the last known total or a fallback
|
||||
// For now, we'll note this condition but keep the calculated value
|
||||
}
|
||||
|
||||
console.log(`💰 Parsed account data:`);
|
||||
console.log(` USDC Balance: $${usdcValue.toFixed(2)}`);
|
||||
console.log(` SOL Position: ${solAmount.toFixed(6)} SOL`);
|
||||
console.log(` SOL Value: $${solValue.toFixed(2)} (@ $${solPrice})`);
|
||||
console.log(` Total Value: $${totalValue.toFixed(2)}`);
|
||||
|
||||
return {
|
||||
usdcBalance: usdcValue,
|
||||
solPosition: solAmount,
|
||||
solPrice,
|
||||
solValue,
|
||||
totalValue,
|
||||
settlementPending: totalValue < 10 && solAmount === 0
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to parse account data:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getTradingBalance(): Promise<TradingBalance> {
|
||||
try {
|
||||
const data = await this.getAccountData();
|
||||
const parsed = this.parseAccountData(data);
|
||||
|
||||
// Calculate unrealized PnL (SOL value minus some baseline)
|
||||
const unrealizedPnl = parsed.solValue - (parsed.solPosition * 15); // Assuming $15 entry price
|
||||
|
||||
return {
|
||||
totalValue: parsed.totalValue,
|
||||
availableBalance: parsed.usdcBalance,
|
||||
marginUsed: 0, // Not available without full account parsing
|
||||
unrealizedPnl,
|
||||
positions: parsed.solPosition > 0 ? [{
|
||||
symbol: 'SOL',
|
||||
size: parsed.solPosition,
|
||||
notionalValue: parsed.solValue,
|
||||
unrealizedPnl,
|
||||
side: 'long' as const
|
||||
}] : [],
|
||||
// Add settlement status info
|
||||
...(parsed.settlementPending && {
|
||||
_meta: {
|
||||
settlementPending: true,
|
||||
note: 'Low balance with no positions may indicate settlement lag after position closure'
|
||||
}
|
||||
})
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to get trading balance:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getPositions(): Promise<Position[]> {
|
||||
try {
|
||||
const data = await this.getAccountData();
|
||||
const parsed = this.parseAccountData(data);
|
||||
|
||||
if (parsed.solPosition <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const unrealizedPnl = parsed.solValue - (parsed.solPosition * 15); // Assuming $15 entry
|
||||
|
||||
return [{
|
||||
symbol: 'SOL',
|
||||
side: 'long',
|
||||
size: parsed.solPosition,
|
||||
entryPrice: 15, // Estimated entry price
|
||||
markPrice: parsed.solPrice,
|
||||
unrealizedPnl,
|
||||
notionalValue: parsed.solValue
|
||||
}];
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to get positions:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async placeOrder(orderRequest: OrderRequest): Promise<OrderResponse> {
|
||||
console.log('📝 Order placement not implemented in direct mode:', orderRequest);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Order placement requires SDK integration'
|
||||
};
|
||||
}
|
||||
|
||||
async executeTrade(tradeParams: TradeParams): Promise<TradeResult> {
|
||||
console.log('🎯 Execute Trade Request:', tradeParams);
|
||||
|
||||
try {
|
||||
// Simulated trade execution only
|
||||
console.log(`📝 Simulating ${tradeParams.side} ${tradeParams.amount} ${tradeParams.symbol}`);
|
||||
console.log(`💰 Order Type: ${tradeParams.orderType}`);
|
||||
|
||||
if (tradeParams.price) {
|
||||
console.log(`💲 Price: $${tradeParams.price}`);
|
||||
}
|
||||
|
||||
if (tradeParams.stopLoss) {
|
||||
console.log(`🛑 Stop Loss: $${tradeParams.stopLoss}`);
|
||||
}
|
||||
|
||||
if (tradeParams.takeProfit) {
|
||||
console.log(`🎯 Take Profit: $${tradeParams.takeProfit}`);
|
||||
}
|
||||
|
||||
// Return simulated success response
|
||||
return {
|
||||
success: true,
|
||||
txId: 'simulated_' + Date.now(),
|
||||
executedPrice: tradeParams.price || (tradeParams.symbol === 'SOL' ? 17.11 : 100),
|
||||
executedAmount: tradeParams.amount,
|
||||
conditionalOrders: [],
|
||||
error: undefined
|
||||
};
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('❌ Trade execution failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Trade execution failed'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
console.log('✅ Direct service disconnected (no cleanup needed)');
|
||||
}
|
||||
|
||||
async getServiceStatus(): Promise<{
|
||||
drift: { connected: boolean; accountPDA: string };
|
||||
}> {
|
||||
return {
|
||||
drift: {
|
||||
connected: true,
|
||||
accountPDA: this.accountPDA
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const driftTradingService = new DriftTradingService();
|
||||
Reference in New Issue
Block a user