From cd6f24f64e08bedcd1367c6b4307f5f9939a48a0 Mon Sep 17 00:00:00 2001 From: Rodrigo <95635797+poly-rodr@users.noreply.github.com> Date: Tue, 21 May 2024 12:17:01 -0300 Subject: [PATCH] market price calculations --- src/client.ts | 30 ++++++++- src/order-builder/helpers.ts | 13 ++++ tests/order-builder/helpers.test.ts | 97 ++++++++++++++++++++++++++++- 3 files changed, 136 insertions(+), 4 deletions(-) diff --git a/src/client.ts b/src/client.ts index 2a8ee3c..df8abcb 100644 --- a/src/client.ts +++ b/src/client.ts @@ -103,6 +103,7 @@ import { } from "./endpoints"; import { OrderBuilder } from "./order-builder/builder"; import { END_CURSOR, INITIAL_CURSOR } from "./constants"; +import { calculateMarketPrice } from "./order-builder/helpers"; export class ClobClient { readonly host: string; @@ -507,8 +508,11 @@ export class ClobClient { const negRisk = options?.negRisk ?? false; if (!userMarketOrder.price) { - const marketPrice = await this.getPrice(tokenID, Side.SELL); - userMarketOrder.price = parseFloat(marketPrice); + userMarketOrder.price = await this.calculateMarketPrice( + tokenID, + Side.BUY, + userMarketOrder.amount, + ); } if (!priceValid(userMarketOrder.price, tickSize)) { @@ -824,6 +828,28 @@ export class ClobClient { return this.get(`${this.host}${GET_MARKET_TRADES_EVENTS}${conditionID}`); } + public async calculateMarketPrice( + tokenID: string, + side: Side, + amount: number, + ): Promise { + const book = await this.getOrderBook(tokenID); + if (!book) { + throw new Error("no orderbook"); + } + if (side === Side.BUY) { + if (!book.asks) { + throw new Error("no match"); + } + return calculateMarketPrice(book.asks, amount); + } else { + if (!book.bids) { + throw new Error("no match"); + } + return calculateMarketPrice(book.bids, amount); + } + } + private canL1Auth(): void { if (this.signer === undefined) { throw L1_AUTH_UNAVAILABLE_ERROR; diff --git a/src/order-builder/helpers.ts b/src/order-builder/helpers.ts index 081e04b..ad0143a 100644 --- a/src/order-builder/helpers.ts +++ b/src/order-builder/helpers.ts @@ -16,6 +16,7 @@ import { TickSize, RoundConfig, CreateOrderOptions, + OrderSummary, } from "../types"; import { decimalPlaces, roundDown, roundNormal, roundUp } from "../utilities"; import { @@ -301,3 +302,15 @@ export const createMarketBuyOrder = async ( return buildOrder(eoaSigner, exchangeContract, chainId, orderData); }; + +export const calculateMarketPrice = (positions: OrderSummary[], amountToMatch: number) => { + let sum = 0; + for (let i = 0; i < positions.length; i++) { + const p = positions[i]; + sum += parseFloat(p.size) * parseFloat(p.price); + if (sum >= amountToMatch) { + return parseFloat(p.price); + } + } + throw new Error("no match"); +}; diff --git a/tests/order-builder/helpers.test.ts b/tests/order-builder/helpers.test.ts index da14efe..925b2e5 100644 --- a/tests/order-builder/helpers.test.ts +++ b/tests/order-builder/helpers.test.ts @@ -1,6 +1,6 @@ import "mocha"; import { expect } from "chai"; -import { UserOrder, Side, Chain, UserMarketOrder } from "../../src/types"; +import { UserOrder, Side, Chain, UserMarketOrder, OrderSummary } from "../../src/types"; import { buildOrderCreationArgs, buildOrder, @@ -10,6 +10,7 @@ import { getOrderRawAmounts, getMarketBuyOrderRawAmounts, ROUNDING_CONFIG, + calculateMarketPrice, } from "../../src/order-builder/helpers"; import { OrderData, SignatureType, Side as UtilsSide } from "@polymarket/order-utils"; import { Wallet } from "@ethersproject/wallet"; @@ -3477,7 +3478,7 @@ describe("helpers", () => { }); }); - describe("CTF Exchange", () => { + describe("Neg Risk CTF Exchange", () => { describe("buy order", async () => { it("0.1", async () => { const order: UserMarketOrder = { @@ -3621,4 +3622,96 @@ describe("helpers", () => { }); }); }); + + describe.only("calculateMarketPrice", () => { + describe("BUY", () => { + it("empty orderbook", () => { + expect(() => calculateMarketPrice([], 100)).to.throw("no match"); + }); + it("not enough", () => { + const positions = [ + { price: "0.5", size: "100" }, + { price: "0.4", size: "100" }, + ] as OrderSummary[]; + expect(() => calculateMarketPrice(positions, 100)).to.throw("no match"); + }); + it("ok", () => { + let positions = [ + { price: "0.5", size: "100" }, + { price: "0.4", size: "100" }, + { price: "0.3", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.3); + + positions = [ + { price: "0.5", size: "100" }, + { price: "0.4", size: "200" }, + { price: "0.3", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.5", size: "120" }, + { price: "0.4", size: "100" }, + { price: "0.3", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.5", size: "200" }, + { price: "0.4", size: "100" }, + { price: "0.3", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.5); + }); + }); + describe("SELL", () => { + it("empty orderbook", () => { + expect(() => calculateMarketPrice([], 100)).to.throw("no match"); + }); + it("not enough", () => { + const positions = [ + { price: "0.4", size: "100" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(() => calculateMarketPrice(positions, 100)).to.throw("no match"); + }); + it("ok", () => { + let positions = [ + { price: "0.3", size: "100" }, + { price: "0.4", size: "100" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.5); + + positions = [ + { price: "0.3", size: "100" }, + { price: "0.4", size: "300" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.3", size: "100" }, + { price: "0.4", size: "200" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.3", size: "300" }, + { price: "0.4", size: "100" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.3", size: "334" }, + { price: "0.4", size: "100" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.3); + }); + }); + }); });