import Decimal from "decimal.js-light"; import { toDefaultNumber, formatToCeil } from "@utils/FormatNumber"; type numType = string | number; export enum ESide { LONG = 1, SHORT = -1 } export enum DirEnum { UP = 'up', DOWN = 'down' } /** * 参数非空校验 * @example paramIsNil(['a', 'b', 'c']) => {'a': 1, 'b': 2, 'c': undefined} => undefined * */ export function paramIsNil(params: Array, args: { [P: string]: any }) { for (const param of params) { if (args[param] === undefined || args[param] === null || args[param] === "") { return param; } } } /** * 预估强平价格计算 * @param posMargin 仓位保证金 * @param increaseMargin 增加保证金 * @param positionAmt 持仓数量 * @param averagePosPrice 持仓均价 * @param marginRatio 维持保证金率 * @param amtRate 资金费率 * @param takerRate taker手续费 * @param direction 多/空 * */ export interface liquidationPriceProps { posMargin: string | number; increaseMargin: string | number; positionAmt: string | number; averagePosPrice: string | number; marginRatio: string | number; amtRate: string | number; takerRate: string | number; direction: ESide; } export function calcLiquidationPrice({ posMargin, increaseMargin, positionAmt, averagePosPrice, marginRatio, amtRate, takerRate, direction }: liquidationPriceProps) { const nilParamName = paramIsNil( [ "posMargin", "increaseMargin", "positionAmt", "averagePosPrice", "marginRatio", "amtRate", "takerRate", "direction" ], arguments?.[0] ); if (nilParamName) return ""; const _positiveSymbol = direction === ESide.LONG ? 1 : -1; const _oppositeSymbol = _positiveSymbol * -1; const _posMargin = new Decimal(posMargin); const _increaseMargin = new Decimal(increaseMargin); if (!positionAmt) return; const _positionAmt = new Decimal(positionAmt); const _averagePosPrice = new Decimal(averagePosPrice).mul(new Decimal(_oppositeSymbol)); const _marginRatio = new Decimal(marginRatio); const _amtRate = new Decimal(amtRate); const _takerRate = new Decimal(takerRate); const _maxRate = new Decimal(Math.max(_amtRate.mul(new Decimal(_positiveSymbol)).toNumber(), 0)); const _bcs = _marginRatio.add(_maxRate).add(_takerRate).add(new Decimal(_oppositeSymbol)); if (!_bcs.toNumber()) return; return _posMargin.add(_increaseMargin).div(_positionAmt).add(_averagePosPrice).div(_bcs).toNumber(); } /** * 预计盈亏计算 * @param strikePrice 触发价 * @param avgEntryPrice 开仓价格 * @param amt 数量 * @param side 方向 * */ export interface CalcPreProfitProps { strikePrice: number | string; avgEntryPrice: number | string; amt: number | string; side: ESide, dir: DirEnum, } export function calcPreProfit({ strikePrice, avgEntryPrice, amt, side, dir }: CalcPreProfitProps) { if (!toDefaultNumber(strikePrice) || !toDefaultNumber(avgEntryPrice) || !toDefaultNumber(amt)) return `--`; const isLong = side === ESide.LONG ? 1 : -1; const isUp = dir === DirEnum.UP ? 1 : -1; return new Decimal(toDefaultNumber(strikePrice)) .sub(new Decimal(toDefaultNumber(avgEntryPrice))) .mul(toDefaultNumber(isLong)) .mul(toDefaultNumber(isUp)) .mul(new Decimal(toDefaultNumber(amt))) .toNumber(); } /** * 回报率触发价格计算 * @param returnRate 回报率 * @param posMargin 保证金 * @param avgEntryPrice 开仓价格 * */ export interface calcStrikePriceByReturnRateProps{ returnRate: number | string; posMargin: number | string; avgEntryPrice: number | string; side: ESide; dir: DirEnum; } export function calcStrikePriceByReturnRate({ returnRate, posMargin, avgEntryPrice, side, dir }: calcStrikePriceByReturnRateProps){ const isLong = side === ESide.LONG ? 1 : -1; const isUp = dir === DirEnum.UP ? 1 : -1; return new Decimal(toDefaultNumber(returnRate)) .mul(new Decimal(isUp)) .mul(new Decimal(toDefaultNumber(posMargin))) .mul(new Decimal(isLong)) .add(new Decimal(toDefaultNumber(avgEntryPrice))) .toNumber() } /** * 基于成本算数量 * 杠杆拉满计算数量(面值) * 做多: 委托数量 = 可用/委托价格/(taker费率 + 1/杠杆) * 做空: 委托数量 = 可用/max( 买一价,委托价格 )/(taker费率 + 1/ 杠杆 ) */ interface quantityByCostProps { /** 资产 */ balance: numType; /** 价格 */ price: numType; /** taker手续费 */ takerFee: numType; /** 杠杆 */ leverage: numType; } export const getQuantityByCost = ({ balance, price, takerFee, leverage }: quantityByCostProps) => { balance = toDefaultNumber(balance); price = toDefaultNumber(price); takerFee = toDefaultNumber(takerFee); leverage = toDefaultNumber(leverage) || 1; if (!balance || !price) return 0; const quantity = new Decimal(balance) .div(price) .div(new Decimal(takerFee).add(new Decimal(1).div(leverage))) .toNumber(); return quantity; }; /** * 基于面值算成本 * 成本=( 委托价格 * 委托数量 / 杠杆 ) + ( 委托价格 * 委托数量 * taker手续费率 ) */ interface costByQuantityProps { price: numType; quantity: numType; leverage: numType; takerFee: numType; } export const getCostByQuantity = ({ price, quantity, leverage, takerFee }: costByQuantityProps) => { price = toDefaultNumber(price); takerFee = toDefaultNumber(takerFee); leverage = toDefaultNumber(leverage) || 1; quantity = toDefaultNumber(quantity); if (!price || !takerFee || !leverage || !quantity) return 0; const p = new Decimal(price); return p.mul(quantity).div(leverage).add(p.mul(quantity).mul(takerFee)).toNumber(); }; /** * 计算订单价值 value = price * quantity */ interface orderValueProps { price: numType; quantity: numType; scale: numType; } export const getOrderValue = ({ price, quantity, scale }: orderValueProps) => { price = toDefaultNumber(price); quantity = toDefaultNumber(quantity); scale = toDefaultNumber(scale); return new Decimal(price).mul(quantity).toFixed(scale, Decimal.ROUND_DOWN); }; /** * 按比例取数据 * @param rate 比例 - 25/ 50这种 */ interface rateValueProps { /** 比例 */ rate: number; /** 总量 */ max: numType; } export const getRateValue = ({ rate, max }: rateValueProps) => { max = toDefaultNumber(max); rate = toDefaultNumber(rate); return new Decimal(max).mul(rate).div(100).toNumber(); }; /** * 数量(价值) = 张数 * 合约面值 */ interface valueByContrProps { /** 张 */ value: numType; /** 单张面值 */ multiplier: numType; /** 面值精度 */ scale: numType; } export const getValueByContr = ({ value, multiplier, scale }: valueByContrProps) => { value = toDefaultNumber(value); multiplier = toDefaultNumber(multiplier); scale = toDefaultNumber(scale); return new Decimal(value).mul(multiplier).toFixed(scale, Decimal.ROUND_DOWN); }; /** * 张数 = 价值 / 单张面值 * @param value * @param multiplier */ interface ContrByValueProps { value: numType; multiplier: numType; } export const getContrByValue = ({ value, multiplier }: ContrByValueProps) => { value = toDefaultNumber(value); multiplier = toDefaultNumber(multiplier); let contr = 0; if (multiplier && value) { contr = new Decimal(value).div(multiplier).toNumber(); } return new Decimal(contr).toFixed(0, Decimal.ROUND_DOWN); }; /** * 张 / 面值转换 * @param value 原始数量 * @param multiplier 单张面值 * @param scale 精度 * @param getContr 是否获取张,默认false * @returns */ interface ContrOrValueProps { value: numType; multiplier: numType; scale: numType; getContr?: boolean; } export const getContrOrValue = ({ value, multiplier, scale, getContr }: ContrOrValueProps) => { return getContr ? getContrByValue({ value, multiplier }) : getValueByContr({ value, multiplier, scale }); }; /** * 格式化杠杆 * @param leverage * @param scale * @returns */ export const getformatLeverage = (leverage: numType, scale: number) => { const lev = toDefaultNumber(formatToCeil(leverage, scale)); return Math.max(lev, 1); };