Better tx distribution - 52 weeks down-sampling
This commit is contained in:
parent
2cc32d5764
commit
ac451b34af
172
src/App.tsx
172
src/App.tsx
|
|
@ -21,7 +21,10 @@ import {
|
|||
getInitialParams,
|
||||
getPriceR,
|
||||
getRandomDeltas,
|
||||
getSlippage
|
||||
getSlippage,
|
||||
getTxDistribution,
|
||||
getDeltaR_priceGrowth,
|
||||
rv_U
|
||||
} from "./math";
|
||||
import { throttle } from "lodash";
|
||||
// General styles
|
||||
|
|
@ -42,6 +45,9 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||
},
|
||||
paddingBottom: theme.spacing(9)
|
||||
},
|
||||
simulationContainer: {
|
||||
minHeight: "442px"
|
||||
},
|
||||
paper: {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
|
|
@ -72,6 +78,15 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||
paddingRight: "5px",
|
||||
paddingLeft: "5px"
|
||||
},
|
||||
boxPlaceholder: {
|
||||
padding: theme.spacing(3, 3),
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: theme.palette.text.secondary,
|
||||
opacity: 0.4
|
||||
},
|
||||
header: {
|
||||
backgroundColor: "#0b1216",
|
||||
color: "#f8f8f8",
|
||||
|
|
@ -102,7 +117,7 @@ export default function App() {
|
|||
* to re-render too often
|
||||
*/
|
||||
const setCurveParamsThrottle = useMemo(
|
||||
() => throttle(setCurveParams, 1000),
|
||||
() => throttle(setCurveParams, 250),
|
||||
[]
|
||||
);
|
||||
|
||||
|
|
@ -124,11 +139,15 @@ export default function App() {
|
|||
const [totalReserve, setTotalReserve] = useState(R0);
|
||||
const [withdrawCount, setWithdrawCount] = useState(0);
|
||||
const [avgSlippage, setAvgSlippage] = useState(0);
|
||||
const [avgTxSize] = useState(10000);
|
||||
const [avgTxSize, setAvgTxSize] = useState(0);
|
||||
// Simulation state variables
|
||||
const [simulationActive, setSimulationActive] = useState(false);
|
||||
const [simulationRunning, setSimulationRunning] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setSimulationActive(false);
|
||||
}, [curveParams]);
|
||||
|
||||
// #### TEST: Immediate simulation
|
||||
|
||||
async function startSimulation() {
|
||||
|
|
@ -157,47 +176,52 @@ export default function App() {
|
|||
const p_t: number[] = [getPriceR({ R: R0, V0, k })];
|
||||
const wFee_t: number[] = [0];
|
||||
const slippage_t: number[] = [];
|
||||
const avgTxSize_t: number[] = [];
|
||||
|
||||
// Random walk
|
||||
const numSteps = 100;
|
||||
const updateEveryNth = 1;
|
||||
|
||||
// Compute the random deltas
|
||||
const deltaR_t = getRandomDeltas({ numSteps, avgTxSize });
|
||||
const numSteps = 52;
|
||||
|
||||
// numSteps = 52 take 8ms to run
|
||||
setSimulationRunning(true);
|
||||
for (let i = 0; i < numSteps; i++) {
|
||||
const deltaR = deltaR_t[i];
|
||||
for (let t = 0; t < numSteps; t++) {
|
||||
const txsWeek = Math.ceil(t < 5 ? rv_U(0, 5) : rv_U(5, 2 * t));
|
||||
const priceGrowth = rv_U(0.99, 1.03);
|
||||
|
||||
const R = getLast(R_t);
|
||||
const deltaR = getDeltaR_priceGrowth({ R, k, priceGrowth });
|
||||
|
||||
const R_next = R + deltaR;
|
||||
|
||||
const txs = getTxDistribution({ sum: deltaR, num: txsWeek });
|
||||
// Compute slippage
|
||||
const slippage = getAvg(
|
||||
txs.map(txR => getSlippage({ R, deltaR: txR, V0, k }))
|
||||
);
|
||||
const txsWithdraw = txs.filter(tx => tx < 0);
|
||||
const wFees = -wFee * txsWithdraw.reduce((t, c) => t + c, 0);
|
||||
const _avgTxSize =
|
||||
txs.reduce((t, c) => t + Math.abs(c), 0) / txs.length;
|
||||
|
||||
R_t.push(R_next);
|
||||
p_t.push(getPriceR({ R: R_next, V0, k }));
|
||||
slippage_t.push(getSlippage({ R, deltaR, V0, k }));
|
||||
|
||||
// Consider withdraw fees on sales only
|
||||
if (deltaR < 0) {
|
||||
wFee_t.push(getLast(wFee_t) - deltaR * wFee);
|
||||
setWithdrawCount(c => c + 1);
|
||||
} else {
|
||||
wFee_t.push(getLast(wFee_t));
|
||||
}
|
||||
slippage_t.push(slippage);
|
||||
avgTxSize_t.push(_avgTxSize);
|
||||
wFee_t.push(getLast(wFee_t) + wFees);
|
||||
setWithdrawCount(c => c + txsWithdraw.length);
|
||||
|
||||
// Stop the simulation if it's no longer active
|
||||
if (!simulationActive || !canContinueSimulation) break;
|
||||
|
||||
if (i % updateEveryNth === 0) {
|
||||
setPriceTimeseries(p_t);
|
||||
setWithdrawFeeTimeseries(wFee_t);
|
||||
setAvgSlippage(getAvg(slippage_t));
|
||||
setTotalReserve(R_next);
|
||||
|
||||
// Make this run non-UI blocking
|
||||
await pause(5);
|
||||
}
|
||||
}
|
||||
|
||||
setPriceTimeseries(p_t);
|
||||
setWithdrawFeeTimeseries(wFee_t);
|
||||
setAvgSlippage(getAvg(slippage_t));
|
||||
setAvgTxSize(getAvg(avgTxSize_t));
|
||||
setTotalReserve(getLast(R_t));
|
||||
|
||||
// Make this run non-UI blocking
|
||||
await pause(5);
|
||||
|
||||
setSimulationRunning(false);
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +234,9 @@ export default function App() {
|
|||
|
||||
const resultFields = [
|
||||
{
|
||||
label: `Average slippage (avg tx size ${avgTxSize} DAI)`,
|
||||
label: `Average slippage (avg tx size ${Math.round(
|
||||
avgTxSize
|
||||
).toLocaleString()} DAI)`,
|
||||
value: +(100 * avgSlippage).toFixed(3) + "%"
|
||||
},
|
||||
{
|
||||
|
|
@ -219,6 +245,10 @@ export default function App() {
|
|||
(+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() +
|
||||
" DAI"
|
||||
},
|
||||
{
|
||||
label: `Capital collected from initial hatch`,
|
||||
value: Math.round(d0 * theta).toLocaleString() + " DAI"
|
||||
},
|
||||
{
|
||||
label: `Total reserve`,
|
||||
value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI"
|
||||
|
|
@ -300,44 +330,58 @@ export default function App() {
|
|||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{simulationActive && (
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} sm={12} md={6} lg={8}>
|
||||
<Paper className={classes.paper}>
|
||||
<Box className={classes.boxHeader}>
|
||||
<Typography variant="h6">Simulation</Typography>
|
||||
<HelpText
|
||||
text={<span>Some context about this simulation</span>}
|
||||
/>
|
||||
</Box>
|
||||
<Grid container spacing={3} className={classes.simulationContainer}>
|
||||
{simulationActive ? (
|
||||
<>
|
||||
<Grid item xs={12} sm={12} md={6} lg={8}>
|
||||
<Paper className={classes.paper}>
|
||||
<Box className={classes.boxHeader}>
|
||||
<Typography variant="h6">Simulation</Typography>
|
||||
<HelpText
|
||||
text={<span>Some context about this simulation</span>}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box className={classes.boxChart}>
|
||||
<PriceSimulationChart
|
||||
priceTimeseries={priceTimeseries}
|
||||
withdrawFeeTimeseries={withdrawFeeTimeseries}
|
||||
p0={p0}
|
||||
p1={p1}
|
||||
/>
|
||||
<Box className={classes.boxChart}>
|
||||
<PriceSimulationChart
|
||||
priceTimeseries={priceTimeseries}
|
||||
withdrawFeeTimeseries={withdrawFeeTimeseries}
|
||||
p0={p0}
|
||||
p1={p1}
|
||||
/>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={12} md={6} lg={4}>
|
||||
<Paper className={classes.paper}>
|
||||
<Box className={classes.boxHeader}>
|
||||
<Typography variant="h6">Results</Typography>
|
||||
<HelpText
|
||||
text={
|
||||
<span>Explanation of what do this results mean</span>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box className={classes.box}>
|
||||
<ResultParams resultFields={resultFields} />
|
||||
</Box>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</>
|
||||
) : (
|
||||
<Grid item xs={12}>
|
||||
<Paper className={classes.paper}>
|
||||
<Box className={classes.boxPlaceholder}>
|
||||
<Typography variant="h6">
|
||||
Run a simulation to see results
|
||||
</Typography>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={12} md={6} lg={4}>
|
||||
<Paper className={classes.paper}>
|
||||
<Box className={classes.boxHeader}>
|
||||
<Typography variant="h6">Results</Typography>
|
||||
<HelpText
|
||||
text={<span>Explanation of what do this results mean</span>}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box className={classes.box}>
|
||||
<ResultParams resultFields={resultFields} />
|
||||
</Box>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
)}
|
||||
</Grid>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ function PriceSimulationChart({
|
|||
|
||||
function ReferenceLabel(props: any) {
|
||||
const { textAnchor, viewBox, text } = props;
|
||||
console.log(props);
|
||||
return (
|
||||
<text
|
||||
x={viewBox.x + 10}
|
||||
|
|
|
|||
61
src/math.ts
61
src/math.ts
|
|
@ -53,6 +53,8 @@ export function getSlippage({
|
|||
return Math.abs(realizedPrice - spotPrice) / spotPrice;
|
||||
}
|
||||
|
||||
// Price walk utils
|
||||
|
||||
/**
|
||||
* Generate a random delta given three components:
|
||||
* 1. Climbing sin
|
||||
|
|
@ -79,3 +81,62 @@ export function getRandomDeltas({
|
|||
const deltaR_avg = getAvg(deltaR_t);
|
||||
return deltaR_t.map((deltaR: number) => (avgTxSize * deltaR) / deltaR_avg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get deltaR for a given price growth factor
|
||||
*/
|
||||
export function getDeltaR_priceGrowth({
|
||||
R,
|
||||
k,
|
||||
priceGrowth
|
||||
}: {
|
||||
R: number;
|
||||
k: number;
|
||||
priceGrowth: number;
|
||||
}) {
|
||||
return -R + (priceGrowth * R ** (1 - 1 / k)) ** (k / (-1 + k));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a tx distribution using a normal distribution,
|
||||
* Given a sum of tx value and a number of transactions
|
||||
*
|
||||
* Demo: https://codepen.io/anon/pen/mNqJjv?editors=0010#0
|
||||
* Very quick: < 10ms for 10000 txs
|
||||
*/
|
||||
export function getTxDistribution({ sum, num }: { sum: number; num: number }) {
|
||||
const mean = sum / num;
|
||||
const off = mean * 4;
|
||||
const x: number[] = [];
|
||||
for (let i = 0; i < num; i++) {
|
||||
x[i] = randn_bm(mean - off, mean + off);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
// Minor utils
|
||||
|
||||
/**
|
||||
* Random variable uniformly distributed
|
||||
*/
|
||||
export function rv_U(min: number, max: number) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Normal variate using Box-Muller transform.
|
||||
* by https://stackoverflow.com/questions/25582882/javascript-math-random-normal-distribution-gaussian-bell-curve/36481059#36481059
|
||||
*/
|
||||
function randn_bm(min: number, max: number) {
|
||||
var u = 0,
|
||||
v = 0;
|
||||
while (u === 0) u = Math.random(); //Converting [0,1) to (0,1)
|
||||
while (v === 0) v = Math.random();
|
||||
let num = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
|
||||
|
||||
num = num / 10.0 + 0.5; // Translate to 0 -> 1
|
||||
if (num > 1 || num < 0) num = randn_bm(min, max); // resample between 0 and 1 if out of range
|
||||
num *= max - min; // Stretch to fill range
|
||||
num += min; // offset to min
|
||||
return num;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue