From ff9a649c3cd3d2f9ac578252212661c683cd3dbe Mon Sep 17 00:00:00 2001 From: Marshall Polaris Date: Sun, 31 Jul 2022 23:38:45 -0700 Subject: [PATCH] Allow unspecfied outcome as input to `sellshares` --- docs/docs/api.md | 6 +++--- functions/src/sell-shares.ts | 29 ++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/docs/docs/api.md b/docs/docs/api.md index 8b7dce30..b1265df4 100644 --- a/docs/docs/api.md +++ b/docs/docs/api.md @@ -581,12 +581,12 @@ $ curl https://manifold.markets/api/v0/market/{marketId}/resolve -X POST \ ### `POST /v0/market/[marketId]/sell` -Sells some quantity of shares in a market on behalf of the authorized user. +Sells some quantity of shares in a binary market on behalf of the authorized user. Parameters: -- `outcome`: Required. One of `YES`, `NO`, or a `number` indicating the numeric - bucket ID, depending on the market type. +- `outcome`: Optional. One of `YES`, or `NO`. If you leave it off, and you only + own one kind of shares, you will sell that kind of shares. - `shares`: Optional. The amount of shares to sell of the outcome given above. If not provided, all the shares you own will be sold. diff --git a/functions/src/sell-shares.ts b/functions/src/sell-shares.ts index b6238434..037102cc 100644 --- a/functions/src/sell-shares.ts +++ b/functions/src/sell-shares.ts @@ -1,4 +1,4 @@ -import { sumBy, uniq } from 'lodash' +import { mapValues, groupBy, sumBy, uniq } from 'lodash' import * as admin from 'firebase-admin' import { z } from 'zod' @@ -17,7 +17,7 @@ import { redeemShares } from './redeem-shares' const bodySchema = z.object({ contractId: z.string(), shares: z.number().optional(), // leave it out to sell all shares - outcome: z.enum(['YES', 'NO']), + outcome: z.enum(['YES', 'NO']).optional(), // leave it out to sell whichever you have }) export const sellshares = newEndpoint({}, async (req, auth) => { @@ -46,9 +46,28 @@ export const sellshares = newEndpoint({}, async (req, auth) => { throw new APIError(400, 'Trading is closed.') const prevLoanAmount = sumBy(userBets, (bet) => bet.loanAmount ?? 0) + const betsByOutcome = groupBy(userBets, (bet) => bet.outcome) + const sharesByOutcome = mapValues(betsByOutcome, (bets) => + sumBy(bets, (b) => b.shares) + ) - const outcomeBets = userBets.filter((bet) => bet.outcome == outcome) - const maxShares = sumBy(outcomeBets, (bet) => bet.shares) + let chosenOutcome: 'YES' | 'NO' + if (outcome != null) { + chosenOutcome = outcome + } else { + const nonzeroShares = Object.entries(sharesByOutcome).filter( + ([_k, v]) => v + ) + if (nonzeroShares.length > 1) { + throw new APIError( + 400, + `You own multiple kinds of shares, but did not specify which to sell.` + ) + } + chosenOutcome = nonzeroShares[0][0] as 'YES' | 'NO' + } + + const maxShares = sharesByOutcome[chosenOutcome] const sharesToSell = shares ?? maxShares if (!floatingLesserEqual(sharesToSell, maxShares)) @@ -63,7 +82,7 @@ export const sellshares = newEndpoint({}, async (req, auth) => { const { newBet, newPool, newP, fees, makers } = getCpmmSellBetInfo( soldShares, - outcome, + chosenOutcome, contract, prevLoanAmount, unfilledBets