Browse Source

updated database and routes

updated database and routes
uzairrizwan1 9 months ago
parent
commit
9ba08e0c28
7 changed files with 387 additions and 1249 deletions
  1. 225 0
      config/database.js
  2. 0 129
      middleware/errorHandler.js
  3. 0 367
      services/priceUpdateService.js
  4. 27 81
      src/server.js
  5. 0 203
      tests/candleValidation.test.js
  6. 0 166
      tests/server.test.js
  7. 135 303
      utils/databaseInit.js

+ 225 - 0
config/database.js

@@ -1 +1,226 @@
 
 
+const mysql = require('mysql2/promise');
+const sqlite3 = require('sqlite3').verbose();
+const { Sequelize } = require('sequelize');
+const { logger } = require('../utils/logger');
+
+let sequelize = null;
+let mysqlConnection = null;
+let sqliteDb = null;
+
+const connectDatabase = async () => {
+  try {
+    const dbType = process.env.DB_TYPE || 'postgres';
+
+    switch (dbType) {
+      case 'mysql':
+        await connectMySQL();
+        break;
+      case 'postgres':
+        await connectPostgreSQL();
+        break;
+      case 'sqlite':
+      default:
+        await connectSQLite();
+        break;
+    }
+
+    logger.info(`Database connected successfully: ${dbType}`);
+    return true;
+  } catch (error) {
+    logger.error('Database connection failed:', error);
+    throw error;
+  }
+};
+
+const connectMySQL = async () => {
+  try {
+    // Create connection pool
+    mysqlConnection = mysql.createPool({
+      host: process.env.DB_HOST || 'localhost',
+      port: process.env.DB_PORT || 3306,
+      user: process.env.DB_USER || 'root',
+      password: process.env.DB_PASSWORD || '',
+      database: process.env.DB_NAME || 'financial_data',
+      waitForConnections: true,
+      connectionLimit: 10,
+      queueLimit: 0,
+      acquireTimeout: 60000,
+      timeout: 60000
+    });
+
+    // Test the connection
+    const connection = await mysqlConnection.getConnection();
+    await connection.ping();
+    connection.release();
+
+    logger.info('MySQL database connected successfully');
+  } catch (error) {
+    logger.error('MySQL connection failed:', error);
+    throw error;
+  }
+};
+
+// PostgreSQL connection function
+const connectPostgreSQL = async () => {
+  try {
+    sequelize = new Sequelize(
+      process.env.DB_NAME || 'financial_data',
+      process.env.DB_USER || 'postgres',
+      process.env.DB_PASSWORD || 'mqldev@123',
+      {
+        host: process.env.DB_HOST || 'localhost',
+        port: process.env.DB_PORT || 5432,
+        dialect: 'postgres',
+        pool: {
+          max: 10,
+          min: 0,
+          acquire: 60000,
+          idle: 10000
+        },
+        logging: process.env.NODE_ENV === 'development' ? logger.info.bind(logger) : false
+      }
+    );
+
+    await sequelize.authenticate();
+    logger.info('PostgreSQL database connected successfully');
+  } catch (error) {
+    logger.error('PostgreSQL connection failed:', error);
+    throw error;
+  }
+};
+
+const connectSQLite = async () => {
+  return new Promise((resolve, reject) => {
+    const dbFile = process.env.DB_FILE || './data/financial_data.db';
+
+    sqliteDb = new sqlite3.Database(dbFile, (err) => {
+      if (err) {
+        logger.error('SQLite connection failed:', err);
+        reject(err);
+      } else {
+        logger.info('SQLite database connected successfully');
+        resolve();
+      }
+    });
+  });
+};
+
+const getMySQLConnection = () => {
+  if (!mysqlConnection) {
+    throw new Error('MySQL connection not established');
+  }
+  return mysqlConnection;
+};
+
+const getSequelizeInstance = () => {
+  if (!sequelize) {
+    throw new Error('Sequelize instance not established');
+  }
+  return sequelize;
+};
+
+const getSQLiteInstance = () => {
+  if (!sqliteDb) {
+    throw new Error('SQLite database not established');
+  }
+  return sqliteDb;
+};
+
+const closeDatabase = async () => {
+  try {
+    if (mysqlConnection) {
+      await mysqlConnection.end();
+      logger.info('MySQL connection closed');
+    }
+
+    if (sequelize) {
+      await sequelize.close();
+      logger.info('PostgreSQL connection closed');
+    }
+
+    if (sqliteDb) {
+      await new Promise((resolve) => {
+        sqliteDb.close((err) => {
+          if (err) {
+            logger.error('Error closing SQLite database:', err);
+          } else {
+            logger.info('SQLite connection closed');
+          }
+          resolve();
+        });
+      });
+    }
+  } catch (error) {
+    logger.error('Error closing database connections:', error);
+  }
+};
+
+// Database query helpers
+const executeQuery = async (query, params = []) => {
+  const dbType = process.env.DB_TYPE || 'postgres';
+
+  switch (dbType) {
+    case 'mysql':
+      const [rows] = await mysqlConnection.execute(query, params);
+      return rows;
+
+    case 'postgres':
+      const [results] = await sequelize.query(query, {
+        replacements: params,
+        type: Sequelize.QueryTypes.SELECT
+      });
+      return results;
+
+    case 'sqlite':
+    default:
+      return new Promise((resolve, reject) => {
+        sqliteDb.all(query, params, (err, rows) => {
+          if (err) {
+            reject(err);
+          } else {
+            resolve(rows);
+          }
+        });
+      });
+  }
+};
+
+const executeNonQuery = async (query, params = []) => {
+  const dbType = process.env.DB_TYPE || 'postgres';
+
+  switch (dbType) {
+    case 'mysql':
+      const [result] = await mysqlConnection.execute(query, params);
+      return result;
+
+    case 'postgres':
+      const [updateResults] = await sequelize.query(query, {
+        replacements: params,
+        type: Sequelize.QueryTypes.UPDATE
+      });
+      return updateResults;
+
+    case 'sqlite':
+    default:
+      return new Promise((resolve, reject) => {
+        sqliteDb.run(query, params, function(err) {
+          if (err) {
+            reject(err);
+          } else {
+            resolve({ changes: this.changes, lastID: this.lastID });
+          }
+        });
+      });
+  }
+};
+
+module.exports = {
+  connectDatabase,
+  closeDatabase,
+  executeQuery,
+  executeNonQuery,
+  getMySQLConnection,
+  getSequelizeInstance,
+  getSQLiteInstance
+};

+ 0 - 129
middleware/errorHandler.js

