Skip to content

Commit 5474bae

Browse files
adding market depth chart to limit orders popup (#1565)
* adding market depth chart to limit orders popup * removing unused code * unpacking props at beginning of component --------- Co-authored-by: James Grugett <jahooma@gmail.com>
1 parent 127501d commit 5474bae

4 files changed

Lines changed: 556 additions & 12 deletions

File tree

web/components/bet/depth-chart.tsx

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import {
2+
VictoryChart,
3+
VictoryGroup,
4+
VictoryArea,
5+
VictoryLine,
6+
VictoryAxis,
7+
VictoryLabel,
8+
} from 'victory'
9+
import { LimitBet } from 'common/bet'
10+
import { CPMMBinaryContract, PseudoNumericContract } from 'common/contract'
11+
import { getDisplayProbability } from 'common/calculate'
12+
13+
interface DepthChartProps {
14+
contract: CPMMBinaryContract | PseudoNumericContract
15+
yesBets: LimitBet[]
16+
noBets: LimitBet[]
17+
}
18+
19+
export function DepthChart(props: DepthChartProps) {
20+
const { contract, yesBets, noBets } = props
21+
22+
// Won't display a depth chart for numeric contracts, only binary contracts right now
23+
if (contract.outcomeType === 'PSEUDO_NUMERIC') {
24+
return <div className="depth-chart" />
25+
}
26+
27+
const yesData = cumulative(yesBets)
28+
const noData = cumulative(noBets)
29+
const maxAmount = Math.max(
30+
yesData[yesData.length - 1].y,
31+
noData[noData.length - 1].y
32+
)
33+
let minX = yesData[yesData.length - 1].x
34+
let maxX = noData[noData.length - 1].x
35+
const xRange = maxX - minX
36+
minX -= xRange * 0.1
37+
if (minX < 0) {
38+
minX = 0
39+
}
40+
maxX += xRange * 0.1
41+
if (maxX > 1) {
42+
maxX = 1
43+
}
44+
yesData.unshift({ x: minX, y: yesData[0].y })
45+
noData.push({ x: maxX, y: noData[noData.length - 1].y })
46+
47+
const currentValue = getDisplayProbability(contract)
48+
49+
return (
50+
<div className="depth-chart">
51+
<h2>Market depth chart</h2>
52+
<VictoryChart
53+
minDomain={{ x: minX, y: 0 }}
54+
maxDomain={{ x: maxX, y: maxAmount }}
55+
padding={{ top: 50, bottom: 50, left: 100, right: 50 }}
56+
domainPadding={30}
57+
>
58+
<VictoryAxis
59+
tickFormat={(t) => `${Math.round(t * 100)}%`}
60+
tickCount={10}
61+
label="Chance"
62+
axisLabelComponent={<VictoryLabel dy={10} />}
63+
/>
64+
<VictoryAxis
65+
tickFormat={(t) => `${Math.round(t)}` + 'M'}
66+
tickCount={6}
67+
dependentAxis={true}
68+
axisLabelComponent={<VictoryLabel dy={-30} />}
69+
label={'Amount'}
70+
/>
71+
<VictoryGroup
72+
style={{
73+
data: { strokeWidth: 1, fillOpacity: 0.4 },
74+
}}
75+
>
76+
{/* // Line that shows the current value */}
77+
<VictoryLine
78+
style={{
79+
data: { stroke: 'gray' },
80+
}}
81+
data={[
82+
{ x: currentValue, y: 0 },
83+
{ x: currentValue, y: maxAmount },
84+
]}
85+
/>
86+
{/* First vertical line of the "yes" bets */}
87+
<VictoryLine
88+
style={{
89+
data: { stroke: '#059669' },
90+
}}
91+
data={[
92+
{ x: yesData[yesData.length - 1].x, y: 0 },
93+
{
94+
x: yesData[yesData.length - 1].x,
95+
y: yesData[yesData.length - 1].y,
96+
},
97+
]}
98+
/>
99+
{/* First vertical line of the "no" bets */}
100+
<VictoryLine
101+
style={{
102+
data: { stroke: 'red' },
103+
}}
104+
data={[
105+
{ x: noData[0].x, y: 0 },
106+
{ x: noData[0].x, y: noData[0].y },
107+
]}
108+
/>
109+
{/* // Area that shows the yes bets */}
110+
<VictoryArea
111+
style={{
112+
data: { fill: '#6EE7B7', stroke: '#059669' },
113+
}}
114+
data={yesData}
115+
interpolation="stepBefore"
116+
/>
117+
{/* // Area that shows the no bets */}
118+
<VictoryArea
119+
style={{
120+
data: { fill: 'red', stroke: 'red' },
121+
}}
122+
data={noData}
123+
interpolation="stepAfter"
124+
/>
125+
</VictoryGroup>
126+
</VictoryChart>
127+
</div>
128+
)
129+
}
130+
131+
type Coordinate = { x: number; y: number }
132+
133+
// Converts a list of LimitBets into a list of coordinates to render into a depth chart.
134+
// Going in order of probability, the y value accumulates each order's amount.
135+
function cumulative(bets: LimitBet[]): Coordinate[] {
136+
const result: Coordinate[] = []
137+
let totalAmount = 0
138+
139+
for (let i = 0; i < bets.length; i++) {
140+
totalAmount += bets[i].orderAmount
141+
result.push({ x: bets[i].limitProb, y: totalAmount })
142+
}
143+
144+
return result
145+
}

web/components/bet/limit-bets.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { Table } from '../widgets/table'
2323
import { Title } from '../widgets/title'
2424
import { Tooltip } from '../widgets/tooltip'
2525
import { InfoTooltip } from '../widgets/info-tooltip'
26+
import { DepthChart } from './depth-chart'
2627

2728
export function YourOrders(props: {
2829
contract: CPMMBinaryContract | PseudoNumericContract
@@ -206,6 +207,7 @@ export function OrderBookButton(props: {
206207
<Button
207208
className={className}
208209
onClick={() => setOpen(true)}
210+
disabled={limitBets.length === 0}
209211
size="xs"
210212
color="indigo"
211213
>
@@ -221,7 +223,8 @@ export function OrderBookButton(props: {
221223
className="ml-1 self-center"
222224
/>
223225
</Title>
224-
<Row className="items-start justify-around gap-2">
226+
<DepthChart contract={contract} yesBets={yesBets} noBets={noBets} />
227+
<Row className="mt-2 items-start justify-around gap-2">
225228
<OrderTable
226229
limitBets={yesBets}
227230
contract={contract}

web/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
"d3-scale": "4.0.2",
5454
"d3-selection": "3.0.0",
5555
"d3-shape": "3.1.0",
56+
"d3-timer": "3.0.1",
57+
"d3-transition": "3.0.1",
5658
"dayjs": "1.10.7",
5759
"firebase": "9.13.0",
5860
"gridjs": "5.1.0",
@@ -77,7 +79,8 @@
7779
"stability-client": "1.6.1",
7880
"string-similarity": "^4.0.4",
7981
"tippy.js": "6.3.7",
80-
"typewriter-effect": "2.19.0"
82+
"typewriter-effect": "2.19.0",
83+
"victory": "36.6.8"
8184
},
8285
"devDependencies": {
8386
"@tailwindcss/forms": "0.4.0",

0 commit comments

Comments
 (0)