diff --git a/web/lib/simulator/entries.ts b/web/lib/simulator/entries.ts index 6e3e587f..c280f8e2 100644 --- a/web/lib/simulator/entries.ts +++ b/web/lib/simulator/entries.ts @@ -17,16 +17,16 @@ function makeWeights(bids: Bid[]) { const weights = [] let yesPot = 0 let noPot = 0 + // First pass: calculate all the weights for (const { yesBid, noBid } of bids) { - const yesWeight = - noPot * (Math.log(yesBid + yesPot) - Math.log(yesPot)) || 0 - const noWeight = yesPot * (Math.log(noBid + noPot) - Math.log(noPot)) || 0 + const yesWeight = yesBid * Math.pow(noPot, 2) / (Math.pow(yesPot, 2) + yesBid * yesPot) || 0 + const noWeight = noBid * Math.pow(yesPot, 2) / (Math.pow(noPot, 2) + noBid * noPot) || 0 // Note: Need to calculate weights BEFORE updating pot yesPot += yesBid noPot += noBid - const prob = yesPot / (yesPot + noPot) + const prob = Math.pow(yesPot, 2) / (Math.pow(yesPot, 2) + Math.pow(noPot, 2)) weights.push({ yesBid, @@ -42,13 +42,16 @@ function makeWeights(bids: Bid[]) { export function makeEntries(bids: Bid[]): Entry[] { const YES_SEED = bids[0].yesBid const NO_SEED = bids[0].noBid + const weights = makeWeights(bids) const yesPot = weights.reduce((sum, { yesBid }) => sum + yesBid, 0) const noPot = weights.reduce((sum, { noBid }) => sum + noBid, 0) const yesWeightsSum = weights.reduce((sum, entry) => sum + entry.yesWeight, 0) const noWeightsSum = weights.reduce((sum, entry) => sum + entry.noWeight, 0) + // Second pass: calculate all the payouts const entries: Entry[] = [] + for (const weight of weights) { const { yesBid, noBid, yesWeight, noWeight } = weight // Payout: You get your initial bid back, as well as your share of the diff --git a/web/pages/simulator/index.tsx b/web/pages/simulator/index.tsx index 7697f6d6..4b61f40a 100644 --- a/web/pages/simulator/index.tsx +++ b/web/pages/simulator/index.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useMemo, useState } from 'react' +import { Line } from 'react-chartjs-2' import { CategoryScale, Chart, @@ -9,8 +10,7 @@ import { Tooltip, Legend, } from 'chart.js' -import { Line } from 'react-chartjs-2' -import { bids as sampleBids } from '../../lib/simulator/sample-bids' + import { Entry, makeEntries } from '../../lib/simulator/entries' import { Header } from '../../components/header' @@ -31,7 +31,7 @@ function TableBody(props: { entries: Entry[] }) { {props.entries.map((entry, i) => ( - {i + 1} + {props.entries.length - i} @@ -46,10 +46,10 @@ function TableRowStart(props: { entry: Entry }) { return ( <> -
SEED
+
ANTE
- {entry.yesBid} / {entry.noBid} + ${entry.yesBid} / ${entry.noBid} ) @@ -59,7 +59,7 @@ function TableRowStart(props: { entry: Entry }) {
YES
- {entry.yesBid} + ${entry.yesBid} ) } else { @@ -68,48 +68,56 @@ function TableRowStart(props: { entry: Entry }) {
NO
- {entry.noBid} + ${entry.noBid} ) } } -function TableRowEnd(props: { entry: Entry | null }) { +function TableRowEnd(props: { entry: Entry | null, isNew?: boolean }) { const { entry } = props if (!entry) { return ( <> - N/A - N/A - N/A - N/A + 0 + 0 + {!props.isNew && <> + N/A + N/A + } ) } else if (entry.yesBid && entry.noBid) { return ( <> + {(entry.prob * 100).toFixed(1)}% N/A - {entry.prob.toFixed(2)} - N/A - N/A + {!props.isNew && <> + N/A + N/A + } ) } else if (entry.yesBid) { return ( <> - {entry.yesWeight.toFixed(2)} - {entry.prob.toFixed(2)} - {entry.yesPayout.toFixed(2)} - {(entry.yesReturn * 100).toFixed(2)}% + {(entry.prob * 100).toFixed(1)}% + ${(entry.yesBid + entry.yesWeight).toFixed(0)} + {!props.isNew && <> + ${entry.yesPayout.toFixed(0)} + {(entry.yesReturn * 100).toFixed(0)}% + } ) } else { return ( <> - {entry.noWeight.toFixed(2)} - {entry.prob.toFixed(2)} - {entry.noPayout.toFixed(2)} - {(entry.noReturn * 100).toFixed(2)}% + {(entry.prob * 100).toFixed(1)}% + ${(entry.noBid + entry.noWeight).toFixed(0)} + {!props.isNew && <> + ${entry.noPayout.toFixed(0)} + {(entry.noReturn * 100).toFixed(0)}% + } ) } @@ -142,30 +150,39 @@ function NewBidTable(props: { setNewBid(0) } + + function toggleBidType() { setNewBidType(newBidType === 'YES' ? 'NO' : 'YES') } - let nextEntry: Entry | null = null + const nextBid = makeBid(newBidType, newBid) + const fakeBids = [...bids.slice(0, steps), nextBid] + const entries = makeEntries(fakeBids) + const nextEntry = entries[entries.length - 1] - if (newBid) { - const nextBid = makeBid(newBidType, newBid) - const fakeBids = [...bids.slice(0, steps), nextBid] - const entries = makeEntries(fakeBids) - nextEntry = entries[entries.length - 1] + function randomBid() { + const bidType = Math.random() < 0.5 + ? 'YES' + : 'NO' + const amount = Math.round(Math.random() * 500) + const bid = makeBid(bidType, amount) + + bids.splice(steps, 0, bid) + setBids(bids) + setSteps(steps + 1) + setNewBid(0) } - return ( + return <> - - + - - + @@ -200,7 +217,7 @@ function NewBidTable(props: { placeholder="0" className="input input-bordered" style={{ maxWidth: 100 }} - value={newBid} + value={newBid.toString()} onChange={(e) => setNewBid(parseInt(e.target.value) || 0)} onKeyUp={(e) => { if (e.key === 'Enter') { @@ -210,7 +227,12 @@ function NewBidTable(props: { onFocus={(e) => e.target.select()} /> - + + +
Order # TypeBidWeightBet ProbPayoutReturnEst Payout
+
- ) + + + } // Show a hello world React page export default function Simulator() { - const [steps, setSteps] = useState(10) - const [bids, setBids] = useState(sampleBids) + const [steps, setSteps] = useState(1) + const [bids, setBids] = useState([{ yesBid: 600, noBid: 400 }]) const entries = useMemo( () => makeEntries(bids.slice(0, steps)), [bids, steps] ) + + const reversedEntries = [...entries].reverse() + const probs = entries.map((entry) => entry.prob) const chartData = { - labels: Array.from({ length: steps }, (_, i) => i + 1), + labels: Array.from({ length: steps }, (_, i) => 1 + i), datasets: [ { label: 'Implied probability', @@ -257,16 +290,7 @@ export default function Simulator() {

Dynamic Parimutuel Market Simulator

- {/* Range slider that sets the current step */} - - setSteps(parseInt(e.target.value))} - /> + @@ -277,15 +301,15 @@ export default function Simulator() { Order # Type - Bid - Weight + Bet Prob - Max Payout + Est Payout + Payout Return - + @@ -297,6 +321,16 @@ export default function Simulator() {
YES
+ {/* Range slider that sets the current step */} + + setSteps(parseInt(e.target.value))} + />