diff --git a/.gitignore b/.gitignore index 9a54c93f..e55cba9e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ coverage # Turbo .turbo +# logs +logs + # Snapshot snapshot.json diff --git a/apps/server/src/controllers/order/index.ts b/apps/server/src/controllers/order/index.ts index 24d1ab6d..4ba96921 100644 --- a/apps/server/src/controllers/order/index.ts +++ b/apps/server/src/controllers/order/index.ts @@ -15,10 +15,11 @@ type TPlaceOrder = { }; export const placeHandler = AsyncWrapper( async (req: Request<{}, {}, TPlaceOrder>, res) => { - const { event_id, l1_expected_price, l1_order_quantity, offer_type } = - req.body; - let orderId = generateOrderId(); + const { event_id, l1_expected_price, l1_order_quantity, offer_type } = req.body; + const orderId = generateOrderId(); + console.log(orderId); + const order = { [orderId]: { event_id: event_id, @@ -28,7 +29,7 @@ export const placeHandler = AsyncWrapper( }, }; await addToOrderQueue(order); - redisClient.publishMessage("123", order); + redisClient.publishMessage(orderId, order); let response = new SuccessResponse("Order placed successfully", 201); return res.status(201).json(response); } diff --git a/packages/order-queue/src/classes/RedisManager.ts b/packages/order-queue/src/classes/RedisManager.ts index d8aa1e21..1851677a 100644 --- a/packages/order-queue/src/classes/RedisManager.ts +++ b/packages/order-queue/src/classes/RedisManager.ts @@ -7,6 +7,7 @@ export class RedisManager { constructor() { this.client = createClient(); + console.log("Redis connected log inside Redis Manager") this.client.connect(); } diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index 0d097b3e..e2e98f19 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -9,7 +9,7 @@ export const userZodSchema = z.object({ export const intiateOrderZodSchema = z.object({ event_id: z.number(), l1_expected_price: z.number().min(0.1, { - message: "Order quantity must be a positive integer", + message: "Order price must be a positive integer", }), l1_order_quantity: z.number().min(0.1, { message: "Order quantity must be a positive integer", diff --git a/services/engine/logs/server.log b/services/engine/logs/server.log index fb8cb976..25775c5b 100644 --- a/services/engine/logs/server.log +++ b/services/engine/logs/server.log @@ -21,3 +21,21 @@ [ 2024-10-20 22:50:18 ] - info - WORKER | Starting order worker [ 2024-10-20 23:47:55 ] - info - WORKER | Starting order worker [ 2024-10-20 23:52:25 ] - info - WORKER | Starting order worker +[ 2024-10-22 11:00:20 ] - info - WORKER | Starting order worker +[ 2024-10-22 11:00:53 ] - info - WORKER | Starting order worker +[ 2024-10-22 11:02:50 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:10:53 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:11:49 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:13:59 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:14:16 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:30:29 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:38:17 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:39:42 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:40:03 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:40:17 ] - info - WORKER | Starting order worker +[ 2024-10-22 13:40:30 ] - info - WORKER | Starting order worker +[ 2024-10-22 16:06:21 ] - info - WORKER | Starting order worker +[ 2024-10-22 23:26:09 ] - info - WORKER | Starting order worker +[ 2024-10-22 23:28:24 ] - info - WORKER | Starting order worker +[ 2024-10-22 23:28:41 ] - info - WORKER | Starting order worker +[ 2024-10-22 23:39:45 ] - info - WORKER | Starting order worker diff --git a/services/engine/src/index.ts b/services/engine/src/index.ts index 66f1c4f2..48855bd3 100644 --- a/services/engine/src/index.ts +++ b/services/engine/src/index.ts @@ -8,18 +8,19 @@ 6. Test Cases */ -import { createClient } from "@repo/order-queue"; +import { RedisManager } from "@repo/order-queue"; import { Engine } from "./trade/Engine"; +import {Orderbook} from "./trade/Orderbook" async function main() { const engine = new Engine(); - const redisClient = createClient(); - await redisClient.connect(); + const redis = new RedisManager(); + const redisClient = redis.getClient(); console.log("connected to redis"); while (true) { - const response = await redisClient.rPop("messages" as string) + const response = await redisClient.lPop("ORDER_QUEUE") if (!response) { } else { @@ -29,4 +30,7 @@ async function main() { } -main(); \ No newline at end of file +main(); + + +export {Engine, Orderbook}; \ No newline at end of file diff --git a/services/engine/src/trade/Engine.ts b/services/engine/src/trade/Engine.ts index 0d94c7fa..a5267344 100644 --- a/services/engine/src/trade/Engine.ts +++ b/services/engine/src/trade/Engine.ts @@ -1,33 +1,17 @@ import fs from "fs"; import { v4 as uuidv4 } from "uuid"; -import { - CANCEL_ORDER, - CREATE_ORDER, - GET_DEPTH, - GET_OPEN_ORDERS, - MessageFromApi, - ON_RAMP, - ORDER_UPDATE, - TRADE_ADDED, -} from "@opinix/types"; +import { CANCEL_ORDER, CREATE_ORDER, GET_DEPTH, GET_OPEN_ORDERS, MessageFromApi, ON_RAMP, ORDER_UPDATE, TRADE_ADDED } from "@opinix/types"; import { Fill, Order, Orderbook } from "./Orderbook"; -import { RedisManager } from "@repo/order-queue"; - -export const EXAMPLE_EVENT = - "bitcoin-to-be-priced-at-6811470-usdt-or-more-at-0735-pm"; +import { RedisManager } from "@repo/order-queue" +export const EXAMPLE_EVENT = "bitcoin-to-be-priced-at-6811470-usdt-or-more-at-0735-pm" export const CURRENCY = "INR"; - interface UserBalance { - [key: string]: { - available: number; - locked: number; - }; + available: number; + locked: number; } - export class Engine { private balances: Map = new Map(); private orderbooks: Orderbook[] = []; - constructor() { let snapshot = null; try { @@ -37,18 +21,14 @@ export class Engine { } catch (error) { console.log("No snapshot found"); } - if (snapshot) { const parsedSnapShot = JSON.parse(snapshot.toString()); - this.orderbooks = parsedSnapShot.orderbook.map( - (o: any) => - new Orderbook(o.bids, o.asks, o.lastTradeId, o.currentPrice, o.event) - ); + this.orderbooks = parsedSnapShot.orderbook.map((o: any) => new Orderbook(o.bids, o.asks, o.lastTradeId, o.currentPrice, o.event));; this.balances = new Map(parsedSnapShot.balance); } else { - const lastTradeId = 0; // for now assuming this random id as lastTradeId - this.orderbooks = [new Orderbook([], [], lastTradeId, 0, EXAMPLE_EVENT)]; - // this.setBaseBalances(); + const lastTradeId = 1; // for now assuming this random id as lastTradeId + this.orderbooks = [new Orderbook([], [], lastTradeId, 0, EXAMPLE_EVENT)] + this.setBaseBalances(); } setInterval(() => { this.saveSnapshot(); @@ -56,85 +36,55 @@ export class Engine { } saveSnapshot() { const snapshotSnapshot = { - orderbooks: this.orderbooks.map((o) => o.getSnapshot()), - balances: Array.from(this.balances.entries()), - }; + orderbooks: this.orderbooks.map(o => o.getSnapshot()), + balances: Array.from(this.balances.entries()) + } fs.writeFileSync("./snapshot.json", JSON.stringify(snapshotSnapshot)); } - - processOrders({ - message, - clientId, - }: { - message: MessageFromApi; - clientId: string; - }) { + processOrders({ message, clientId }: { message: MessageFromApi, clientId: string }) { + console.log("message", message, "clientId", clientId) switch (message.type) { case CREATE_ORDER: try { - const { executedQty, fills, orderId } = this.createOrders( - "", - 1.5, - 1, - "yes", - "jsdbjbvjbjvbdfj" - ); + const { executedQty, fills, orderId } = this.createOrders(message.data.market, message.data.price, message.data.quantity, message.data.side, message.data.userId) // publish it to the server via redis RedisManager.getInstance().sendToApi(clientId, { type: "ORDER_PLACED", payload: { orderId, executedQty, - fills, - }, + fills + } }); + console.log("Pushed ORDER_PLACED into REDIS") } catch (error) { - - - console.log("No snapshot found"); - } - - if (snapshot) { - const parsedSnapShot = JSON.parse(snapshot.toString()); - this.orderbooks = parsedSnapShot.orderbook.map((o: any) => new Orderbook(o.bids, o.asks, o.lastTradeId, o.currentPrice, o.event));; - this.balances = new Map(parsedSnapShot.balance); - } else { - const lastTradeId = 0; // for now assuming this random id as lastTradeId - this.orderbooks = [new Orderbook([], [], lastTradeId, 0, EXAMPLE_EVENT)] - // this.setBaseBalances(); - } - setInterval(() => { - this.saveSnapshot(); - }, 1000 * 3); - } - saveSnapshot() { - const snapshotSnapshot = { - orderbooks: this.orderbooks.map(o => o.getSnapshot()), - balances: Array.from(this.balances.entries()) + console.log(error); + // publish it to the server via redis + RedisManager.getInstance().sendToApi(clientId, { + type: "ORDER_CANCELLED", + payload: { + orderId: "", + executedQty: 0, + remainingQty: 0 + } + }); } break; - case CANCEL_ORDER: try { const orderId = message.data.orderId; const cancelMarket = message.data.market; - const cancelOrderbook = this.orderbooks.find( - (o) => o.market === cancelMarket - ); + const cancelOrderbook = this.orderbooks.find(o => o.market === cancelMarket); if (!cancelOrderbook) { throw new Error("No orderbook found"); } - - const order = - cancelOrderbook.asks.find((o) => o.orderId === orderId) || - cancelOrderbook.bids.find((o) => o.orderId === orderId); + const order = cancelOrderbook.asks.find(o => o.orderId === orderId) || cancelOrderbook.bids.find(o => o.orderId === orderId); if (!order) { console.log("No order found"); throw new Error("No order found"); } - if (order.side === "yes") { - const price = cancelOrderbook.cancelBid(order); + const price = cancelOrderbook.cancelBid(order) const leftQuantity = (order.quantity - order.filled) * order.price; //@ts-ignore this.balances.get(order.userId)[CURRENCY].available += leftQuantity; @@ -144,11 +94,10 @@ export class Engine { this.sendUpdatedDepthAt(price.toString(), cancelMarket); } } else { - const price = cancelOrderbook.cancelAsk(order); + const price = cancelOrderbook.cancelAsk(order) const leftQuantity = order.quantity - order.filled; //@ts-ignore - this.balances.get(order.userId)[quoteAsset].available += - leftQuantity; + this.balances.get(order.userId)[quoteAsset].available += leftQuantity; //@ts-ignore this.balances.get(order.userId)[quoteAsset].locked -= leftQuantity; if (price) { @@ -161,50 +110,44 @@ export class Engine { payload: { orderId, executedQty: 0, - remainingQty: 0, - }, + remainingQty: 0 + } }); } catch (error) { - console.log("Error hwile cancelling order"); + console.log("Error hwile cancelling order",); console.log(error); } break; - case GET_OPEN_ORDERS: try { - const openOrderbook = this.orderbooks.find( - (o) => o.market === message.data.market - ); + const openOrderbook = this.orderbooks.find(o => o.market === message.data.market); if (!openOrderbook) { throw new Error("No orderbook found"); } const openOrders = openOrderbook.getOpenOrders(message.data.userId); - RedisManager.getInstance().sendToApi(clientId, { type: "OPEN_ORDERS", - payload: openOrders, + payload: openOrders }); } catch (error) { console.log(error); } break; - case ON_RAMP: const userId = message.data.userId; const amount = Number(message.data.amount); this.onRamp(userId, amount); break; - case GET_DEPTH: try { const market = message.data.market; - const orderbook = this.orderbooks.find((o) => o.market === market); + const orderbook = this.orderbooks.find(o => o.market === market); if (!orderbook) { throw new Error("No orderbook found"); } RedisManager.getInstance().sendToApi(clientId, { type: "DEPTH", - payload: orderbook.getMarketDepth(), + payload: orderbook.getMarketDepth() }); } catch (e) { console.log(e); @@ -212,144 +155,98 @@ export class Engine { type: "DEPTH", payload: { bids: [], - asks: [], - }, + asks: [] + } }); } break; } } - addOrderbook(orderbook: Orderbook) { this.orderbooks.push(orderbook); } - - createOrders( - market: string, - price: number, - quantity: number, - side: "yes" | "no", - userId: string - ): { - executedQty: number; - fills: Fill[]; - orderId: string; + createOrders(market: string, price: number, quantity: number, side: "yes" | "no", userId: string): { + executedQty: number, + fills: Fill[], + orderId: string } { - const orderbook = this.orderbooks.find((o) => o.market === market); + const orderbook = this.orderbooks.find(o => o.market === market) if (!orderbook) { throw new Error("No orderbook found"); } // Check and Lock funds this.checkAndLockFunds(side, userId, price, quantity); - const order: Order = { price: Number(price), quantity: Number(quantity), orderId: uuidv4(), filled: 0, side, - userId, - }; - + userId + } const { fills, executedQty } = orderbook.addOrder(order); this.updateBalance(userId, side, fills); this.createDbTrades(fills, market, userId); this.updateDbOrders(order, executedQty, fills, market); - this.publisWsDepthUpdates(fills, price, side, market); + this.publisWsDepthUpdates(fills, price, side, market) this.publishWsTrades(fills, userId, market); - return { executedQty, fills, orderId: order.orderId }; } - - checkAndLockFunds( - side: "yes" | "no", - userId: string, - price: number, - quantity: number - ) { + checkAndLockFunds(side: "yes" | "no", userId: string, price: number, quantity: number) { if (side === "yes") { - if ( - (this.balances.get(userId)?.[CURRENCY]?.available || 0) < - Number(quantity) * Number(price) - ) { + console.log(userId); + console.log(price) + console.log(this.balances.get(userId)) + if ((this.balances.get(userId)?.available || 0) < quantity * price) { throw new Error("Insufficient funds"); } + //@ts-ignore + this.balances.get(userId).available = this.balances.get(userId)?.available - quantity * price; //@ts-ignore - this.balances.get(userId)[CURRENCY].available = - this.balances.get(userId)?.[CURRENCY].available - - Number(quantity) * Number(price); - - //@ts-ignore - this.balances.get(userId)[CURRENCY].locked = - this.balances.get(userId)?.[CURRENCY].locked + - Number(quantity) * Number(price); + this.balances.get(userId).locked = this.balances.get(userId)?.locked + quantity * price; } else { - if ( - (this.balances.get(userId)?.[CURRENCY]?.available || 0) < - Number(quantity) - ) { + console.log("askssss balanace", this.balances.get(userId)); + + if ((this.balances.get(userId)?.locked || 0) < Number(quantity)) { throw new Error("Insufficient funds"); } //@ts-ignore - this.balances.get(userId)[CURRENCY].available = - this.balances.get(userId)?.[CURRENCY].available - Number(quantity); - + this.balances.get(userId).available = this.balances.get(userId)?.available - (Number(quantity)); //@ts-ignore - this.balances.get(userId)[CURRENCY].locked = - this.balances.get(userId)?.[CURRENCY].locked + Number(quantity); + this.balances.get(userId).locked = this.balances.get(userId)?.locked + Number(quantity); } } updateBalance(userId: string, side: "yes" | "no", fills: Fill[]) { if (side === "yes") { - fills.forEach((fill) => { + fills.forEach(fill => { // Update quote asset balance //@ts-ignore - this.balances.get(fill.otherUserId)[CURRENCY].available = - this.balances.get(fill.otherUserId)?.[CURRENCY].available + - fill.qty * fill.price; - + this.balances.get(fill.otherUserId)[CURRENCY].available = this.balances.get(fill.otherUserId)?.[CURRENCY].available + (fill.qty * fill.price); //@ts-ignore - this.balances.get(userId)[CURRENCY].locked = - this.balances.get(userId)?.[CURRENCY].locked - fill.qty * fill.price; - + this.balances.get(userId)[CURRENCY].locked = this.balances.get(userId)?.[CURRENCY].locked - (fill.qty * fill.price); // Update base asset balance - //@ts-ignore - this.balances.get(fill.otherUserId)[CURRENCY].locked = - this.balances.get(fill.otherUserId)?.[CURRENCY].locked - fill.qty; - + this.balances.get(fill.otherUserId)[CURRENCY].locked = this.balances.get(fill.otherUserId)?.[CURRENCY].locked - fill.qty; //@ts-ignore - this.balances.get(userId)[CURRENCY].available = - this.balances.get(userId)?.[CURRENCY].available + fill.qty; + this.balances.get(userId)[CURRENCY].available = this.balances.get(userId)?.[CURRENCY].available + fill.qty; }); } else { - fills.forEach((fill) => { + fills.forEach(fill => { // Update quote asset balance //@ts-ignore - this.balances.get(fill.otherUserId)[CURRENCY].locked = - this.balances.get(fill.otherUserId)?.[CURRENCY].locked - - fill.qty * fill.price; - + this.balances.get(fill.otherUserId)[CURRENCY].locked = this.balances.get(fill.otherUserId)?.[CURRENCY].locked - (fill.qty * fill.price); //@ts-ignore - this.balances.get(userId)[CURRENCY].available = - this.balances.get(userId)?.[CURRENCY].available + - fill.qty * fill.price; - + this.balances.get(userId)[CURRENCY].available = this.balances.get(userId)?.[CURRENCY].available + (fill.qty * fill.price); // Update base asset balance - //@ts-ignore - this.balances.get(fill.otherUserId)[CURRENCY].available = - this.balances.get(fill.otherUserId)?.[CURRENCY].available + fill.qty; - + this.balances.get(fill.otherUserId)[CURRENCY].available = this.balances.get(fill.otherUserId)?.[CURRENCY].available + fill.qty; //@ts-ignore - this.balances.get(userId)[CURRENCY].locked = - this.balances.get(userId)?.[CURRENCY].locked - fill.qty; + this.balances.get(userId)[CURRENCY].locked = this.balances.get(userId)?.[CURRENCY].locked - (fill.qty); }); } } - createDbTrades(fills: Fill[], market: string, userId: string) { - fills.forEach((fill) => { + fills.forEach(fill => { RedisManager.getInstance().pushMessage({ type: TRADE_ADDED, data: { @@ -358,18 +255,12 @@ export class Engine { isBuyerMaker: fill.otherUserId === userId, // TODO: Is this right? price: fill.price, quantity: fill.qty, - timestamp: Date.now(), - }, + timestamp: Date.now() + } }); }); } - - updateDbOrders( - order: Order, - executedQty: number, - fills: Fill[], - market: string - ) { + updateDbOrders(order: Order, executedQty: number, fills: Fill[], market: string) { RedisManager.getInstance().pushMessage({ type: ORDER_UPDATE, data: { @@ -379,61 +270,53 @@ export class Engine { price: order.price.toString(), quantity: order.quantity.toString(), side: order.side, - }, + } }); - - fills.forEach((fill) => { + fills.forEach(fill => { RedisManager.getInstance().pushMessage({ type: ORDER_UPDATE, data: { orderId: fill.marketOrderId, - executedQty: fill.qty, - }, + executedQty: fill.qty + } }); }); } - - publisWsDepthUpdates( - fills: Fill[], - price: number, - side: "yes" | "no", - market: string - ) { - const orderbook = this.orderbooks.find((o) => o.market === market); + publisWsDepthUpdates(fills: Fill[], price: number, side: "yes" | "no", market: string) { + const orderbook = this.orderbooks.find(o => o.market === market); if (!orderbook) { return; } const depth = orderbook.getMarketDepth(); if (side === "yes") { - const updatedAsks = depth?.asks.filter((x) => fills.map((f) => f.price)); - const updatedBid = depth?.bids.find((x) => x[0] === price.toString()); - console.log("publish ws depth updates"); + const updatedAsks = depth?.asks.filter(x => fills.map(f => f.price)); + const updatedBid = depth?.bids.find(x => x[0] === price.toString()); + console.log("publish ws depth updates") RedisManager.getInstance().publishMessage(`depth@${market}`, { stream: `depth@${market}`, data: { a: updatedAsks, b: updatedBid ? [updatedBid] : [], - e: "depth", - }, + e: "depth" + } }); } if (side === "no") { - const updatedBids = depth?.bids.filter((x) => fills.map((f) => f.price)); - const updatedAsk = depth?.asks.find((x) => x[0] === price.toString()); - console.log("publish ws depth updates"); + const updatedBids = depth?.bids.filter(x => fills.map(f => f.price)); + const updatedAsk = depth?.asks.find(x => x[0] === price.toString()); + console.log("publish ws depth updates") RedisManager.getInstance().publishMessage(`depth@${market}`, { stream: `depth@${market}`, data: { a: updatedAsk ? [updatedAsk] : [], b: updatedBids, - e: "depth", - }, + e: "depth" + } }); } } - publishWsTrades(fills: Fill[], userId: string, market: string) { - fills.forEach((fill) => { + fills.forEach(fill => { RedisManager.getInstance().publishMessage(`trade@${market}`, { stream: `trade@${market}`, data: { @@ -443,42 +326,49 @@ export class Engine { p: fill.price, q: fill.qty.toString(), s: market, - }, + } }); }); } - onRamp(userId: string, amount: number) { const userBalance = this.balances.get(userId); if (!userBalance) { this.balances.set(userId, { - [CURRENCY]: { - available: amount, - locked: 0, - }, + available: amount, + locked: 0 }); } else { // @ts-ignore userBalance[CURRENCY].available += amount; } } - sendUpdatedDepthAt(price: string, market: string) { - const orderbook = this.orderbooks.find((o) => o.market === market); + const orderbook = this.orderbooks.find(o => o.market === market); if (!orderbook) { return; } const depth = orderbook.getMarketDepth(); - const updatedBids = depth?.bids.filter((b) => b[0] === price); - const updatedAsks = depth?.asks.filter((a) => a[0] === price); - + const updatedBids = depth?.bids.filter(b => b[0] === price); + const updatedAsks = depth?.asks.filter(a => a[0] === price); RedisManager.getInstance().publishMessage(`depth@${market}`, { stream: `depth@${market}`, data: { a: updatedAsks.length ? updatedAsks : [[price, "0"]], b: updatedBids.length ? updatedBids : [[price, "0"]], - e: "depth", - }, + e: "depth" + } + }); + + } + setBaseBalances() { + this.balances.set("1", { + available: 10, + locked: 0 + }); + + this.balances.set("2", { + available: 10, + locked: 0 }); } } diff --git a/services/engine/src/trade/Orderbook.ts b/services/engine/src/trade/Orderbook.ts index f1c77f6f..8bb05682 100644 --- a/services/engine/src/trade/Orderbook.ts +++ b/services/engine/src/trade/Orderbook.ts @@ -35,6 +35,7 @@ export class Orderbook { addOrder(order: Order) { if (order.side === "yes") { + console.log(order) // matchBid const { executedQty, fills } = this.matchBid(order); // fillBid @@ -69,7 +70,6 @@ export class Orderbook { } } - // @ts-ignore Todo; remove ts-ignore matchBid(order: Order): { fills: Fill[]; executedQty: number } { const fills: Fill[] = []; let executedQty = 0; @@ -93,19 +93,19 @@ export class Orderbook { }); } - // if order left after particially filled - for (let i = 0; i < this.asks.length; i++) { - if (this.asks[i]?.filled === this.asks[i]?.quantity) { - this.asks.splice(i, 1); - i--; - } + } + // if order left after particially filled + for (let i = 0; i < this.asks.length; i++) { + if (this.asks[i]?.filled === this.asks[i]?.quantity) { + this.asks.splice(i, 1); + i--; } - - return { - fills, - executedQty, - }; } + console.log("executedQty", executedQty, " fills", fills) + return { + fills, + executedQty, + }; } // @ts-ignore Todo; remove ts-ignore @@ -130,17 +130,17 @@ export class Orderbook { marketOrderId: this.bids[i]?.orderId!, }); } - for (let i = 0; i < this.bids.length; i++) { - if (this.bids[i]?.filled === this.bids[i]?.quantity) { - this.bids.splice(i, 1); - i--; - } + } + for (let i = 0; i < this.bids.length; i++) { + if (this.bids[i]?.filled === this.bids[i]?.quantity) { + this.bids.splice(i, 1); + i--; } - return { - fills, - executedQty, - }; } + return { + fills, + executedQty, + }; } getMarketDepth() { diff --git a/tests/package.json b/tests/package.json index d5650ea6..eaf8d9fa 100644 --- a/tests/package.json +++ b/tests/package.json @@ -16,6 +16,7 @@ "@types/express": "^5.0.0", "express": "^4.21.1", "@repo/order-queue":"*", - "@opinix/types":"*" + "@opinix/types":"*", + "@repo/engine":"*" } } diff --git a/tests/src/engine.test.ts b/tests/src/engine.test.ts index 5239b2ec..85b83818 100644 --- a/tests/src/engine.test.ts +++ b/tests/src/engine.test.ts @@ -1,3 +1,50 @@ -import {RedisManager} from "@repo/order-queue" +import { Engine } from "@repo/engine" import { describe, expect, it, vi } from "vitest"; import { CREATE_ORDER } from "@opinix/types"; + +vi.mock("@repo/order-queue", () => ({ + RedisManager: { + getInstance: () => ({ + publishMessage: vi.fn(), + sendToApi: vi.fn(), + pushMessage: vi.fn() + }) + } +})); + +describe("Engine", () => { + it("Publishes Trade updates", () => { + const engine = new Engine(); + const publishSpy = vi.spyOn(engine, "publishWsTrades"); + engine.processOrders({ + message: { + type: CREATE_ORDER, + data: { + market: "bitcoin-to-be-priced-at-6811470-usdt-or-more-at-0735-pm", + price: 0, + quantity: 1, + side: "yes", + userId: "1" + } + }, + clientId: "1" + }); + + engine.processOrders({ + message: { + type: CREATE_ORDER, + data: { + market: "bitcoin-to-be-priced-at-6811470-usdt-or-more-at-0735-pm", + price: 1, + quantity: 0, + side: "no", + userId: "2" + } + }, + clientId: "2" + }); + + expect(publishSpy).toHaveBeenCalledTimes(2); + + }); +}); \ No newline at end of file diff --git a/tests/src/index.ts b/tests/src/index.ts deleted file mode 100644 index 44b2510b..00000000 --- a/tests/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function sum(a:number, b:number) { - return a + b -} \ No newline at end of file diff --git a/tests/src/orderbook.test.ts b/tests/src/orderbook.test.ts deleted file mode 100644 index bb7b3ed3..00000000 --- a/tests/src/orderbook.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -// sum.test.js -import { expect, test } from 'vitest' -import { sum } from '.' - -test('adds 1 + 2 to equal 3', () => { - expect(sum(1, 2)).toBe(3) -}) \ No newline at end of file