@@ -1,129 +0,0 @@
-const { logger } = require('../utils/logger');
-
-// Custom Error class
-class AppError extends Error {
-  constructor(message, statusCode, isOperational = true) {
-    super(message);
-    this.statusCode = statusCode;
-    this.isOperational = isOperational;
-    this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
-
-    Error.captureStackTrace(this, this.constructor);
-  }
-}
-
-// Global error handler middleware
-const errorHandler = (err, req, res, next) => {
-  let error = { ...err };
-  error.message = err.message;
-
-  // Log error
-  logger.error('Error occurred:', {
-    message: err.message,
-    stack: err.stack,
-    url: req.originalUrl,
-    method: req.method,
-    ip: req.ip,
-    userAgent: req.get('User-Agent')
-  });
-
-  // Mongoose bad ObjectId
-  if (err.name === 'CastError') {
-    const message = 'Resource not found';
-    error = new AppError(message, 404);
-  }
-
-  // Mongoose duplicate key
-  if (err.code === 11000) {
-    const message = 'Duplicate field value entered';
-    error = new AppError(message, 400);
-  }
-
-  // Mongoose validation error
-  if (err.name === 'ValidationError') {
-    const message = Object.values(err.errors).map(val => val.message).join(', ');
-    error = new AppError(message, 400);
-  }
-
-  // JWT errors
-  if (err.name === 'JsonWebTokenError') {
-    const message = 'Invalid token';
-    error = new AppError(message, 401);
-  }
-
-  if (err.name === 'TokenExpiredError') {
-    const message = 'Token expired';
-    error = new AppError(message, 401);
-  }
-
-  // Database connection errors
-  if (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND') {
-    const message = 'Database connection failed';
-    error = new AppError(message, 503);
-  }
-
-  // Send error response
-  const statusCode = error.statusCode || 500;
-  const status = error.status || 'error';
-  const message = error.message || 'Something went wrong';
-
-  res.status(statusCode).json({
-    status,
-    message,
-    ...(process.env.NODE_ENV === 'development' && {
-      stack: err.stack,
-      error: err
-    })
-  });
-};
-
-// 404 handler
-const notFound = (req, res, next) => {
-  const error = new AppError(`Not found - ${req.originalUrl}`, 404);
-  next(error);
-};
-
-// Async error wrapper
-const catchAsync = (fn) => {
-  return (req, res, next) => {
-    fn(req, res, next).catch(next);
-  };
-};
-
-// Development error sender (for debugging)
-const sendErrorDev = (err, res) => {
-  res.status(err.statusCode || 500).json({
-    status: err.status,
-    error: err,
-    message: err.message,
-    stack: err.stack
-  });
-};
-
-// Production error sender (security-conscious)
-const sendErrorProd = (err, res) => {
-  // Operational, trusted error: send message to client
-  if (err.isOperational) {
-    res.status(err.statusCode).json({
-      status: err.status,
-      message: err.message
-    });
-  } else {
-    // Programming or other unknown error: don't leak error details
-    logger.error('ERROR 💥', err);
-
-    res.status(500).json({
-      status: 'error',
-      message: 'Something went wrong!'
-    });
-  }
-};
-
-module.exports = {
-  AppError,
-  errorHandler,
-  notFound,
-  catchAsync,
-  sendErrorDev,
-  sendErrorProd
-};

+ 0 - 367
services/priceUpdateService.js

@@ -1,367 +0,0 @@
-const { logger } = require('../utils/logger');
-const { executeQuery } = require('../config/database');
-const { broadcastPriceUpdate, broadcastMultiplePriceUpdates } = require('./websocketService');
-const { addTick } = require('./candlestickService');
-
-// In-memory cache for current prices
-const priceCache = new Map();
-const symbolSubscriptions = new Map();
-
-// Simulated price data for development
-const generateMockPriceData = (symbol) => {
-  const basePrice = priceCache.get(symbol)?.close || 1.0;
-  const change = (Math.random() - 0.5) * 0.02; // ±1% change
-  const newPrice = basePrice * (1 + change);
-
-  return {
-    symbol,
-    open: basePrice,
-    high: Math.max(basePrice, newPrice) * (1 + Math.random() * 0.001),
-    low: Math.min(basePrice, newPrice) * (1 - Math.random() * 0.001),
-    close: newPrice,
-    volume: Math.floor(Math.random() * 1000000) + 100000,
-    timestamp: new Date(),
-    timeframe: 'H1'
-  };
-};
-
-class PriceUpdateService {
-  constructor() {
-    this.isRunning = false;
-    this.updateInterval = null;
-    this.supportedSymbols = [];
-    this.updateCallbacks = [];
-
-    this.initialize();
-  }
-
-  initialize() {
-    // Load supported symbols from environment
-    this.supportedSymbols = (process.env.SUPPORTED_SYMBOLS || 'EURUSD,GBPUSD,USDJPY')
-      .split(',')
-      .map(symbol => symbol.trim());
-
-    logger.info(`Price update service initialized with symbols: ${this.supportedSymbols.join(', ')}`);
-
-    // Initialize price cache with mock data
-    this.initializePriceCache();
-  }
-
-  initializePriceCache() {
-    this.supportedSymbols.forEach(symbol => {
-      priceCache.set(symbol, generateMockPriceData(symbol));
-    });
-    logger.info('Price cache initialized');
-  }
-
-  start() {
-    if (this.isRunning) {
-      logger.warn('Price update service is already running');
-      return;
-    }
-
-    const interval = parseInt(process.env.PRICE_UPDATE_INTERVAL) || 1000;
-
-    this.updateInterval = setInterval(() => {
-      this.updatePrices();
-    }, interval);
-
-    this.isRunning = true;
-    logger.info(`Price update service started with ${interval}ms interval`);
-
-    // Start cleanup interval
-    setInterval(() => {
-      this.cleanup();
-    }, 60000); // Cleanup every minute
-  }
-
-  stop() {
-    if (!this.isRunning) {
-      return;
-    }
-
-    if (this.updateInterval) {
-      clearInterval(this.updateInterval);
-      this.updateInterval = null;
-    }
-
-    this.isRunning = false;
-    logger.info('Price update service stopped');
-  }
-
-  async updatePrices() {
-    try {
-      const updates = [];
-
-      for (const symbol of this.supportedSymbols) {
-        const priceData = await this.generatePriceUpdate(symbol);
-        updates.push(priceData);
-
-        // Update cache
-        priceCache.set(symbol, priceData);
-
-        // Store in database (if needed)
-        await this.storePriceData(priceData);
-      }
-
-      // Broadcast updates to WebSocket clients
-      if (updates.length > 0) {
-        broadcastMultiplePriceUpdates(updates);
-      }
-
-      // Call registered update callbacks
-      this.updateCallbacks.forEach(callback => {
-        try {
-          callback(updates);
-        } catch (error) {
-          logger.error('Error in price update callback:', error);
-        }
-      });
-
-    } catch (error) {
-      logger.error('Error updating prices:', error);
-    }
-  }
-
-  async generatePriceUpdate(symbol) {
-    // In a real implementation, this would fetch from external APIs
-    // For now, we'll generate mock data
-    const currentPrice = priceCache.get(symbol);
-    const change = (Math.random() - 0.5) * 0.02; // ±1% change
-    const newPrice = currentPrice ? currentPrice.close * (1 + change) : 1.0;
-
-    const priceData = {
-      symbol,
-      open: currentPrice ? currentPrice.close : newPrice,
-      high: Math.max(currentPrice ? currentPrice.close : newPrice, newPrice) * (1 + Math.random() * 0.001),
-      low: Math.min(currentPrice ? currentPrice.close : newPrice, newPrice) * (1 - Math.random() * 0.001),
-      close: newPrice,
-      volume: Math.floor(Math.random() * 1000000) + 100000,
-      timestamp: new Date(),
-      timeframe: 'H1'
-    };
-
-    // Add tick data for candlestick aggregation
-    // Generate multiple ticks within this price update period
-    const tickCount = Math.floor(Math.random() * 5) + 3; // 3-7 ticks per update
-    for (let i = 0; i < tickCount; i++) {
-      const tickPrice = newPrice + (Math.random() - 0.5) * 0.001; // Small variations
-      const tickTimestamp = new Date(priceData.timestamp.getTime() - (tickCount - i) * 100); // Spread over the interval
-      addTick(symbol, tickPrice, tickTimestamp);
-    }
-
-    return priceData;
-  }
-
-  async storePriceData(priceData) {
-    try {
-      const dbType = process.env.DB_TYPE || 'sqlite';
-
-      if (dbType === 'sqlite') {
-        await this.storePriceDataSQLite(priceData);
-      } else if (dbType === 'mysql') {
-        await this.storePriceDataMySQL(priceData);
-      } else if (dbType === 'postgres') {
-        await this.storePriceDataPostgreSQL(priceData);
-      }
-    } catch (error) {
-      logger.error('Error storing price data:', error);
-    }
-  }
-
-  async storePriceDataSQLite(priceData) {
-    const query = `
-      INSERT OR IGNORE INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
-      VALUES (?, ?, ?, ?, ?, ?, ?, ?)
-    `;
-
-    await executeQuery(query, [
-      priceData.symbol,
-      priceData.timeframe,
-      priceData.timestamp,
-      priceData.open,
-      priceData.high,
-      priceData.low,
-      priceData.close,
-      priceData.volume
-    ]);
-  }
-
-  async storePriceDataMySQL(priceData) {
-    const query = `
-      INSERT IGNORE INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
-      VALUES (?, ?, ?, ?, ?, ?, ?, ?)
-    `;
-
-    await executeQuery(query, [
-      priceData.symbol,
-      priceData.timeframe,
-      priceData.timestamp,
-      priceData.open,
-      priceData.high,
-      priceData.low,
-      priceData.close,
-      priceData.volume
-    ]);
-  }
-
-  async storePriceDataPostgreSQL(priceData) {
-    const query = `
-      INSERT INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
-      VALUES (?, ?, ?, ?, ?, ?, ?, ?)
-      ON CONFLICT DO NOTHING
-    `;
-
-    await executeQuery(query, [
-      priceData.symbol,
-      priceData.timeframe,
-      priceData.timestamp,
-      priceData.open,
-      priceData.high,
-      priceData.low,
-      priceData.close,
-      priceData.volume
-    ]);
-  }
-
-  // Get current price for a symbol
-  getCurrentPrice(symbol) {
-    return priceCache.get(symbol);
-  }
-
-  // Get current prices for multiple symbols
-  getCurrentPrices(symbols = null) {
-    const targetSymbols = symbols || this.supportedSymbols;
-    const prices = {};
-
-    targetSymbols.forEach(symbol => {
-      prices[symbol] = priceCache.get(symbol);
-    });
-
-    return prices;
-  }
-
-  // Get historical data for a symbol
-  async getHistoricalData(symbol, timeframe = 'H1', limit = 1000) {
-    try {
-      const dbType = process.env.DB_TYPE || 'sqlite';
-      let query;
-
-      if (dbType === 'sqlite') {
-        query = `
-          SELECT * FROM price_data
-          WHERE symbol = ? AND timeframe = ?
-          ORDER BY timestamp DESC
-          LIMIT ?
-        `;
-      } else {
-        query = `
-          SELECT * FROM price_data
-          WHERE symbol = ? AND timeframe = ?
-          ORDER BY timestamp DESC
-          LIMIT ?
-        `;
-      }
-
-      const params = [symbol, timeframe, limit];
-      const data = await executeQuery(query, params);
-
-      return data.reverse(); // Return in chronological order
-    } catch (error) {
-      logger.error(`Error fetching historical data for ${symbol}:`, error);
-      return [];
-    }
-  }
-
-  // Register callback for price updates
-  onPriceUpdate(callback) {
-    this.updateCallbacks.push(callback);
-  }
-
-  // Remove price update callback
-  removePriceUpdateCallback(callback) {
-    const index = this.updateCallbacks.indexOf(callback);
-    if (index > -1) {
-      this.updateCallbacks.splice(index, 1);
-    }
-  }
-
-  // Cleanup old data
-  async cleanup() {
-    try {
-      const cutoffDate = new Date();
-      cutoffDate.setDate(cutoffDate.getDate() - 30); // Keep 30 days of data
-
-      const dbType = process.env.DB_TYPE || 'sqlite';
-      let query;
-
-      if (dbType === 'sqlite') {
-        query = 'DELETE FROM price_data WHERE timestamp < ?';
-      } else {
-        query = 'DELETE FROM price_data WHERE timestamp < ?';
-      }
-
-      const params = [cutoffDate];
-      await executeQuery(query, params);
-
-      logger.info('Price data cleanup completed');
-    } catch (error) {
-      logger.error('Error during price data cleanup:', error);
-    }
-  }
-
-  // Get service statistics
-  getStats() {
-    return {
-      isRunning: this.isRunning,
-      supportedSymbols: this.supportedSymbols,
-      cachedSymbols: Array.from(priceCache.keys()),
-      updateCallbacks: this.updateCallbacks.length,
-      cacheSize: priceCache.size,
-      uptime: process.uptime()
-    };
-  }
-}
-
-// Create singleton instance
-const priceUpdateService = new PriceUpdateService();
-
-// Export functions for external use
-const startPriceUpdates = (io) => {
-  priceUpdateService.start();
-};
-
-const stopPriceUpdates = () => {
-  priceUpdateService.stop();
-};
-
-const getCurrentPrice = (symbol) => {
-  return priceUpdateService.getCurrentPrice(symbol);
-};
-
-const getCurrentPrices = (symbols) => {
-  return priceUpdateService.getCurrentPrices(symbols);
-};
-
-const getHistoricalData = (symbol, timeframe, limit) => {
-  return priceUpdateService.getHistoricalData(symbol, timeframe, limit);
-};
-
-const onPriceUpdate = (callback) => {
-  priceUpdateService.onPriceUpdate(callback);
-};
-
-const getPriceUpdateStats = () => {
-  return priceUpdateService.getStats();
-};
-
-module.exports = {
-  startPriceUpdates,
-  stopPriceUpdates,
-  getCurrentPrice,
-  getCurrentPrices,
-  getHistoricalData,
-  onPriceUpdate,
-  getPriceUpdateStats,
-  priceUpdateService
-};

