feat: enhance quantity and price rounding functions for improved precision in trading calculations

This commit is contained in:
discountry
2025-11-06 21:06:03 +08:00
parent 0f70c6b6aa
commit d0d1afa0ea
5 changed files with 92 additions and 18 deletions
+42 -9
View File
@@ -1,21 +1,54 @@
const MAX_SCALE_DECIMALS = 12;
const STEP_TOLERANCE = 1e-9;
function resolveStepScale(step: number): { scale: number; stepInt: number; decimals: number } {
if (!Number.isFinite(step) || step <= 0) {
return { scale: 1, stepInt: 1, decimals: 0 };
}
let decimals = Math.min(MAX_SCALE_DECIMALS, Math.max(0, decimalsOf(step)));
let scale = Math.pow(10, decimals);
let scaledStep = step * scale;
while (decimals < MAX_SCALE_DECIMALS && Math.abs(Math.round(scaledStep) - scaledStep) > STEP_TOLERANCE) {
decimals += 1;
scale *= 10;
scaledStep = step * scale;
}
const stepInt = Math.max(1, Math.round(scaledStep));
return { scale, stepInt, decimals };
}
export function roundDownToTick(value: number, tick: number): number {
if (!Number.isFinite(value) || !Number.isFinite(tick) || tick <= 0) return value;
const scaled = Math.floor(value / tick) * tick;
// Avoid floating residuals
return Number(scaled.toFixed(Math.max(0, decimalsOf(tick))));
const sign = value < 0 ? -1 : 1;
const absValue = Math.abs(value);
const { scale, stepInt, decimals } = resolveStepScale(tick);
const scaledValue = Math.floor(absValue * scale + STEP_TOLERANCE);
const resultInt = Math.floor(scaledValue / stepInt) * stepInt;
const rounded = resultInt / scale;
return sign * Number(rounded.toFixed(decimals));
}
export function roundQtyDownToStep(value: number, step: number): number {
if (!Number.isFinite(value) || !Number.isFinite(step) || step <= 0) return value;
const scaled = Math.floor(value / step) * step;
return Number(scaled.toFixed(Math.max(0, decimalsOf(step))));
const sign = value < 0 ? -1 : 1;
const absValue = Math.abs(value);
const { scale, stepInt, decimals } = resolveStepScale(step);
const scaledValue = Math.floor(absValue * scale + STEP_TOLERANCE);
const resultInt = Math.floor(scaledValue / stepInt) * stepInt;
const rounded = resultInt / scale;
return sign * Number(rounded.toFixed(decimals));
}
export function decimalsOf(step: number): number {
const s = step.toString();
if (!s.includes(".")) return 0;
const fraction = s.split(".")[1];
return fraction ? fraction.length : 0;
if (!Number.isFinite(step)) return 0;
if (Number.isInteger(step)) return 0;
let decimals = 0;
let scaled = step;
while (decimals < MAX_SCALE_DECIMALS && Math.abs(Math.round(scaled) - scaled) > STEP_TOLERANCE) {
scaled *= 10;
decimals += 1;
}
return decimals;
}
export function isNearlyZero(value: number, epsilon = 1e-5): boolean {