فهرست منبع

feat: Add WebSocket real-time support with Socket.io

- Integrate Socket.io server with HTTP server in src/server.js
- Add WebSocket event emission in live price controller for real-time price updates
- Add WebSocket event emission in candle controller for real-time candle updates
- Implement client subscription/unsubscription system for symbols
- Add socket.io and socket.io-client dependencies
- Update server port configuration to 3001
- Maintain backward compatibility with existing REST APIs
- Support multiple concurrent client connections for InsightBull projects
uzairrizwan1 9 ماه پیش
والد
کامیت
9244a27a2c
6فایلهای تغییر یافته به همراه513 افزوده شده و 2 حذف شده
  1. 1 1
      README.md
  2. 341 0
      package-lock.json
  3. 2 0
      package.json
  4. 64 0
      src/controllers/candleController.js
  5. 50 0
      src/controllers/livePriceController.js
  6. 55 1
      src/server.js

+ 1 - 1
README.md

@@ -658,7 +658,7 @@ DB_USER=your_username
 DB_PASSWORD=your_password
 
 # Server Configuration
-PORT=3000
+PORT=3001
 NODE_ENV=development
 
 # JWT Configuration (if needed for authentication)

+ 341 - 0
package-lock.json

@@ -17,6 +17,8 @@
         "morgan": "^1.10.1",
         "pg": "^8.16.3",
         "sequelize": "^6.37.7",
+        "socket.io": "^4.8.1",
+        "socket.io-client": "^4.8.1",
         "winston": "^3.18.3"
       },
       "devDependencies": {
@@ -1187,6 +1189,12 @@
         "text-hex": "1.0.x"
       }
     },
+    "node_modules/@socket.io/component-emitter": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+      "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+      "license": "MIT"
+    },
     "node_modules/@standard-schema/spec": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
@@ -1249,6 +1257,15 @@
         "@babel/types": "^7.28.2"
       }
     },
+    "node_modules/@types/cors": {
+      "version": "2.8.19",
+      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
+      "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/debug": {
       "version": "4.1.12",
       "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -1887,6 +1904,15 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/base64id": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+      "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+      "license": "MIT",
+      "engines": {
+        "node": "^4.5.0 || >= 5.9"
+      }
+    },
     "node_modules/baseline-browser-mapping": {
       "version": "2.8.17",
       "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.17.tgz",
@@ -2661,6 +2687,125 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/engine.io": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
+      "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/cors": "^2.8.12",
+        "@types/node": ">=10.0.0",
+        "accepts": "~1.3.4",
+        "base64id": "2.0.0",
+        "cookie": "~0.7.2",
+        "cors": "~2.8.5",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1"
+      },
+      "engines": {
+        "node": ">=10.2.0"
+      }
+    },
+    "node_modules/engine.io-client": {
+      "version": "6.6.3",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
+      "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1",
+        "xmlhttprequest-ssl": "~2.1.1"
+      }
+    },
+    "node_modules/engine.io-client/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/engine.io-parser": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+      "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/engine.io/node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/engine.io/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/engine.io/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/engine.io/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/engine.io/node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/error-ex": {
       "version": "1.3.4",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
@@ -5906,6 +6051,173 @@
         "node": ">=8"
       }
     },
+    "node_modules/socket.io": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
+      "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.4",
+        "base64id": "~2.0.0",
+        "cors": "~2.8.5",
+        "debug": "~4.3.2",
+        "engine.io": "~6.6.0",
+        "socket.io-adapter": "~2.5.2",
+        "socket.io-parser": "~4.2.4"
+      },
+      "engines": {
+        "node": ">=10.2.0"
+      }
+    },
+    "node_modules/socket.io-adapter": {
+      "version": "2.5.5",
+      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
+      "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "~4.3.4",
+        "ws": "~8.17.1"
+      }
+    },
+    "node_modules/socket.io-adapter/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/socket.io-client": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
+      "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.2",
+        "engine.io-client": "~6.6.1",
+        "socket.io-parser": "~4.2.4"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-client/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/socket.io-parser": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+      "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-parser/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/socket.io/node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/socket.io/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/socket.io/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/socket.io/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/socket.io/node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -6745,6 +7057,35 @@
         "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
       }
     },