+ 27 - 81
src/server.js

@@ -1,74 +1,49 @@
 const express = require('express');
 const express = require('express');
-const http = require('http');
-const socketIo = require('socket.io');
 const cors = require('cors');
 const cors = require('cors');
-const helmet = require('helmet');
-const morgan = require('morgan');
-const compression = require('compression');
-const rateLimit = require('express-rate-limit');
-
+const path = require('path');
 require('dotenv').config();
 require('dotenv').config();
 
 
-// Import custom modules
-const { errorHandler } = require('../middleware/errorHandler');
 const { logger } = require('../utils/logger');
 const { logger } = require('../utils/logger');
 const { connectDatabase } = require('../config/database');
 const { connectDatabase } = require('../config/database');
 const { initializeDatabase } = require('../utils/databaseInit');
 const { initializeDatabase } = require('../utils/databaseInit');
 const priceRoutes = require('../routes/priceRoutes');
 const priceRoutes = require('../routes/priceRoutes');
-const symbolRoutes = require('../routes/symbolRoutes');
-const { initializeWebSocket } = require('../services/websocketService');
-const { startPriceUpdates } = require('../services/priceUpdateService');
 
 
-class FinancialDataServer {
+class MarketDataServer {
   constructor() {
   constructor() {
     this.app = express();
     this.app = express();
-    this.server = http.createServer(this.app);
-    this.io = socketIo(this.server, {
-      cors: {
-        origin: process.env.CLIENT_URL || "http://localhost:3000",
-        methods: ["GET", "POST"]
-      }
-    });
     this.port = process.env.PORT || 3001;
     this.port = process.env.PORT || 3001;
 
 
     this.initializeMiddleware();
     this.initializeMiddleware();
     this.initializeRoutes();
     this.initializeRoutes();
-    this.initializeWebSocket();
-    this.initializeErrorHandling();
     this.initializeDatabase();
     this.initializeDatabase();
   }
   }
 
 
   initializeMiddleware() {
   initializeMiddleware() {
-    // Security middleware
-    this.app.use(helmet());
-
-    // CORS configuration
+    // ✅ Enable CORS
     this.app.use(cors({
     this.app.use(cors({
       origin: process.env.CLIENT_URL || "http://localhost:3000",
       origin: process.env.CLIENT_URL || "http://localhost:3000",
       credentials: true
       credentials: true
     }));
     }));
 
 
-    // Rate limiting
-    const limiter = rateLimit({
-      windowMs: 15 * 60 * 1000, // 15 minutes
-      max: 1000, // limit each IP to 1000 requests per windowMs
-      message: 'Too many requests from this IP, please try again later.'
-    });
-    this.app.use('/api/', limiter);
-
-    // Body parsing middleware
-    this.app.use(express.json({ limit: '10mb' }));
-    this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));
-
-    // Compression middleware
-    this.app.use(compression());
+    // ✅ Enable JSON and URL Encoded body parsing
+    this.app.use(express.json({
+      strict: true, // reject invalid JSON
+      limit: '1mb'  // prevent large payload attacks
+    }));
+    this.app.use(express.urlencoded({ extended: true }));
 
 
-    // Logging middleware
-    this.app.use(morgan('combined', { stream: { write: msg => logger.info(msg.trim()) } }));
+    // ✅ Handle invalid JSON gracefully
+    this.app.use((err, req, res, next) => {
+      if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
+        logger.error('Invalid JSON received:', err.message);
+        return res.status(400).json({ success: false, message: 'Invalid JSON format' });
+      }
+      next();
+    });
   }
   }
 
 
   initializeRoutes() {
   initializeRoutes() {
-    // Health check endpoint
+    // Health check
     this.app.get('/health', (req, res) => {
     this.app.get('/health', (req, res) => {
       res.status(200).json({
       res.status(200).json({
         status: 'OK',
         status: 'OK',
@@ -77,27 +52,11 @@ class FinancialDataServer {
       });
       });
     });
     });
 
 
-    // API routes
-    this.app.use('/api/prices', priceRoutes);
-    this.app.use('/api/symbols', symbolRoutes);
-
-    // Serve static files from public directory
-    this.app.use(express.static('public'));
+    // ✅ API Routes
+    this.app.use('/api', priceRoutes);
 
 
-    // Catch all handler: send back React's index.html file for client-side routing
-    if (process.env.NODE_ENV === 'production') {
-      this.app.get('*', (req, res) => {
-        res.sendFile(path.join(__dirname, '../public/index.html'));
-      });
-    }
-  }
-
-  initializeWebSocket() {
-    initializeWebSocket(this.io);
-  }
-
-  initializeErrorHandling() {
-    this.app.use(errorHandler);
+    // ✅ Serve static files
+    this.app.use(express.static(path.join(__dirname, '../public')));
   }
   }
 
 
   async initializeDatabase() {
   async initializeDatabase() {
@@ -105,7 +64,6 @@ class FinancialDataServer {
       await connectDatabase();
       await connectDatabase();
       logger.info('Database connected successfully');
       logger.info('Database connected successfully');
 
 
-      // Initialize database tables and sample data
       await initializeDatabase();
       await initializeDatabase();
       logger.info('Database initialized successfully');
       logger.info('Database initialized successfully');
     } catch (error) {
     } catch (error) {
@@ -114,20 +72,12 @@ class FinancialDataServer {
     }
     }
   }
   }
 
 
-  startPriceUpdates() {
-    startPriceUpdates(this.io);
-  }
-
   async start() {
   async start() {
     try {
     try {
-      this.server.listen(this.port, () => {
-        logger.info(`Financial Data Server running on port ${this.port}`);
+      this.app.listen(this.port, () => {
+        logger.info(`✅ Market Data Server running on port ${this.port}`);
         logger.info(`Environment: ${process.env.NODE_ENV || 'development'}`);
         logger.info(`Environment: ${process.env.NODE_ENV || 'development'}`);
       });
       });
-
-      // Start real-time price updates
-      this.startPriceUpdates();
-
     } catch (error) {
     } catch (error) {
       logger.error('Failed to start server:', error);
       logger.error('Failed to start server:', error);
       process.exit(1);
       process.exit(1);
@@ -136,21 +86,17 @@ class FinancialDataServer {
 
 
   async stop() {
   async stop() {
     logger.info('Shutting down server...');
     logger.info('Shutting down server...');
-    this.server.close(() => {
-      logger.info('Server closed');
-      process.exit(0);
-    });
+    process.exit(0);
   }
   }
 }
 }
 
 
-// Create and start server
-const server = new FinancialDataServer();
+// Create and start server
+const server = new MarketDataServer();
 
 
 // Graceful shutdown
 // Graceful shutdown
 process.on('SIGTERM', () => server.stop());
 process.on('SIGTERM', () => server.stop());
 process.on('SIGINT', () => server.stop());
 process.on('SIGINT', () => server.stop());
 
 
-// Start the server
 server.start().catch(error => {
 server.start().catch(error => {
   logger.error('Failed to start server:', error);
   logger.error('Failed to start server:', error);
   process.exit(1);
   process.exit(1);

+ 0 - 203
tests/candleValidation.test.js

@@ -1,203 +0,0 @@
-const request = require('supertest');
-const { expect } = require('chai');
-const app = require('../src/server');
-const { getHistoricalCandles, validateCandles, generateTestCandles } = require('../services/candlestickService');
-
-describe('Candlestick API Tests', () => {
-  const testSymbol = 'EURUSD';
-  const testTimeframe = 'H1';
-
-  describe('GET /api/prices/validate-candles/:symbol', () => {
-    it('should validate candles for a valid symbol', async () => {
-      const response = await request(app)
-        .get(`/api/prices/validate-candles/${testSymbol}`)
-        .query({ timeframe: testTimeframe, limit: 50 })
-        .expect(200);
-
-      expect(response.body.status).to.equal('success');
-      expect(response.body.data).to.have.property('total');
-      expect(response.body.data).to.have.property('valid');
-      expect(response.body.data).to.have.property('invalid');
-      expect(response.body.data.total).to.be.greaterThan(0);
-    });
-
-    it('should return 400 for missing symbol', async () => {
-      const response = await request(app)
-        .get('/api/prices/validate-candles/')
-        .expect(404); // Express will return 404 for missing route param
-    });
-
-    it('should handle different timeframes', async () => {
-      const timeframes = ['M1', 'M5', 'M15', 'H1', 'H4', 'D1'];
-
-      for (const timeframe of timeframes) {
-        const response = await request(app)
-          .get(`/api/prices/validate-candles/${testSymbol}`)
-          .query({ timeframe, limit: 10 })
-          .expect(200);
-
-        expect(response.body.status).to.equal('success');
-        expect(response.body.timeframe).to.equal(timeframe);
-      }
-    });
-  });
-
-  describe('GET /api/prices/generate-test-candles/:symbol', () => {
-    it('should generate test candles for a valid symbol', async () => {
-      const response = await request(app)
-        .get(`/api/prices/generate-test-candles/${testSymbol}`)
-        .query({ timeframe: testTimeframe, count: 20 })
-        .expect(200);
-
-      expect(response.body.status).to.equal('success');
-      expect(response.body.data).to.be.an('array');
-      expect(response.body.data.length).to.equal(20);
-      expect(response.body.validation).to.have.property('total', 20);
-    });
-
-    it('should validate generated test candles', async () => {
-      const response = await request(app)
-        .get(`/api/prices/generate-test-candles/${testSymbol}`)
-        .query({ timeframe: testTimeframe, count: 10 })
-        .expect(200);
-
-      expect(response.body.meta.validationPassed).to.equal(true);
-      expect(response.body.meta.successRate).to.equal('100.00%');
-    });
-  });
-
-  describe('GET /api/prices/candle-info/:symbol', () => {
-    it('should return candle statistics for a valid symbol', async () => {
-      const response = await request(app)
-        .get(`/api/prices/candle-info/${testSymbol}`)
-        .query({ timeframe: testTimeframe })
-        .expect(200);
-
-      expect(response.body.status).to.equal('success');
-      expect(response.body.data).to.have.property('count');
-      expect(response.body.data).to.have.property('priceRange');
-      expect(response.body.data).to.have.property('volume');
-      expect(response.body.data.symbol).to.equal(testSymbol.toUpperCase());
-    });
-  });
-
-  describe('Candlestick Service Unit Tests', () => {
-    it('should generate valid test candles', () => {
-      const candles = generateTestCandles(testSymbol, testTimeframe, 10);
-      expect(candles).to.have.length(10);
-
-      candles.forEach(candle => {
-        expect(candle.symbol).to.equal(testSymbol);
-        expect(candle.timeframe).to.equal(testTimeframe);
-        expect(candle).to.have.property('open');
-        expect(candle).to.have.property('high');
-        expect(candle).to.have.property('low');
-        expect(candle).to.have.property('close');
-        expect(candle).to.have.property('volume');
-        expect(candle).to.have.property('timestamp');
-      });
-    });
-
-    it('should validate correct candles', () => {
-      const validCandles = generateTestCandles(testSymbol, testTimeframe, 5);
-      const results = validateCandles(validCandles);
-
-      expect(results.total).to.equal(5);
-      expect(results.valid).to.equal(5);
-      expect(results.invalid).to.equal(0);
-      expect(results.issues).to.have.length(0);
-    });
-
-    it('should detect invalid candles', () => {
-      const invalidCandles = [
-        {
-          symbol: testSymbol,
-          timeframe: testTimeframe,
-          timestamp: new Date(),
-          open: 'invalid',
-          high: 1.1,
-          low: 1.0,
-          close: 1.05,
-          volume: 1000
-        },
-        {
-          symbol: testSymbol,
-          timeframe: testTimeframe,
-          timestamp: new Date(),
-          open: 1.0,
-          high: 0.9, // Invalid: high < open
-          low: 1.0,
-          close: 1.05,
-          volume: 1000
-        }
-      ];
-
-      const results = validateCandles(invalidCandles);
-      expect(results.invalid).to.be.greaterThan(0);
-      expect(results.issues.length).to.be.greaterThan(0);
-    });
-  });
-
-  describe('GET /api/prices/historical/:symbol (Integration)', () => {
-    it('should return historical data that can be validated', async () => {
-      const response = await request(app)
-        .get(`/api/prices/historical/${testSymbol}`)
-        .query({ timeframe: testTimeframe, limit: 20 })
-        .expect(200);
-
-      expect(response.body.status).to.equal('success');
-      expect(response.body.data).to.be.an('array');
-
-      if (response.body.data.length > 0) {
-        // Validate the returned candles
-        const validationResults = validateCandles(response.body.data);
-        expect(validationResults.total).to.equal(response.body.data.length);
-
-        // Log validation results for debugging
-        console.log(`Historical data validation: ${validationResults.valid}/${validationResults.total} valid candles`);
-      }
-    });
-  });
-
-  describe('Chart Data Format Tests', () => {
-    it('should return data in correct format for charting', async () => {
-      const response = await request(app)
-        .get(`/api/prices/historical/${testSymbol}`)
-        .query({ timeframe: testTimeframe, limit: 10 })
-        .expect(200);
-
-      expect(response.body.status).to.equal('success');
-      expect(response.body.data).to.be.an('array');
-
-      if (response.body.data.length > 0) {
-        const candle = response.body.data[0];
-
-        // Check required fields for charting
-        expect(candle).to.have.property('timestamp');
-        expect(candle).to.have.property('open');
-        expect(candle).to.have.property('high');
-        expect(candle).to.have.property('low');
-        expect(candle).to.have.property('close');
-
-        // Validate data types
-        expect(typeof candle.open).to.equal('number');
-        expect(typeof candle.high).to.equal('number');
-        expect(typeof candle.low).to.equal('number');
-        expect(typeof candle.close).to.equal('number');
-
-        // Validate price logic
-        expect(candle.high).to.be.at.least(Math.max(candle.open, candle.close));
-        expect(candle.low).to.be.at.most(Math.min(candle.open, candle.close));
-      }
-    });
-  });
-});
-
-// Helper function to wait for price updates
-const waitForPriceUpdate = (ms = 2000) => {
-  return new Promise(resolve => setTimeout(resolve, ms));
-};
-
-module.exports = {
-  waitForPriceUpdate
-};

+ 0 - 166
tests/server.test.js

@@ -1,166 +0,0 @@
-const request = require('supertest');
-const { expect } = require('chai');
-const server = require('../src/server');
-
-describe('Financial Data Server', () => {
-  let app;
-
-  before(async () => {
-    // Get the Express app instance from the server
-    app = server.app;
-  });
-
-  describe('Health Check', () => {
-    it('should return server health status', (done) => {
-      request(app)
-        .get('/health')
-        .expect(200)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'OK');
-          expect(res.body).to.have.property('timestamp');
-          expect(res.body).to.have.property('uptime');
-          done();
-        });
-    });
-  });
-
-  describe('Price API', () => {
-    it('should return current price for valid symbol', (done) => {
-      request(app)
-        .get('/api/prices/current/EURUSD')
-        .expect(200)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'success');
-          expect(res.body).to.have.property('data');
-          expect(res.body.data).to.have.property('symbol', 'EURUSD');
-          expect(res.body.data).to.have.property('price');
-          expect(res.body.data).to.have.property('change');
-          expect(res.body.data).to.have.property('changePercent');
-          done();
-        });
-    });
-
-    it('should return 404 for invalid symbol', (done) => {
-      request(app)
-        .get('/api/prices/current/INVALID')
-        .expect(404)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'error');
-          expect(res.body).to.have.property('message');
-          done();
-        });
-    });
-
-    it('should return historical data for valid symbol', (done) => {
-      request(app)
-        .get('/api/prices/historical/EURUSD?timeframe=H1&limit=10')
-        .expect(200)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'success');
-          expect(res.body).to.have.property('data');
-          expect(res.body.data).to.be.an('array');
-          expect(res.body).to.have.property('symbol', 'EURUSD');
-          expect(res.body).to.have.property('timeframe', 'H1');
-          done();
-        });
-    });
-  });
-
-  describe('Symbol API', () => {
-    it('should return list of symbols', (done) => {
-      request(app)
-        .get('/api/symbols')
-        .expect(200)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'success');
-          expect(res.body).to.have.property('data');
-          expect(res.body.data).to.be.an('array');
-          expect(res.body).to.have.property('pagination');
-          done();
-        });
-    });
-
-    it('should return specific symbol details', (done) => {
-      request(app)
-        .get('/api/symbols/EURUSD')
-        .expect(200)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'success');
-          expect(res.body).to.have.property('data');
-          expect(res.body.data).to.have.property('symbol', 'EURUSD');
-          expect(res.body.data).to.have.property('name');
-          expect(res.body.data).to.have.property('category');
-          expect(res.body.data).to.have.property('exchange');
-          done();
-        });
-    });
-
-    it('should return 404 for non-existent symbol', (done) => {
-      request(app)
-        .get('/api/symbols/NONEXISTENT')
-        .expect(404)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'error');
-          expect(res.body).to.have.property('message');
-          done();
-        });
-    });
-
-    it('should return symbol categories', (done) => {
-      request(app)
-        .get('/api/symbols/categories/list')
-        .expect(200)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'success');
-          expect(res.body).to.have.property('data');
-          expect(res.body.data).to.be.an('array');
-          done();
-        });
-    });
-  });
-
-  describe('Error Handling', () => {
-    it('should handle 404 for unknown routes', (done) => {
-      request(app)
-        .get('/api/unknown-route')
-        .expect(404)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'error');
-          expect(res.body).to.have.property('message');
-          done();
-        });
-    });
-
-    it('should handle malformed JSON', (done) => {
-      request(app)
-        .post('/api/prices/subscribe')
-        .set('Content-Type', 'application/json')
-        .send('{ malformed json }')
-        .expect(400)
-        .end((err, res) => {
-          if (err) return done(err);
-
-          expect(res.body).to.have.property('status', 'error');
-          done();
-        });
-    });
-  });
-});

