()
+ const [isSubmitting, setIsSubmitting] = useState(false)
+ const [wasSubmitted, setWasSubmitted] = useState(false)
+
+ const rangeError =
+ lowLimitProb !== undefined &&
+ highLimitProb !== undefined &&
+ lowLimitProb >= highLimitProb
+
+ const outOfRangeError =
+ (lowLimitProb !== undefined &&
+ (lowLimitProb <= 0 || lowLimitProb >= 100)) ||
+ (highLimitProb !== undefined &&
+ (highLimitProb <= 0 || highLimitProb >= 100))
+
+ const initialLow = initialProb * 0.9
+ const initialHigh = initialProb + (1 - initialProb) * 0.1
+ const lowPlaceholder = Math.round(
+ isPseudoNumeric ? getMappedValue(contract)(initialLow) : initialLow * 100
+ ).toString()
+ const highPlaceholder = Math.round(
+ isPseudoNumeric ? getMappedValue(contract)(initialHigh) : initialHigh * 100
+ ).toString()
+
+ const hasYesLimitBet = lowLimitProb !== undefined && !!betAmount
+ const hasNoLimitBet = highLimitProb !== undefined && !!betAmount
+ const hasTwoBets = hasYesLimitBet && hasNoLimitBet
+
+ const betDisabled =
+ isSubmitting ||
+ !betAmount ||
+ rangeError ||
+ outOfRangeError ||
+ error ||
+ (!hasYesLimitBet && !hasNoLimitBet)
+
+ const yesLimitProb =
+ lowLimitProb === undefined ? undefined : lowLimitProb / 100
+ const noLimitProb =
+ highLimitProb === undefined ? undefined : highLimitProb / 100
+
+ const shares =
+ yesLimitProb !== undefined && noLimitProb !== undefined
+ ? Math.min(
+ (betAmount ?? 0) / yesLimitProb,
+ (betAmount ?? 0) / (1 - noLimitProb)
+ )
+ : (betAmount ?? 0) / (yesLimitProb ?? 1 - (noLimitProb ?? 1))
+
+ const yesAmount = shares * (yesLimitProb ?? 1)
+ const noAmount = shares * (1 - (noLimitProb ?? 1))
+
+ const profitIfBothFilled = shares - (yesAmount + noAmount)
+
+ function onBetChange(newAmount: number | undefined) {
+ setWasSubmitted(false)
+ setBetAmount(newAmount)
+ }
+
+ async function submitBet() {
+ if (!user || betDisabled) return
+
+ setError(undefined)
+ setIsSubmitting(true)
+
+ const betsPromise = hasTwoBets
+ ? Promise.all([
+ placeBet({
+ outcome: 'YES',
+ amount: yesAmount,
+ limitProb: yesLimitProb,
+ contractId: contract.id,
+ }),
+ placeBet({
+ outcome: 'NO',
+ amount: noAmount,
+ limitProb: noLimitProb,
+ contractId: contract.id,
+ }),
+ ])
+ : placeBet({
+ outcome: hasYesLimitBet ? 'YES' : 'NO',
+ amount: betAmount,
+ contractId: contract.id,
+ limitProb: hasYesLimitBet ? yesLimitProb : noLimitProb,
+ })
+
+ betsPromise
+ .catch((e) => {
+ if (e instanceof APIError) {
+ setError(e.toString())
+ } else {
+ console.error(e)
+ setError('Error placing bet')
+ }
+ setIsSubmitting(false)
+ })
+ .then((r) => {
+ console.log('placed bet. Result:', r)
+ setIsSubmitting(false)
+ setWasSubmitted(true)
+ setBetAmount(undefined)
+ if (onBuySuccess) onBuySuccess()
+ })
+
+ if (hasYesLimitBet) {
+ track('bet', {
+ location: 'bet panel',
+ outcomeType: contract.outcomeType,
+ slug: contract.slug,
+ contractId: contract.id,
+ amount: yesAmount,
+ outcome: 'YES',
+ limitProb: yesLimitProb,
+ isLimitOrder: true,
+ isRangeOrder: hasTwoBets,
+ })
+ }
+ if (hasNoLimitBet) {
+ track('bet', {
+ location: 'bet panel',
+ outcomeType: contract.outcomeType,
+ slug: contract.slug,
+ contractId: contract.id,
+ amount: noAmount,
+ outcome: 'NO',
+ limitProb: noLimitProb,
+ isLimitOrder: true,
+ isRangeOrder: hasTwoBets,
+ })
+ }
+ }
+
+ const {
+ currentPayout: yesPayout,
+ currentReturn: yesReturn,
+ totalFees: yesFees,
+ newBet: yesBet,
+ } = getBinaryBetStats(
+ 'YES',
+ yesAmount,
+ contract,
+ Math.min(yesLimitProb ?? initialLow, 0.999),
+ unfilledBets as LimitBet[]
+ )
+ const yesReturnPercent = formatPercent(yesReturn)
+
+ const {
+ currentPayout: noPayout,
+ currentReturn: noReturn,
+ totalFees: noFees,
+ newBet: noBet,
+ } = getBinaryBetStats(
+ 'NO',
+ noAmount,
+ contract,
+ Math.max(noLimitProb ?? initialHigh, 0.01),
+ unfilledBets as LimitBet[]
+ )
+ const noReturnPercent = formatPercent(noReturn)
+
+ return (
+
+
+ Bet when the {isPseudoNumeric ? 'value' : 'probability'} reaches Low
+ and/or High limit.
+
+
+
+
+ Low
+
+
+
+ High
+
+
+
+
+ {rangeError && (
+
+ Low limit must be less than High limit
+
+ )}
+ {outOfRangeError && (
+
+ Limit is out of range
+
+ )}
+
+
+ Max amount*
+
+
+
+
+ {(hasTwoBets || (hasYesLimitBet && yesBet.amount !== 0)) && (
+
+
+ {isPseudoNumeric ? (
+
+ ) : (
+
+ )}{' '}
+ current fill
+
+
+ {formatMoney(yesBet.amount)} of{' '}
+ {formatMoney(yesBet.orderAmount ?? 0)}
+
+
+ )}
+ {(hasTwoBets || (hasNoLimitBet && noBet.amount !== 0)) && (
+
+
+ {isPseudoNumeric ? (
+
+ ) : (
+
+ )}{' '}
+ current fill
+
+
+ {formatMoney(noBet.amount)} of{' '}
+ {formatMoney(noBet.orderAmount ?? 0)}
+
+
+ )}
+ {hasTwoBets && (
+
+
+ Profit if both orders filled
+
+
+ {formatMoney(profitIfBothFilled)}
+
+
+ )}
+ {hasYesLimitBet && !hasTwoBets && (
+
+
+
+ {isPseudoNumeric ? (
+ 'Max payout'
+ ) : (
+ <>
+ Max payout
+ >
+ )}
+
+
+
+
+
+ {formatMoney(yesPayout)}
+
+ (+{yesReturnPercent})
+
+
+ )}
+ {hasNoLimitBet && !hasTwoBets && (
+
+
+
+ {isPseudoNumeric ? (
+ 'Max payout'
+ ) : (
+ <>
+ Max payout
+ >
+ )}
+
+
+
+
+
+ {formatMoney(noPayout)}
+
+ (+{noReturnPercent})
+
+
+ )}
+
+
+ {(hasYesLimitBet || hasNoLimitBet) && }
+
{user && (
)}
- {wasSubmitted && (
- {isLimitOrder ? 'Order' : 'Bet'} submitted!
- )}
- >
+ {wasSubmitted && Order submitted!
}
+
)
}
diff --git a/web/components/bucket-input.tsx b/web/components/bucket-input.tsx
index 195032dc..19dacd65 100644
--- a/web/components/bucket-input.tsx
+++ b/web/components/bucket-input.tsx
@@ -9,8 +9,9 @@ export function BucketInput(props: {
contract: NumericContract | PseudoNumericContract
isSubmitting?: boolean
onBucketChange: (value?: number, bucket?: string) => void
+ placeholder?: string
}) {
- const { contract, isSubmitting, onBucketChange } = props
+ const { contract, isSubmitting, onBucketChange, placeholder } = props
const [numberString, setNumberString] = useState('')
@@ -39,7 +40,7 @@ export function BucketInput(props: {
error={undefined}
disabled={isSubmitting}
numberString={numberString}
- label="Value"
+ placeholder={placeholder}
/>
)
}
diff --git a/web/components/number-input.tsx b/web/components/number-input.tsx
index d7159fab..0b48df6e 100644
--- a/web/components/number-input.tsx
+++ b/web/components/number-input.tsx
@@ -9,8 +9,8 @@ export function NumberInput(props: {
numberString: string
onChange: (newNumberString: string) => void
error: string | undefined
- label: string
disabled?: boolean
+ placeholder?: string
className?: string
inputClassName?: string
// Needed to focus the amount input
@@ -21,8 +21,8 @@ export function NumberInput(props: {
numberString,
onChange,
error,
- label,
disabled,
+ placeholder,
className,
inputClassName,
inputRef,
@@ -32,16 +32,17 @@ export function NumberInput(props: {
return (