+    "node_modules/ws": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xmlhttprequest-ssl": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
+      "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/xtend": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

+ 2 - 0
package.json

@@ -30,6 +30,8 @@
     "morgan": "^1.10.1",
     "pg": "^8.16.3",
     "sequelize": "^6.37.7",
+    "socket.io": "^4.8.1",
+    "socket.io-client": "^4.8.1",
     "winston": "^3.18.3"
   },
   "devDependencies": {

+ 64 - 0
src/controllers/candleController.js

@@ -120,6 +120,29 @@ class CandleController {
 
       const candle = await Candle1h.create(candleData);
 
+      // Emit WebSocket event for real-time updates
+      const io = req.app.get('io');
+      if (io) {
+        const eventData = {
+          symbol: symbol.symbol,
+          symbolId: symbol.id,
+          openTime: candle.openTime,
+          open: candle.open,
+          high: candle.high,
+          low: candle.low,
+          close: candle.close,
+          volume: candle.volume,
+          exchange: symbol.exchange,
+          instrumentType: symbol.instrumentType
+        };
+
+        // Emit to all clients subscribed to this symbol
+        io.to(`symbol:${symbol.symbol}`).emit('candleUpdate', eventData);
+
+        // Also emit to general candle updates
+        io.emit('candleUpdate', eventData);
+      }
+
       res.status(201).json({
         success: true,
         data: candle,
@@ -159,6 +182,47 @@ class CandleController {
 
       const createdCandles = await Candle1h.bulkCreate(candles);
 
+      // Emit WebSocket events for real-time updates
+      const io = req.app.get('io');
+      if (io) {
+        // Group candles by symbol for efficient emission
+        const symbolGroups = {};
+        for (const candle of createdCandles) {
+          if (!symbolGroups[candle.symbolId]) {
+            symbolGroups[candle.symbolId] = [];
+          }
+          symbolGroups[candle.symbolId].push(candle);
+        }
+
+        // Emit events for each symbol
+        for (const [symbolId, symbolCandles] of Object.entries(symbolGroups)) {
+          const symbol = await Symbol.findByPk(symbolId);
+          if (symbol) {
+            // Emit the latest candle for this symbol (most recent)
+            const latestCandle = symbolCandles.sort((a, b) => new Date(b.openTime) - new Date(a.openTime))[0];
+
+            const eventData = {
+              symbol: symbol.symbol,
+              symbolId: symbol.id,
+              openTime: latestCandle.openTime,
+              open: latestCandle.open,
+              high: latestCandle.high,
+              low: latestCandle.low,
+              close: latestCandle.close,
+              volume: latestCandle.volume,
+              exchange: symbol.exchange,
+              instrumentType: symbol.instrumentType
+            };
+
+            // Emit to all clients subscribed to this symbol
+            io.to(`symbol:${symbol.symbol}`).emit('candleUpdate', eventData);
+
+            // Also emit to general candle updates
+            io.emit('candleUpdate', eventData);
+          }
+        }
+      }
+
       res.status(201).json({
         success: true,
         data: createdCandles,

+ 50 - 0
src/controllers/livePriceController.js

@@ -90,6 +90,29 @@ class LivePriceController {
         lastUpdated: new Date()
       });
 
+      // Emit WebSocket event for real-time updates
+      const io = req.app.get('io');
+      if (io) {
+        const eventData = {
+          symbol: symbol.symbol,
+          symbolId: symbol.id,
+          price,
+          bid,
+          ask,
+          bidSize,
+          askSize,
+          lastUpdated: livePrice.lastUpdated,
+          exchange: symbol.exchange,
+          instrumentType: symbol.instrumentType
+        };
+
+        // Emit to all clients subscribed to this symbol
+        io.to(`symbol:${symbol.symbol}`).emit('livePriceUpdate', eventData);
+
+        // Also emit to general live price updates
+        io.emit('livePriceUpdate', eventData);
+      }
+
       res.status(created ? 201 : 200).json({
         success: true,
         data: livePrice,
@@ -142,9 +165,36 @@ class LivePriceController {
       }));
 
       const updatedPrices = [];
+      const io = req.app.get('io');
+
       for (const data of upsertData) {
         const [livePrice] = await LivePrice.upsert(data);
         updatedPrices.push(livePrice);
+
+        // Emit WebSocket event for each updated price
+        if (io) {
+          const symbol = await Symbol.findByPk(data.symbolId);
+          if (symbol) {
+            const eventData = {
+              symbol: symbol.symbol,
+              symbolId: symbol.id,
+              price: data.price,
+              bid: data.bid,
+              ask: data.ask,
+              bidSize: data.bidSize,
+              askSize: data.askSize,
+              lastUpdated: data.lastUpdated,
+              exchange: symbol.exchange,
+              instrumentType: symbol.instrumentType
+            };
+
+            // Emit to all clients subscribed to this symbol
+            io.to(`symbol:${symbol.symbol}`).emit('livePriceUpdate', eventData);
+
+            // Also emit to general live price updates
+            io.emit('livePriceUpdate', eventData);
+          }
+        }
       }
 
       res.json({

+ 55 - 1
src/server.js

@@ -1,6 +1,8 @@
 const app = require('./app');
 const { testConnection } = require('./config/database');
 const logger = require('./utils/logger');
+const { createServer } = require('http');
+const { Server } = require('socket.io');
 
 const PORT = process.env.PORT || 3000;
 
@@ -10,11 +12,63 @@ const startServer = async () => {
     // Test database connection
     await testConnection();
 
+    // Create HTTP server
+    const server = createServer(app);
+
+    // Initialize Socket.io
+    const io = new Server(server, {
+      cors: {
+        origin: process.env.CORS_ORIGIN || "*",
+        methods: ["GET", "POST"],
+        credentials: true
+      }
+    });
+
+    // Socket.io connection handling
+    io.on('connection', (socket) => {
+      logger.info(`Client connected: ${socket.id}`);
+
+      // Handle subscription to symbols
+      socket.on('subscribe', (symbols) => {
+        if (Array.isArray(symbols)) {
+          symbols.forEach(symbol => {
+            socket.join(`symbol:${symbol}`);
+            logger.info(`Client ${socket.id} subscribed to ${symbol}`);
+          });
+        } else {
+          socket.join(`symbol:${symbols}`);
+          logger.info(`Client ${socket.id} subscribed to ${symbols}`);
+        }
+      });
+
+      // Handle unsubscription
+      socket.on('unsubscribe', (symbols) => {
+        if (Array.isArray(symbols)) {
+          symbols.forEach(symbol => {
+            socket.leave(`symbol:${symbol}`);
+            logger.info(`Client ${socket.id} unsubscribed from ${symbol}`);
+          });
+        } else {
+          socket.leave(`symbol:${symbols}`);
+          logger.info(`Client ${socket.id} unsubscribed from ${symbols}`);
+        }
+      });
+
+      // Handle disconnect
+      socket.on('disconnect', () => {
+        logger.info(`Client disconnected: ${socket.id}`);
+      });
+    });
+
+    // Make io accessible to routes/controllers
+    app.set('io', io);
+
     // Start the server
-    const server = app.listen(PORT, () => {
+    server.listen(PORT, () => {
       logger.info(`Market Data Service is running on port ${PORT}`);
       logger.info(`Environment: ${process.env.NODE_ENV || 'development'}`);
       logger.info(`Health check available at: http://localhost:${PORT}/health`);
+      logger.info(`WebSocket server ready for connections`);
     });
 
     // Graceful shutdown