+ 135 - 303
utils/databaseInit.js

@@ -1,7 +1,5 @@
 const { executeQuery, executeNonQuery } = require('../config/database');
 const { executeQuery, executeNonQuery } = require('../config/database');
 const { logger } = require('./logger');
 const { logger } = require('./logger');
-const fs = require('fs');
-const path = require('path');
 
 
 class DatabaseInitializer {
 class DatabaseInitializer {
   constructor() {
   constructor() {
@@ -18,7 +16,7 @@ class DatabaseInitializer {
       logger.info('Starting database initialization...');
       logger.info('Starting database initialization...');
 
 
       // Create tables based on database type
       // Create tables based on database type
-      const dbType = process.env.DB_TYPE || 'sqlite';
+      const dbType = process.env.DB_TYPE || 'postgres';
 
 
       switch (dbType) {
       switch (dbType) {
         case 'mysql':
         case 'mysql':
@@ -31,9 +29,6 @@ class DatabaseInitializer {
           break;
           break;
       }
       }
 
 
-      // Insert sample data
-      await this.insertSampleData();
-
       this.initialized = true;
       this.initialized = true;
       logger.info('Database initialization completed successfully');
       logger.info('Database initialization completed successfully');
 
 
@@ -45,7 +40,7 @@ class DatabaseInitializer {
 
 
   async initializeRelationalDatabase() {
   async initializeRelationalDatabase() {
     try {
     try {
-      const dbType = process.env.DB_TYPE || 'mysql';
+      const dbType = process.env.DB_TYPE || 'postgres';
 
 
       if (dbType === 'mysql') {
       if (dbType === 'mysql') {
         await this.initializeMySQL();
         await this.initializeMySQL();
@@ -61,47 +56,45 @@ class DatabaseInitializer {
 
 
   async initializeMySQL() {
   async initializeMySQL() {
     try {
     try {
-      // Create price_data table for MySQL
-      const createPriceTableQuery = `
-        CREATE TABLE IF NOT EXISTS price_data (
-          id INT PRIMARY KEY AUTO_INCREMENT,
-          symbol VARCHAR(10) NOT NULL,
-          timeframe VARCHAR(5) NOT NULL,
-          timestamp DATETIME NOT NULL,
-          open DECIMAL(15,8) NOT NULL,
-          high DECIMAL(15,8) NOT NULL,
-          low DECIMAL(15,8) NOT NULL,
-          close DECIMAL(15,8) NOT NULL,
-          volume BIGINT NOT NULL,
-          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-          INDEX idx_symbol_timeframe (symbol, timeframe),
-          INDEX idx_timestamp (timestamp),
-          INDEX idx_symbol_timestamp (symbol, timestamp)
+      // Create candles_h1 table for storing H1 candlestick data
+      const createCandlesH1TableQuery = `
+        CREATE TABLE IF NOT EXISTS candles_h1 (
+          id INT AUTO_INCREMENT PRIMARY KEY,
+          symbol TEXT NOT NULL,
+          market TEXT NOT NULL,
+          open FLOAT NOT NULL,
+          high FLOAT NOT NULL,
+          low FLOAT NOT NULL,
+          close FLOAT NOT NULL,
+          volume FLOAT NOT NULL,
+          timestamp TIMESTAMP NOT NULL,
+          UNIQUE KEY unique_symbol_timestamp (symbol, timestamp),
+          INDEX idx_symbol (symbol),
+          INDEX idx_market (market),
+          INDEX idx_timestamp (timestamp)
         ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
         ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
       `;
       `;
 
 
-      await executeNonQuery(createPriceTableQuery);
-      logger.info('MySQL price data table created successfully');
-
-      // Create symbols table for MySQL
-      const createSymbolsTableQuery = `
-        CREATE TABLE IF NOT EXISTS symbols (
-          id INT PRIMARY KEY AUTO_INCREMENT,
-          symbol VARCHAR(10) UNIQUE NOT NULL,
-          name VARCHAR(100) NOT NULL,
-          category VARCHAR(20) NOT NULL,
-          exchange VARCHAR(20) NOT NULL,
-          is_active BOOLEAN DEFAULT TRUE,
-          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+      await executeNonQuery(createCandlesH1TableQuery);
+      logger.info('MySQL candles_h1 table created successfully');
+
+      // Create live_prices table for real-time price updates
+      const createLivePricesTableQuery = `
+        CREATE TABLE IF NOT EXISTS live_prices (
+          id INT AUTO_INCREMENT PRIMARY KEY,
+          symbol TEXT NOT NULL,
+          market TEXT NOT NULL,
+          price FLOAT NOT NULL,
+          updated_at TIMESTAMP NOT NULL,
+          UNIQUE KEY unique_symbol_market (symbol, market),
           INDEX idx_symbol (symbol),
           INDEX idx_symbol (symbol),
-          INDEX idx_category (category),
-          INDEX idx_exchange (exchange)
+          INDEX idx_market (market),
+          INDEX idx_updated_at (updated_at)
         ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
         ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
       `;
       `;
 
 
-      await executeNonQuery(createSymbolsTableQuery);
-      logger.info('MySQL symbols table created successfully');
+      await executeNonQuery(createLivePricesTableQuery);
+      logger.info('MySQL live_prices table created successfully');
 
 
     } catch (error) {
     } catch (error) {
       logger.error('Error creating MySQL tables:', error);
       logger.error('Error creating MySQL tables:', error);
@@ -111,54 +104,65 @@ class DatabaseInitializer {
 
 
   async initializePostgreSQL() {
   async initializePostgreSQL() {
     try {
     try {
-      // Create price_data table for PostgreSQL
-      const createPriceTableQuery = `
-        CREATE TABLE IF NOT EXISTS price_data (
+      // Create candles_h1 table for PostgreSQL
+      const createCandlesH1TableQuery = `
+        CREATE TABLE IF NOT EXISTS candles_h1 (
           id SERIAL PRIMARY KEY,
           id SERIAL PRIMARY KEY,
-          symbol VARCHAR(10) NOT NULL,
-          timeframe VARCHAR(5) NOT NULL,
+          symbol TEXT NOT NULL,
+          market TEXT NOT NULL,
+          open FLOAT NOT NULL,
+          high FLOAT NOT NULL,
+          low FLOAT NOT NULL,
+          close FLOAT NOT NULL,
+          volume FLOAT NOT NULL,
           timestamp TIMESTAMP NOT NULL,
           timestamp TIMESTAMP NOT NULL,
-          open DECIMAL(15,8) NOT NULL,
-          high DECIMAL(15,8) NOT NULL,
-          low DECIMAL(15,8) NOT NULL,
-          close DECIMAL(15,8) NOT NULL,
-          volume BIGINT NOT NULL,
-          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+          UNIQUE(symbol, timestamp)
         );
         );
       `;
       `;
 
 
-      await executeNonQuery(createPriceTableQuery);
-      logger.info('PostgreSQL price data table created successfully');
+      await executeNonQuery(createCandlesH1TableQuery);
+      logger.info('PostgreSQL candles_h1 table created successfully');
 
 
-      // Create indexes for PostgreSQL
-      const createIndexes = [
-        'CREATE INDEX IF NOT EXISTS idx_symbol_timeframe ON price_data(symbol, timeframe);',
-        'CREATE INDEX IF NOT EXISTS idx_timestamp ON price_data(timestamp);',
-        'CREATE INDEX IF NOT EXISTS idx_symbol_timestamp ON price_data(symbol, timestamp);'
+      // Create indexes for candles_h1
+      const createCandlesH1Indexes = [
+        'CREATE INDEX IF NOT EXISTS idx_candles_symbol ON candles_h1(symbol);',
+        'CREATE INDEX IF NOT EXISTS idx_candles_market ON candles_h1(market);',
+        'CREATE INDEX IF NOT EXISTS idx_candles_timestamp ON candles_h1(timestamp);'
       ];
       ];
 
 
-      for (const indexQuery of createIndexes) {
+      for (const indexQuery of createCandlesH1Indexes) {
         await executeNonQuery(indexQuery);
         await executeNonQuery(indexQuery);
       }
       }
 
 
-      logger.info('PostgreSQL indexes created successfully');
+      logger.info('PostgreSQL candles_h1 indexes created successfully');
 
 
-      // Create symbols table for PostgreSQL
-      const createSymbolsTableQuery = `
-        CREATE TABLE IF NOT EXISTS symbols (
+      // Create live_prices table for PostgreSQL
+      const createLivePricesTableQuery = `
+        CREATE TABLE IF NOT EXISTS live_prices (
           id SERIAL PRIMARY KEY,
           id SERIAL PRIMARY KEY,
-          symbol VARCHAR(10) UNIQUE NOT NULL,
-          name VARCHAR(100) NOT NULL,
-          category VARCHAR(20) NOT NULL,
-          exchange VARCHAR(20) NOT NULL,
-          is_active BOOLEAN DEFAULT TRUE,
-          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+          symbol TEXT NOT NULL,
+          market TEXT NOT NULL,
+          price FLOAT NOT NULL,
+          updated_at TIMESTAMP NOT NULL,
+          UNIQUE(symbol, market)
         );
         );
       `;
       `;
 
 
-      await executeNonQuery(createSymbolsTableQuery);
-      logger.info('PostgreSQL symbols table created successfully');
+      await executeNonQuery(createLivePricesTableQuery);
+      logger.info('PostgreSQL live_prices table created successfully');
+
+      // Create indexes for live_prices
+      const createLivePricesIndexes = [
+        'CREATE INDEX IF NOT EXISTS idx_live_symbol ON live_prices(symbol);',
+        'CREATE INDEX IF NOT EXISTS idx_live_market ON live_prices(market);',
+        'CREATE INDEX IF NOT EXISTS idx_live_updated_at ON live_prices(updated_at);'
+      ];
+
+      for (const indexQuery of createLivePricesIndexes) {
+        await executeNonQuery(indexQuery);
+      }
+
+      logger.info('PostgreSQL live_prices indexes created successfully');
 
 
     } catch (error) {
     } catch (error) {
       logger.error('Error creating PostgreSQL tables:', error);
       logger.error('Error creating PostgreSQL tables:', error);
@@ -168,259 +172,87 @@ class DatabaseInitializer {
 
 
   async initializeSQLite() {
   async initializeSQLite() {
     try {
     try {
-      // Create price_data table
-      const createPriceTableQuery = `
-        CREATE TABLE IF NOT EXISTS price_data (
+      // Create candles_h1 table for SQLite
+      const createCandlesH1TableQuery = `
+        CREATE TABLE IF NOT EXISTS candles_h1 (
           id INTEGER PRIMARY KEY AUTOINCREMENT,
           id INTEGER PRIMARY KEY AUTOINCREMENT,
           symbol TEXT NOT NULL,
           symbol TEXT NOT NULL,
-          timeframe TEXT NOT NULL,
-          timestamp DATETIME NOT NULL,
+          market TEXT NOT NULL,
           open REAL NOT NULL,
           open REAL NOT NULL,
           high REAL NOT NULL,
           high REAL NOT NULL,
           low REAL NOT NULL,
           low REAL NOT NULL,
           close REAL NOT NULL,
           close REAL NOT NULL,
-          volume INTEGER NOT NULL,
-          created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+          volume REAL NOT NULL,
+          timestamp DATETIME NOT NULL,
+          UNIQUE(symbol, timestamp)
         );
         );
       `;
       `;
 
 
-      await executeNonQuery(createPriceTableQuery);
-      logger.info('SQLite price data table created successfully');
+      await executeNonQuery(createCandlesH1TableQuery);
+      logger.info('SQLite candles_h1 table created successfully');
 
 
-      // Create indexes for better performance
-      const createIndexes = [
-        'CREATE INDEX IF NOT EXISTS idx_symbol_timeframe ON price_data(symbol, timeframe);',
-        'CREATE INDEX IF NOT EXISTS idx_timestamp ON price_data(timestamp);',
-        'CREATE INDEX IF NOT EXISTS idx_symbol_timestamp ON price_data(symbol, timestamp);'
+      // Create indexes for candles_h1
+      const createCandlesH1Indexes = [
+        'CREATE INDEX IF NOT EXISTS idx_candles_symbol ON candles_h1(symbol);',
+        'CREATE INDEX IF NOT EXISTS idx_candles_market ON candles_h1(market);',
+        'CREATE INDEX IF NOT EXISTS idx_candles_timestamp ON candles_h1(timestamp);'
       ];
       ];
 
 
-      for (const indexQuery of createIndexes) {
+      for (const indexQuery of createCandlesH1Indexes) {
         await executeNonQuery(indexQuery);
         await executeNonQuery(indexQuery);
       }
       }
 
 
-      logger.info('SQLite indexes created successfully');
+      logger.info('SQLite candles_h1 indexes created successfully');
 
 
-      // Create symbols table
-      const createSymbolsTableQuery = `
-        CREATE TABLE IF NOT EXISTS symbols (
+      // Create live_prices table for SQLite
+      const createLivePricesTableQuery = `
+        CREATE TABLE IF NOT EXISTS live_prices (
           id INTEGER PRIMARY KEY AUTOINCREMENT,
           id INTEGER PRIMARY KEY AUTOINCREMENT,
-          symbol TEXT UNIQUE NOT NULL,
-          name TEXT NOT NULL,
-          category TEXT NOT NULL,
-          exchange TEXT NOT NULL,
-          is_active INTEGER DEFAULT 1,
-          created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
-          updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
+          symbol TEXT NOT NULL,
+          market TEXT NOT NULL,
+          price REAL NOT NULL,
+          updated_at DATETIME NOT NULL,
+          UNIQUE(symbol, market)
         );
         );
       `;
       `;
 
 
-      await executeNonQuery(createSymbolsTableQuery);
-      logger.info('SQLite symbols table created successfully');
-
-    } catch (error) {
-      logger.error('Error creating SQLite tables:', error);
-      throw error;
-    }
-  }
-
-  async insertSampleData() {
-    try {
-      const dbType = process.env.DB_TYPE || 'sqlite';
-
-      // Check if symbols already exist
-      let existingSymbols;
-      if (dbType === 'sqlite') {
-        existingSymbols = await executeQuery('SELECT COUNT(*) as count FROM symbols');
-      } else {
-        existingSymbols = await executeQuery('SELECT COUNT(*) as count FROM symbols');
-      }
-
-      // Handle different database result formats
-      let symbolCount = 0;
-      if (existingSymbols && existingSymbols.length > 0) {
-        symbolCount = existingSymbols[0].count || 0;
-      }
-
-      if (symbolCount > 0) {
-        logger.info('Sample symbols already exist, skipping insertion');
-        return;
-      }
+      await executeNonQuery(createLivePricesTableQuery);
+      logger.info('SQLite live_prices table created successfully');
 
 
-      // Insert sample symbols
-      const sampleSymbols = [
-        { symbol: 'EURUSD', name: 'Euro vs US Dollar', category: 'forex', exchange: 'FX' },
-        { symbol: 'GBPUSD', name: 'British Pound vs US Dollar', category: 'forex', exchange: 'FX' },
-        { symbol: 'USDJPY', name: 'US Dollar vs Japanese Yen', category: 'forex', exchange: 'FX' },
-        { symbol: 'USDCHF', name: 'US Dollar vs Swiss Franc', category: 'forex', exchange: 'FX' },
-        { symbol: 'USDCAD', name: 'US Dollar vs Canadian Dollar', category: 'forex', exchange: 'FX' },
-        { symbol: 'AUDUSD', name: 'Australian Dollar vs US Dollar', category: 'forex', exchange: 'FX' },
-        { symbol: 'NZDUSD', name: 'New Zealand Dollar vs US Dollar', category: 'forex', exchange: 'FX' },
-        { symbol: 'BTCUSD', name: 'Bitcoin vs US Dollar', category: 'crypto', exchange: 'Crypto' },
-        { symbol: 'ETHUSD', name: 'Ethereum vs US Dollar', category: 'crypto', exchange: 'Crypto' },
-        { symbol: 'AAPL', name: 'Apple Inc.', category: 'stocks', exchange: 'NASDAQ' },
-        { symbol: 'GOOGL', name: 'Alphabet Inc.', category: 'stocks', exchange: 'NASDAQ' },
-        { symbol: 'MSFT', name: 'Microsoft Corporation', category: 'stocks', exchange: 'NASDAQ' },
-        { symbol: 'TSLA', name: 'Tesla Inc.', category: 'stocks', exchange: 'NASDAQ' },
-        { symbol: 'AMZN', name: 'Amazon.com Inc.', category: 'stocks', exchange: 'NASDAQ' },
-        { symbol: 'META', name: 'Meta Platforms Inc.', category: 'stocks', exchange: 'NASDAQ' }
+      // Create indexes for live_prices
+      const createLivePricesIndexes = [
+        'CREATE INDEX IF NOT EXISTS idx_live_symbol ON live_prices(symbol);',
+        'CREATE INDEX IF NOT EXISTS idx_live_market ON live_prices(market);',
+        'CREATE INDEX IF NOT EXISTS idx_live_updated_at ON live_prices(updated_at);'
       ];
       ];
 
 
-      for (const symbolData of sampleSymbols) {
-        let insertQuery;
-        if (dbType === 'sqlite') {
-          insertQuery = `
-            INSERT OR IGNORE INTO symbols (symbol, name, category, exchange, is_active)
-            VALUES (?, ?, ?, ?, 1)
-          `;
-        } else if (dbType === 'postgres') {
-          insertQuery = `
-            INSERT INTO symbols (symbol, name, category, exchange, is_active)
-            VALUES (?, ?, ?, ?, ?)
-            ON CONFLICT (symbol) DO NOTHING
-          `;
-        } else {
-          insertQuery = `
-            INSERT IGNORE INTO symbols (symbol, name, category, exchange, is_active)
-            VALUES (?, ?, ?, ?, 1)
-          `;
-        }
-
-        await executeNonQuery(insertQuery, [
-          symbolData.symbol,
-          symbolData.name,
-          symbolData.category,
-          symbolData.exchange,
-          true
-        ]);
-      }
-
-      logger.info(`Inserted ${sampleSymbols.length} sample symbols`);
-
-      // Generate sample price data for the last 1000 H1 candles
-      await this.generateSamplePriceData(sampleSymbols.slice(0, 7)); // Generate for first 7 symbols
-
-    } catch (error) {
-      logger.error('Error inserting sample data:', error);
-      throw error;
-    }
-  }
-
-  async generateSamplePriceData(symbols) {
-    try {
-      logger.info('Generating sample price data...');
-
-      const now = new Date();
-      const dbType = process.env.DB_TYPE || 'sqlite';
-
-      for (const symbolData of symbols) {
-        logger.info(`Generating price data for ${symbolData.symbol}...`);
-
-        // Generate 1000 H1 candles (about 41 days of data)
-        const priceData = [];
-        let currentPrice = this.getInitialPrice(symbolData.symbol);
-
-        for (let i = 999; i >= 0; i--) {
-          const timestamp = new Date(now.getTime() - (i * 60 * 60 * 1000)); // H1 intervals
-          const open = currentPrice;
-
-          // Generate realistic price movement (±1%)
-          const change = (Math.random() - 0.5) * 0.02;
-          const close = open * (1 + change);
-
-          const high = Math.max(open, close) * (1 + Math.random() * 0.001);
-          const low = Math.min(open, close) * (1 - Math.random() * 0.001);
-          const volume = Math.floor(Math.random() * 1000000) + 100000;
-
-          priceData.push({
-            symbol: symbolData.symbol,
-            timeframe: 'H1',
-            timestamp,
-            open: open.toFixed(8),
-            high: high.toFixed(8),
-            low: low.toFixed(8),
-            close: close.toFixed(8),
-            volume
-          });
-
-          currentPrice = close;
-        }
-
-        // Insert price data in batches
-        const batchSize = 100;
-        for (let i = 0; i < priceData.length; i += batchSize) {
-          const batch = priceData.slice(i, i + batchSize);
-
-          for (const price of batch) {
-            let insertQuery;
-            if (dbType === 'sqlite') {
-              insertQuery = `
-                INSERT OR IGNORE INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
-                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
-              `;
-            } else if (dbType === 'postgres') {
-              insertQuery = `
-                INSERT INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
-                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
-                ON CONFLICT DO NOTHING
-              `;
-            } else {
-              insertQuery = `
-                INSERT IGNORE INTO price_data (symbol, timeframe, timestamp, open, high, low, close, volume)
-                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
-              `;
-            }
-
-            await executeNonQuery(insertQuery, [
-              price.symbol,
-              price.timeframe,
-              price.timestamp,
-              price.open,
-              price.high,
-              price.low,
-              price.close,
-              price.volume
-            ]);
-          }
-        }
-
-        logger.info(`Generated ${priceData.length} price records for ${symbolData.symbol}`);
+      for (const indexQuery of createLivePricesIndexes) {
+        await executeNonQuery(indexQuery);
       }
       }
 
 
-      logger.info('Sample price data generation completed');
+      logger.info('SQLite live_prices indexes created successfully');
 
 
     } catch (error) {
     } catch (error) {
-      logger.error('Error generating sample price data:', error);
+      logger.error('Error creating SQLite tables:', error);
       throw error;
       throw error;
     }
     }
   }
   }
 
 
-  getInitialPrice(symbol) {
-    const initialPrices = {
-      'EURUSD': 1.0850,
-      'GBPUSD': 1.2650,
-      'USDJPY': 150.50,
-      'USDCHF': 0.9150,
-      'USDCAD': 1.3450,
-      'AUDUSD': 0.6550,
-      'NZDUSD': 0.6050
-    };
-
-    return initialPrices[symbol] || 1.0000;
-  }
+  // Removed all sample data generation methods as per user requirements
 
 
   async reset() {
   async reset() {
     try {
     try {
       logger.info('Resetting database...');
       logger.info('Resetting database...');
 
 
-      const dbType = process.env.DB_TYPE || 'sqlite';
+      const dbType = process.env.DB_TYPE || 'postgres';
 
 
-      // Drop tables
+      // Drop tables in correct order (considering foreign key constraints)
       if (dbType === 'sqlite') {
       if (dbType === 'sqlite') {
-        await executeNonQuery('DROP TABLE IF EXISTS price_data');
-        await executeNonQuery('DROP TABLE IF EXISTS symbols');
+        await executeNonQuery('DROP TABLE IF EXISTS candles_h1');
+        await executeNonQuery('DROP TABLE IF EXISTS live_prices');
       } else {
       } else {
-        await executeNonQuery('DROP TABLE IF EXISTS price_data');
-        await executeNonQuery('DROP TABLE IF EXISTS symbols');
+        await executeNonQuery('DROP TABLE IF EXISTS candles_h1');
+        await executeNonQuery('DROP TABLE IF EXISTS live_prices');
       }
       }
 
 
       this.initialized = false;
       this.initialized = false;
@@ -437,41 +269,41 @@ class DatabaseInitializer {
 
 
   async getStats() {
   async getStats() {
     try {
     try {
-      const dbType = process.env.DB_TYPE || 'sqlite';
+      const dbType = process.env.DB_TYPE || 'postgres';
 
 
-      let symbolCount, priceCount;
+      let candlesH1Count, livePricesCount;
 
 
       if (dbType === 'sqlite') {
       if (dbType === 'sqlite') {
-        symbolCount = await executeQuery('SELECT COUNT(*) as count FROM symbols');
-        priceCount = await executeQuery('SELECT COUNT(*) as count FROM price_data');
+        candlesH1Count = await executeQuery('SELECT COUNT(*) as count FROM candles_h1');
+        livePricesCount = await executeQuery('SELECT COUNT(*) as count FROM live_prices');
       } else {
       } else {
-        symbolCount = await executeQuery('SELECT COUNT(*) as count FROM symbols');
-        priceCount = await executeQuery('SELECT COUNT(*) as count FROM price_data');
+        candlesH1Count = await executeQuery('SELECT COUNT(*) as count FROM candles_h1');
+        livePricesCount = await executeQuery('SELECT COUNT(*) as count FROM live_prices');
       }
       }
 
 
       // Handle different database result formats
       // Handle different database result formats
-      let symbols = 0;
-      let priceRecords = 0;
+      let candlesH1 = 0;
+      let livePrices = 0;
 
 
-      if (symbolCount && symbolCount.length > 0) {
-        symbols = symbolCount[0].count || 0;
+      if (candlesH1Count && candlesH1Count.length > 0) {
+        candlesH1 = candlesH1Count[0].count || 0;
       }
       }
 
 
-      if (priceCount && priceCount.length > 0) {
-        priceRecords = priceCount[0].count || 0;
+      if (livePricesCount && livePricesCount.length > 0) {
+        livePrices = livePricesCount[0].count || 0;
       }
       }
 
 
       return {
       return {
-        symbols,
-        price_records: priceRecords,
+        candles_h1: candlesH1,
+        live_prices: livePrices,
         initialized: this.initialized
         initialized: this.initialized
       };
       };
 
 
     } catch (error) {
     } catch (error) {
       logger.error('Error getting database stats:', error);
       logger.error('Error getting database stats:', error);
       return {
       return {
-        symbols: 0,
-        price_records: 0,
+        candles_h1: 0,
+        live_prices: 0,
         initialized: false
         initialized: false
       };
       };
     }
     }