Better tx distribution - 52 weeks down-sampling
This commit is contained in:
parent
2cc32d5764
commit
ac451b34af
98
src/App.tsx
98
src/App.tsx
|
|
@ -21,7 +21,10 @@ import {
|
||||||
getInitialParams,
|
getInitialParams,
|
||||||
getPriceR,
|
getPriceR,
|
||||||
getRandomDeltas,
|
getRandomDeltas,
|
||||||
getSlippage
|
getSlippage,
|
||||||
|
getTxDistribution,
|
||||||
|
getDeltaR_priceGrowth,
|
||||||
|
rv_U
|
||||||
} from "./math";
|
} from "./math";
|
||||||
import { throttle } from "lodash";
|
import { throttle } from "lodash";
|
||||||
// General styles
|
// General styles
|
||||||
|
|
@ -42,6 +45,9 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||||
},
|
},
|
||||||
paddingBottom: theme.spacing(9)
|
paddingBottom: theme.spacing(9)
|
||||||
},
|
},
|
||||||
|
simulationContainer: {
|
||||||
|
minHeight: "442px"
|
||||||
|
},
|
||||||
paper: {
|
paper: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
|
@ -72,6 +78,15 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||||
paddingRight: "5px",
|
paddingRight: "5px",
|
||||||
paddingLeft: "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: {
|
header: {
|
||||||
backgroundColor: "#0b1216",
|
backgroundColor: "#0b1216",
|
||||||
color: "#f8f8f8",
|
color: "#f8f8f8",
|
||||||
|
|
@ -102,7 +117,7 @@ export default function App() {
|
||||||
* to re-render too often
|
* to re-render too often
|
||||||
*/
|
*/
|
||||||
const setCurveParamsThrottle = useMemo(
|
const setCurveParamsThrottle = useMemo(
|
||||||
() => throttle(setCurveParams, 1000),
|
() => throttle(setCurveParams, 250),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -124,11 +139,15 @@ export default function App() {
|
||||||
const [totalReserve, setTotalReserve] = useState(R0);
|
const [totalReserve, setTotalReserve] = useState(R0);
|
||||||
const [withdrawCount, setWithdrawCount] = useState(0);
|
const [withdrawCount, setWithdrawCount] = useState(0);
|
||||||
const [avgSlippage, setAvgSlippage] = useState(0);
|
const [avgSlippage, setAvgSlippage] = useState(0);
|
||||||
const [avgTxSize] = useState(10000);
|
const [avgTxSize, setAvgTxSize] = useState(0);
|
||||||
// Simulation state variables
|
// Simulation state variables
|
||||||
const [simulationActive, setSimulationActive] = useState(false);
|
const [simulationActive, setSimulationActive] = useState(false);
|
||||||
const [simulationRunning, setSimulationRunning] = useState(false);
|
const [simulationRunning, setSimulationRunning] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSimulationActive(false);
|
||||||
|
}, [curveParams]);
|
||||||
|
|
||||||
// #### TEST: Immediate simulation
|
// #### TEST: Immediate simulation
|
||||||
|
|
||||||
async function startSimulation() {
|
async function startSimulation() {
|
||||||
|
|
@ -157,47 +176,52 @@ export default function App() {
|
||||||
const p_t: number[] = [getPriceR({ R: R0, V0, k })];
|
const p_t: number[] = [getPriceR({ R: R0, V0, k })];
|
||||||
const wFee_t: number[] = [0];
|
const wFee_t: number[] = [0];
|
||||||
const slippage_t: number[] = [];
|
const slippage_t: number[] = [];
|
||||||
|
const avgTxSize_t: number[] = [];
|
||||||
|
|
||||||
// Random walk
|
// Random walk
|
||||||
const numSteps = 100;
|
const numSteps = 52;
|
||||||
const updateEveryNth = 1;
|
|
||||||
|
|
||||||
// Compute the random deltas
|
|
||||||
const deltaR_t = getRandomDeltas({ numSteps, avgTxSize });
|
|
||||||
|
|
||||||
|
// numSteps = 52 take 8ms to run
|
||||||
setSimulationRunning(true);
|
setSimulationRunning(true);
|
||||||
for (let i = 0; i < numSteps; i++) {
|
for (let t = 0; t < numSteps; t++) {
|
||||||
const deltaR = deltaR_t[i];
|
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 R = getLast(R_t);
|
||||||
|
const deltaR = getDeltaR_priceGrowth({ R, k, priceGrowth });
|
||||||
|
|
||||||
const R_next = R + deltaR;
|
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);
|
R_t.push(R_next);
|
||||||
p_t.push(getPriceR({ R: R_next, V0, k }));
|
p_t.push(getPriceR({ R: R_next, V0, k }));
|
||||||
slippage_t.push(getSlippage({ R, deltaR, V0, k }));
|
slippage_t.push(slippage);
|
||||||
|
avgTxSize_t.push(_avgTxSize);
|
||||||
// Consider withdraw fees on sales only
|
wFee_t.push(getLast(wFee_t) + wFees);
|
||||||
if (deltaR < 0) {
|
setWithdrawCount(c => c + txsWithdraw.length);
|
||||||
wFee_t.push(getLast(wFee_t) - deltaR * wFee);
|
|
||||||
setWithdrawCount(c => c + 1);
|
|
||||||
} else {
|
|
||||||
wFee_t.push(getLast(wFee_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the simulation if it's no longer active
|
// Stop the simulation if it's no longer active
|
||||||
if (!simulationActive || !canContinueSimulation) break;
|
if (!simulationActive || !canContinueSimulation) break;
|
||||||
|
}
|
||||||
|
|
||||||
if (i % updateEveryNth === 0) {
|
|
||||||
setPriceTimeseries(p_t);
|
setPriceTimeseries(p_t);
|
||||||
setWithdrawFeeTimeseries(wFee_t);
|
setWithdrawFeeTimeseries(wFee_t);
|
||||||
setAvgSlippage(getAvg(slippage_t));
|
setAvgSlippage(getAvg(slippage_t));
|
||||||
setTotalReserve(R_next);
|
setAvgTxSize(getAvg(avgTxSize_t));
|
||||||
|
setTotalReserve(getLast(R_t));
|
||||||
|
|
||||||
// Make this run non-UI blocking
|
// Make this run non-UI blocking
|
||||||
await pause(5);
|
await pause(5);
|
||||||
}
|
|
||||||
}
|
|
||||||
setSimulationRunning(false);
|
setSimulationRunning(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,7 +234,9 @@ export default function App() {
|
||||||
|
|
||||||
const resultFields = [
|
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) + "%"
|
value: +(100 * avgSlippage).toFixed(3) + "%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -219,6 +245,10 @@ export default function App() {
|
||||||
(+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() +
|
(+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() +
|
||||||
" DAI"
|
" DAI"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: `Capital collected from initial hatch`,
|
||||||
|
value: Math.round(d0 * theta).toLocaleString() + " DAI"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: `Total reserve`,
|
label: `Total reserve`,
|
||||||
value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI"
|
value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI"
|
||||||
|
|
@ -300,8 +330,9 @@ export default function App() {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{simulationActive && (
|
<Grid container spacing={3} className={classes.simulationContainer}>
|
||||||
<Grid container spacing={3}>
|
{simulationActive ? (
|
||||||
|
<>
|
||||||
<Grid item xs={12} sm={12} md={6} lg={8}>
|
<Grid item xs={12} sm={12} md={6} lg={8}>
|
||||||
<Paper className={classes.paper}>
|
<Paper className={classes.paper}>
|
||||||
<Box className={classes.boxHeader}>
|
<Box className={classes.boxHeader}>
|
||||||
|
|
@ -327,7 +358,9 @@ export default function App() {
|
||||||
<Box className={classes.boxHeader}>
|
<Box className={classes.boxHeader}>
|
||||||
<Typography variant="h6">Results</Typography>
|
<Typography variant="h6">Results</Typography>
|
||||||
<HelpText
|
<HelpText
|
||||||
text={<span>Explanation of what do this results mean</span>}
|
text={
|
||||||
|
<span>Explanation of what do this results mean</span>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
@ -336,8 +369,19 @@ export default function App() {
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</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>
|
||||||
)}
|
)}
|
||||||
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ function PriceSimulationChart({
|
||||||
|
|
||||||
function ReferenceLabel(props: any) {
|
function ReferenceLabel(props: any) {
|
||||||
const { textAnchor, viewBox, text } = props;
|
const { textAnchor, viewBox, text } = props;
|
||||||
console.log(props);
|
|
||||||
return (
|
return (
|
||||||
<text
|
<text
|
||||||
x={viewBox.x + 10}
|
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;
|
return Math.abs(realizedPrice - spotPrice) / spotPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Price walk utils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a random delta given three components:
|
* Generate a random delta given three components:
|
||||||
* 1. Climbing sin
|
* 1. Climbing sin
|
||||||
|
|
@ -79,3 +81,62 @@ export function getRandomDeltas({
|
||||||
const deltaR_avg = getAvg(deltaR_t);
|
const deltaR_avg = getAvg(deltaR_t);
|
||||||
return deltaR_t.map((deltaR: number) => (avgTxSize * deltaR) / deltaR_avg);
|
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