Skip to content

Commit

Permalink
Merge pull request #133 from Polymarket/feat/fok-sells
Browse files Browse the repository at this point in the history
FOK Market Sells
  • Loading branch information
poly-rodr authored Jan 16, 2025
2 parents 605334a + 4936037 commit 0ea25e1
Show file tree
Hide file tree
Showing 9 changed files with 2,025 additions and 278 deletions.
17 changes: 7 additions & 10 deletions examples/marketOrder.ts → examples/marketBuyOrder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ethers } from "ethers";
import { config as dotenvConfig } from "dotenv";
import { resolve } from "path";
import { ApiKeyCreds, Chain, ClobClient, OrderType } from "../src";
import { ApiKeyCreds, Chain, ClobClient, OrderType, Side } from "../src";

dotenvConfig({ path: resolve(__dirname, "../.env") });

Expand All @@ -18,20 +18,17 @@ async function main() {
};
const clobClient = new ClobClient(host, chainId, wallet, creds);

// Create a YES market buy order for the equivalent of 100 USDC for the market price
const YES = "71321045679252212594626385532706912750332728571942532289631379312455583992563";
const marketOrder = await clobClient.createMarketBuyOrder({
// Create a YES market buy order for the equivalent of 100 USDC for the market price
const marketBuyOrder = await clobClient.createMarketOrder({
tokenID: YES,
amount: 100,
feeRateBps: 0,
nonce: 0,
price: 0.5,
amount: 100, // $$$
side: Side.BUY,
});
console.log("Created Market Order", marketOrder);
console.log("Created Market BUY Order", marketBuyOrder);

// Send it to the server
const resp = await clobClient.postOrder(marketOrder, OrderType.FOK);
console.log(resp);
console.log(await clobClient.postOrder(marketBuyOrder, OrderType.FOK));
}

main();
34 changes: 34 additions & 0 deletions examples/marketSellOrder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ethers } from "ethers";
import { config as dotenvConfig } from "dotenv";
import { resolve } from "path";
import { ApiKeyCreds, Chain, ClobClient, OrderType, Side } from "../src";

dotenvConfig({ path: resolve(__dirname, "../.env") });

async function main() {
const wallet = new ethers.Wallet(`${process.env.PK}`);
const chainId = parseInt(`${process.env.CHAIN_ID || Chain.AMOY}`) as Chain;
console.log(`Address: ${await wallet.getAddress()}, chainId: ${chainId}`);

const host = process.env.CLOB_API_URL || "http://localhost:8080";
const creds: ApiKeyCreds = {
key: `${process.env.CLOB_API_KEY}`,
secret: `${process.env.CLOB_SECRET}`,
passphrase: `${process.env.CLOB_PASS_PHRASE}`,
};
const clobClient = new ClobClient(host, chainId, wallet, creds);

const YES = "71321045679252212594626385532706912750332728571942532289631379312455583992563";
// Create a YES market sell order for the equivalent of 100 shares for the market price
const marketBuyOrder = await clobClient.createMarketOrder({
tokenID: YES,
amount: 110, // SHARES
side: Side.SELL,
});
console.log("Created Market SELL Order", marketBuyOrder);

// Send it to the server
console.log(await clobClient.postOrder(marketBuyOrder, OrderType.FOK));
}

main();
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@polymarket/clob-client",
"description": "Typescript client for Polymarket's CLOB",
"version": "4.13.0",
"version": "4.14.0",
"contributors": [
{
"name": "Jonathan Amenechi",
Expand Down
10 changes: 5 additions & 5 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ import {
} from "./endpoints";
import { OrderBuilder } from "./order-builder/builder";
import { END_CURSOR, INITIAL_CURSOR } from "./constants";
import { calculateMarketPrice } from "./order-builder/helpers";
import { calculateBuyMarketPrice, calculateSellMarketPrice } from "./order-builder/helpers";

export class ClobClient {
readonly host: string;
Expand Down Expand Up @@ -639,7 +639,7 @@ export class ClobClient {
});
}

public async createMarketBuyOrder(
public async createMarketOrder(
userMarketOrder: UserMarketOrder,
options?: Partial<CreateOrderOptions>,
): Promise<SignedOrder> {
Expand All @@ -652,7 +652,7 @@ export class ClobClient {
if (!userMarketOrder.price) {
userMarketOrder.price = await this.calculateMarketPrice(
tokenID,
Side.BUY,
userMarketOrder.side,
userMarketOrder.amount,
);
}
Expand Down Expand Up @@ -1017,12 +1017,12 @@ export class ClobClient {
if (!book.asks) {
throw new Error("no match");
}
return calculateMarketPrice(book.asks, amount);
return calculateBuyMarketPrice(book.asks, amount);
} else {
if (!book.bids) {
throw new Error("no match");
}
return calculateMarketPrice(book.bids, amount);
return calculateSellMarketPrice(book.bids, amount);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/order-builder/builder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Wallet } from "@ethersproject/wallet";
import { JsonRpcSigner } from "@ethersproject/providers";
import { SignedOrder, SignatureType } from "@polymarket/order-utils";
import { createMarketBuyOrder, createOrder } from "./helpers";
import { createMarketOrder, createOrder } from "./helpers";
import { Chain, CreateOrderOptions, UserMarketOrder, UserOrder } from "../types";

export class OrderBuilder {
Expand Down Expand Up @@ -53,7 +53,7 @@ export class OrderBuilder {
userMarketOrder: UserMarketOrder,
options: CreateOrderOptions,
): Promise<SignedOrder> {
return createMarketBuyOrder(
return createMarketOrder(
this.signer,
this.chainId,
this.signatureType,
Expand Down
89 changes: 64 additions & 25 deletions src/order-builder/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ import {
OrderSummary,
} from "../types";
import { decimalPlaces, roundDown, roundNormal, roundUp } from "../utilities";
import {
COLLATERAL_TOKEN_DECIMALS,
CONDITIONAL_TOKEN_DECIMALS,
getContractConfig,
} from "../config";
import { COLLATERAL_TOKEN_DECIMALS, getContractConfig } from "../config";

export const ROUNDING_CONFIG: Record<TickSize, RoundConfig> = {
"0.1": {
Expand Down Expand Up @@ -129,7 +125,7 @@ export const buildOrderCreationArgs = async (
);

const makerAmount = parseUnits(rawMakerAmt.toString(), COLLATERAL_TOKEN_DECIMALS).toString();
const takerAmount = parseUnits(rawTakerAmt.toString(), CONDITIONAL_TOKEN_DECIMALS).toString();
const takerAmount = parseUnits(rawTakerAmt.toString(), COLLATERAL_TOKEN_DECIMALS).toString();

let taker;
if (typeof userOrder.taker !== "undefined" && userOrder.taker) {
Expand Down Expand Up @@ -196,47 +192,66 @@ export const createOrder = async (
return buildOrder(eoaSigner, exchangeContract, chainId, orderData);
};

export const getMarketBuyOrderRawAmounts = (
export const getMarketOrderRawAmounts = (
side: Side,
amount: number,
price: number,
roundConfig: RoundConfig,
): { rawMakerAmt: number; rawTakerAmt: number } => {
): { side: UtilsSide; rawMakerAmt: number; rawTakerAmt: number } => {
// force 2 decimals places
const rawMakerAmt = roundDown(amount, roundConfig.size);
const rawPrice = roundDown(price, roundConfig.price);

let rawTakerAmt = rawMakerAmt / rawPrice;
if (decimalPlaces(rawTakerAmt) > roundConfig.amount) {
rawTakerAmt = roundUp(rawTakerAmt, roundConfig.amount + 4);
if (side === Side.BUY) {
const rawMakerAmt = roundDown(amount, roundConfig.size);
let rawTakerAmt = rawMakerAmt / rawPrice;
if (decimalPlaces(rawTakerAmt) > roundConfig.amount) {
rawTakerAmt = roundDown(rawTakerAmt, roundConfig.amount);
rawTakerAmt = roundUp(rawTakerAmt, roundConfig.amount + 4);
if (decimalPlaces(rawTakerAmt) > roundConfig.amount) {
rawTakerAmt = roundDown(rawTakerAmt, roundConfig.amount);
}
}
return {
side: UtilsSide.BUY,
rawMakerAmt,
rawTakerAmt,
};
} else {
const rawMakerAmt = roundDown(amount, roundConfig.size);
let rawTakerAmt = rawMakerAmt * rawPrice;
if (decimalPlaces(rawTakerAmt) > roundConfig.amount) {
rawTakerAmt = roundUp(rawTakerAmt, roundConfig.amount + 4);
if (decimalPlaces(rawTakerAmt) > roundConfig.amount) {
rawTakerAmt = roundDown(rawTakerAmt, roundConfig.amount);
}
}
}

return {
rawMakerAmt,
rawTakerAmt,
};
return {
side: UtilsSide.SELL,
rawMakerAmt,
rawTakerAmt,
};
}
};

/**
* Translate simple user market order to args used to generate Orders
*/
export const buildMarketBuyOrderCreationArgs = async (
export const buildMarketOrderCreationArgs = async (
signer: string,
maker: string,
signatureType: SignatureType,
userMarketOrder: UserMarketOrder,
roundConfig: RoundConfig,
): Promise<OrderData> => {
const { rawMakerAmt, rawTakerAmt } = getMarketBuyOrderRawAmounts(
const { side, rawMakerAmt, rawTakerAmt } = getMarketOrderRawAmounts(
userMarketOrder.side,
userMarketOrder.amount,
userMarketOrder.price || 1,
roundConfig,
);

const makerAmount = parseUnits(rawMakerAmt.toString(), COLLATERAL_TOKEN_DECIMALS).toString();
const takerAmount = parseUnits(rawTakerAmt.toString(), CONDITIONAL_TOKEN_DECIMALS).toString();
const takerAmount = parseUnits(rawTakerAmt.toString(), COLLATERAL_TOKEN_DECIMALS).toString();

let taker;
if (typeof userMarketOrder.taker !== "undefined" && userMarketOrder.taker) {
Expand Down Expand Up @@ -265,7 +280,7 @@ export const buildMarketBuyOrderCreationArgs = async (
tokenId: userMarketOrder.tokenID,
makerAmount,
takerAmount,
side: UtilsSide.BUY,
side,
feeRateBps,
nonce,
signer,
Expand All @@ -274,7 +289,7 @@ export const buildMarketBuyOrderCreationArgs = async (
} as OrderData;
};

export const createMarketBuyOrder = async (
export const createMarketOrder = async (
eoaSigner: Wallet | JsonRpcSigner,
chainId: Chain,
signatureType: SignatureType,
Expand All @@ -288,7 +303,7 @@ export const createMarketBuyOrder = async (
const maker = funderAddress === undefined ? eoaSignerAddress : funderAddress;
const contractConfig = getContractConfig(chainId);

const orderData = await buildMarketBuyOrderCreationArgs(
const orderData = await buildMarketOrderCreationArgs(
eoaSignerAddress,
maker,
signatureType,
Expand All @@ -303,7 +318,13 @@ export const createMarketBuyOrder = async (
return buildOrder(eoaSigner, exchangeContract, chainId, orderData);
};

export const calculateMarketPrice = (positions: OrderSummary[], amountToMatch: number) => {
/**
* calculateBuyMarketPrice calculates the market price to buy a $$ amount
* @param positions
* @param amountToMatch worth to buy
* @returns
*/
export const calculateBuyMarketPrice = (positions: OrderSummary[], amountToMatch: number) => {
let sum = 0;
for (let i = 0; i < positions.length; i++) {
const p = positions[i];
Expand All @@ -314,3 +335,21 @@ export const calculateMarketPrice = (positions: OrderSummary[], amountToMatch: n
}
throw new Error("no match");
};

/**
* calculateSellMarketPrice calculates the market price to sell a shares
* @param positions
* @param amountToMatch sells to share
* @returns
*/
export const calculateSellMarketPrice = (positions: OrderSummary[], amountToMatch: number) => {
let sum = 0;
for (let i = positions.length - 1; i >= 0; i--) {
const p = positions[i];
sum += parseFloat(p.size);
if (sum >= amountToMatch) {
return parseFloat(p.price);
}
}
throw new Error("no match");
};
8 changes: 7 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,16 @@ export interface UserMarketOrder {
price?: number;

/**
* Amount in terms of Collateral
* BUY orders: $$$ Amount to buy
* SELL orders: Shares to sell
*/
amount: number;

/**
* Side of the order
*/
side: Side;

/**
* Fee rate, in basis points, charged to the order maker, charged on proceeds
*/
Expand Down
Loading

0 comments on commit 0ea25e1

Please sign in to comment.