initial: stock trading dashboard
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
}
|
||||
Generated
+4672
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "trading-bot-server",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"build": "nest build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@nestjs/platform-socket.io": "^10.0.0",
|
||||
"@nestjs/websockets": "^10.0.0",
|
||||
"socket.io": "^4.6.0",
|
||||
"rxjs": "^7.8.0",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
"@nestjs/schematics": "^10.0.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BotGateway } from './websocket/bot.gateway';
|
||||
import { BotService } from './bot/bot.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [],
|
||||
providers: [BotGateway, BotService],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class BotService {
|
||||
private logger = new Logger('BotService');
|
||||
|
||||
// 봇 상태 데이터 저장
|
||||
private botState = {
|
||||
isRunning: false,
|
||||
positions: [],
|
||||
balance: 0,
|
||||
totalProfit: 0,
|
||||
logs: [],
|
||||
};
|
||||
|
||||
getBotState() {
|
||||
return this.botState;
|
||||
}
|
||||
|
||||
updateBotState(state: any) {
|
||||
this.botState = { ...this.botState, ...state };
|
||||
this.logger.log('Bot state updated');
|
||||
}
|
||||
|
||||
addLog(log: any) {
|
||||
this.botState.logs.push({
|
||||
...log,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// 최근 100개만 유지
|
||||
if (this.botState.logs.length > 100) {
|
||||
this.botState.logs = this.botState.logs.slice(-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
app.enableCors({
|
||||
origin: ['http://localhost:5173', 'http://stock.wageulwageul.com', 'http://3.34.1.212'],
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
await app.listen(3001);
|
||||
console.log('🚀 Trading Bot Dashboard Server running on http://localhost:3001');
|
||||
}
|
||||
bootstrap();
|
||||
@@ -0,0 +1,98 @@
|
||||
import {
|
||||
WebSocketGateway,
|
||||
WebSocketServer,
|
||||
SubscribeMessage,
|
||||
OnGatewayConnection,
|
||||
OnGatewayDisconnect,
|
||||
} from '@nestjs/websockets';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
@WebSocketGateway({
|
||||
cors: {
|
||||
origin: ['http://localhost:5173', 'http://stock.wageulwageul.com', 'http://3.34.1.212', 'https://stock.wageulwageul.com'],
|
||||
credentials: true,
|
||||
},
|
||||
})
|
||||
export class BotGateway implements OnGatewayConnection, OnGatewayDisconnect {
|
||||
@WebSocketServer()
|
||||
server: Server;
|
||||
|
||||
private logger = new Logger('BotGateway');
|
||||
private connectedClients = new Set<string>();
|
||||
|
||||
// 마지막 상태 캐시 - 새 클라이언트 접속 시 즉시 전송
|
||||
private lastUpdate: any = null;
|
||||
private recentLogs: any[] = [];
|
||||
private readonly MAX_LOGS = 100;
|
||||
|
||||
handleConnection(client: Socket) {
|
||||
this.connectedClients.add(client.id);
|
||||
this.logger.log(`Client connected: ${client.id} (Total: ${this.connectedClients.size})`);
|
||||
|
||||
client.emit('connection:success', {
|
||||
message: 'Connected to Trading Bot Dashboard',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// 새 클라이언트에게 마지막 상태 즉시 전송
|
||||
if (this.lastUpdate) {
|
||||
client.emit('dashboard:update', this.lastUpdate);
|
||||
}
|
||||
|
||||
// 최근 로그 재전송 (최대 50개)
|
||||
const logsToSend = this.recentLogs.slice(-50);
|
||||
for (const log of logsToSend) {
|
||||
client.emit('dashboard:log', log);
|
||||
}
|
||||
}
|
||||
|
||||
handleDisconnect(client: Socket) {
|
||||
this.connectedClients.delete(client.id);
|
||||
this.logger.log(`Client disconnected: ${client.id} (Total: ${this.connectedClients.size})`);
|
||||
}
|
||||
|
||||
@SubscribeMessage('bot:update')
|
||||
handleBotUpdate(client: Socket, payload: any) {
|
||||
this.lastUpdate = payload;
|
||||
this.server.emit('dashboard:update', payload);
|
||||
}
|
||||
|
||||
@SubscribeMessage('bot:trade')
|
||||
handleTrade(client: Socket, payload: any) {
|
||||
this.logger.log(`Trade executed: ${payload.action} ${payload.stock_code}`);
|
||||
this.server.emit('dashboard:trade', payload);
|
||||
}
|
||||
|
||||
@SubscribeMessage('bot:log')
|
||||
handleLog(client: Socket, payload: any) {
|
||||
// 로그 캐시에 추가
|
||||
this.recentLogs.push(payload);
|
||||
if (this.recentLogs.length > this.MAX_LOGS) {
|
||||
this.recentLogs.shift();
|
||||
}
|
||||
this.server.emit('dashboard:log', payload);
|
||||
}
|
||||
|
||||
broadcastUpdate(data: any) {
|
||||
this.lastUpdate = data;
|
||||
this.server.emit('dashboard:update', data);
|
||||
}
|
||||
|
||||
broadcastTrade(data: any) {
|
||||
this.server.emit('dashboard:trade', data);
|
||||
}
|
||||
|
||||
broadcastLog(message: string, level: string = 'info') {
|
||||
const payload = {
|
||||
message,
|
||||
level,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
this.recentLogs.push(payload);
|
||||
if (this.recentLogs.length > this.MAX_LOGS) {
|
||||
this.recentLogs.shift();
|
||||
}
|
||||
this.server.emit('dashboard:log', payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user