pinpet-sdk 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,323 @@
1
+
2
+ const CurveAMM = require('../../utils/curve_amm');
3
+ const { absoluteValue } = require('./utils');
4
+
5
+
6
+
7
+ /**
8
+ * Simulate sell transaction analysis
9
+ * @param {string} mint - Token address
10
+ * @param {bigint|string|number} sellTokenAmount - Token amount to sell (u64 format, precision 10^6)
11
+ * @returns {Promise<Object>} Sell analysis result
12
+ */
13
+ async function simulateSell(mint, sellTokenAmount) {
14
+ try {
15
+ // 1. Parameter validation
16
+ if (!mint || typeof mint !== 'string') {
17
+ return {
18
+ success: false,
19
+ errorCode: 'PARAM_ERROR',
20
+ errorMessage: 'Token address must be a valid string',
21
+ data: null
22
+ };
23
+ }
24
+
25
+ if (sellTokenAmount === undefined || sellTokenAmount === null) {
26
+ return {
27
+ success: false,
28
+ errorCode: 'PARAM_ERROR',
29
+ errorMessage: 'Sell token amount cannot be empty',
30
+ data: null
31
+ };
32
+ }
33
+
34
+ // Convert to bigint
35
+ let sellTokenAmountU64;
36
+ try {
37
+ sellTokenAmountU64 = typeof sellTokenAmount === 'bigint' ? sellTokenAmount : BigInt(sellTokenAmount);
38
+ if (sellTokenAmountU64 <= 0n) {
39
+ throw new Error('Amount must be greater than 0');
40
+ }
41
+ } catch (error) {
42
+ return {
43
+ success: false,
44
+ errorCode: 'PARAM_ERROR',
45
+ errorMessage: `Invalid token amount: ${error.message}`,
46
+ data: null
47
+ };
48
+ }
49
+
50
+
51
+ let currentPriceU64;
52
+ try {
53
+ const priceString = await this.sdk.data.price(mint);
54
+ currentPriceU64 = BigInt(priceString);
55
+ } catch (error) {
56
+ return {
57
+ success: false,
58
+ errorCode: 'API_ERROR',
59
+ errorMessage: `获取价格信息失败: ${error.message} / Failed to get price info: ${error.message}`,
60
+ data: null
61
+ };
62
+ }
63
+
64
+
65
+
66
+ // 3. 获取做多订单列表 / Get long order list (down_orders)
67
+ let ordersData;
68
+ try {
69
+ ordersData = await this.sdk.data.orders(mint, {
70
+ type: 'down_orders',
71
+ limit: 500
72
+ });
73
+ if (!ordersData.success) {
74
+ return {
75
+ success: false,
76
+ errorCode: 'API_ERROR',
77
+ errorMessage: '无法获取做多订单数据 / Cannot get long order data',
78
+ data: null
79
+ };
80
+ }
81
+ } catch (error) {
82
+ return {
83
+ success: false,
84
+ errorCode: 'API_ERROR',
85
+ errorMessage: `获取订单数据失败: ${error.message} / Failed to get order data: ${error.message}`,
86
+ data: null
87
+ };
88
+ }
89
+
90
+ // 4. 转换订单数据格式 / Convert order data format
91
+ const longOrderList = ordersData.data.orders.map(order => ({
92
+ ...order,
93
+ lockLpStartPrice: order.lock_lp_start_price,
94
+ lockLpEndPrice: order.lock_lp_end_price,
95
+ lockLpSolAmount: BigInt(order.lock_lp_sol_amount),
96
+ lockLpTokenAmount: BigInt(order.lock_lp_token_amount)
97
+ }));
98
+
99
+ // 如果没有订单,添加 null 表示无限制
100
+ if (longOrderList.length === 0) {
101
+ longOrderList.push(null);
102
+ }
103
+
104
+ // 5. 开始 AMM 计算 / Start AMM calculation
105
+ // 计算理想情况下的基准SOL数量(完全无滑点)
106
+ const idealTradeResult = CurveAMM.sellFromPriceWithTokenInput(currentPriceU64, sellTokenAmountU64);
107
+ let idealSolAmount = 0n;
108
+ if (idealTradeResult) {
109
+ idealSolAmount = idealTradeResult[1];
110
+ }
111
+ const idealTokenAmount = sellTokenAmountU64;
112
+
113
+ // 初始化价格区间和流动性相关变量
114
+ let totalPriceSpan = 0n;
115
+ let totalLiquiditySolAmount = 0n;
116
+ let totalLiquidityTokenAmount = 0n;
117
+ let targetReachedAtSegmentIndex = -1;
118
+ let minAllowedPrice = 0n;
119
+
120
+ // 构建价格区间分析列表
121
+ const priceSegmentAnalysisList = new Array(longOrderList.length);
122
+
123
+ // 遍历订单列表并计算每个价格区间的参数
124
+ for (let segmentIndex = 0; segmentIndex < longOrderList.length; segmentIndex++) {
125
+ let segmentStartPrice, segmentEndPrice;
126
+
127
+ // 根据区间位置确定起始和结束价格
128
+ if (segmentIndex === 0) {
129
+ // 第一个区间:从当前价格开始(向下卖出)
130
+ segmentStartPrice = currentPriceU64;
131
+
132
+ if (longOrderList[0] === null) {
133
+ // 如果第一个订单就是null,表示没有任何订单
134
+ segmentEndPrice = CurveAMM.MIN_U128_PRICE; // 最低价格
135
+ minAllowedPrice = CurveAMM.MIN_U128_PRICE; // 无限制到最低价格
136
+ } else {
137
+ // 到第一个订单结束价格的下一个单位
138
+ segmentEndPrice = BigInt(longOrderList[0].lockLpStartPrice);
139
+ minAllowedPrice = BigInt(longOrderList[0].lockLpStartPrice);
140
+ }
141
+ } else if (longOrderList[segmentIndex] === null) {
142
+ // 当前遍历到null(链表结束)
143
+ segmentStartPrice = BigInt(longOrderList[segmentIndex - 1].lockLpEndPrice);
144
+ segmentEndPrice = CurveAMM.MIN_U128_PRICE; // 到最低价格
145
+ } else {
146
+ // 普通情况:位于两个订单之间的空隙
147
+ segmentStartPrice = BigInt(longOrderList[segmentIndex - 1].lockLpEndPrice);
148
+ segmentEndPrice = BigInt(longOrderList[segmentIndex].lockLpStartPrice);
149
+ }
150
+
151
+ // 验证价格区间的有效性
152
+ if (segmentStartPrice < segmentEndPrice) {
153
+ // 价格区间无效,跳过(卖出时起始价格应该高于结束价格)
154
+ priceSegmentAnalysisList[segmentIndex] = {
155
+ startPrice: segmentStartPrice,
156
+ endPrice: segmentEndPrice,
157
+ obtainedSolAmount: null,
158
+ consumedTokenAmount: null,
159
+ isValid: false
160
+ };
161
+ continue;
162
+ }
163
+
164
+ if (segmentStartPrice === segmentEndPrice) {
165
+ priceSegmentAnalysisList[segmentIndex] = {
166
+ startPrice: segmentStartPrice,
167
+ endPrice: segmentEndPrice,
168
+ obtainedSolAmount: 0n,
169
+ consumedTokenAmount: 0n,
170
+ isValid: true
171
+ };
172
+ continue;
173
+ }
174
+
175
+ // 使用AMM计算该区间的交易参数(卖出:从高价格到低价格)
176
+ const segmentTradeResult = CurveAMM.sellFromPriceToPrice(segmentStartPrice, segmentEndPrice);
177
+
178
+ if (!segmentTradeResult) {
179
+ // AMM计算失败
180
+ priceSegmentAnalysisList[segmentIndex] = {
181
+ startPrice: segmentStartPrice,
182
+ endPrice: segmentEndPrice,
183
+ obtainedSolAmount: null,
184
+ consumedTokenAmount: null,
185
+ isValid: false
186
+ };
187
+ } else {
188
+ // 计算成功,保存结果
189
+ const [consumedTokenAmount, obtainedSolAmount] = segmentTradeResult;
190
+ priceSegmentAnalysisList[segmentIndex] = {
191
+ startPrice: segmentStartPrice,
192
+ endPrice: segmentEndPrice,
193
+ obtainedSolAmount,
194
+ consumedTokenAmount,
195
+ isValid: true
196
+ };
197
+ }
198
+ }
199
+
200
+ // 累计计算总流动性深度
201
+ for (let i = 0; i < priceSegmentAnalysisList.length; i++) {
202
+ const segment = priceSegmentAnalysisList[i];
203
+
204
+ if (segment.isValid && segment.obtainedSolAmount !== null && segment.consumedTokenAmount !== null) {
205
+ totalLiquiditySolAmount += BigInt(segment.obtainedSolAmount);
206
+ totalLiquidityTokenAmount += BigInt(segment.consumedTokenAmount);
207
+
208
+ // Token输入:检查累计的Token数量是否已经达到目标
209
+ if (totalLiquidityTokenAmount >= sellTokenAmountU64 && targetReachedAtSegmentIndex === -1) {
210
+ targetReachedAtSegmentIndex = i;
211
+ }
212
+ }
213
+ }
214
+
215
+ // 计算实际交易参数
216
+ let actualObtainedSolAmount = 0n;
217
+ let actualConsumedTokenAmount = 0n;
218
+ let transactionCompletionRate = 0.0;
219
+
220
+ if (targetReachedAtSegmentIndex !== -1) {
221
+ // 可以100%完成交易
222
+ transactionCompletionRate = 100.0;
223
+
224
+ for (let i = 0; i <= targetReachedAtSegmentIndex; i++) {
225
+ const currentSegment = priceSegmentAnalysisList[i];
226
+
227
+ if (i === targetReachedAtSegmentIndex) {
228
+ // 最后一个区间:可能只需要部分交易
229
+ // Token输入:计算剩余需要卖出的Token
230
+ const remainingTokenToSell = sellTokenAmountU64 - actualConsumedTokenAmount;
231
+ const partialTradeResult = CurveAMM.sellFromPriceWithTokenInput(
232
+ currentSegment.startPrice,
233
+ remainingTokenToSell
234
+ );
235
+
236
+ if (partialTradeResult) {
237
+ const [finalPrice, obtainedSolForPartial] = partialTradeResult;
238
+ actualObtainedSolAmount += obtainedSolForPartial;
239
+ actualConsumedTokenAmount += remainingTokenToSell;
240
+ totalPriceSpan += absoluteValue(currentSegment.startPrice - finalPrice) + 1n;
241
+ }
242
+ } else {
243
+ // 完整使用该区间
244
+ actualObtainedSolAmount += currentSegment.obtainedSolAmount;
245
+ actualConsumedTokenAmount += currentSegment.consumedTokenAmount;
246
+ totalPriceSpan += absoluteValue(currentSegment.startPrice - currentSegment.endPrice) + 1n;
247
+ }
248
+ }
249
+ } else {
250
+ // 无法完全完成交易,使用所有可用流动性
251
+ for (let i = 0; i < priceSegmentAnalysisList.length; i++) {
252
+ const segment = priceSegmentAnalysisList[i];
253
+ if (segment.isValid) {
254
+ actualObtainedSolAmount += segment.obtainedSolAmount;
255
+ actualConsumedTokenAmount += segment.consumedTokenAmount;
256
+ totalPriceSpan += absoluteValue(segment.startPrice - segment.endPrice) + 1n;
257
+ }
258
+ }
259
+
260
+ // 计算交易完成率
261
+ if (sellTokenAmountU64 > 0n) {
262
+ transactionCompletionRate = parseFloat(
263
+ CurveAMM.u64ToTokenDecimal(actualConsumedTokenAmount)
264
+ .div(CurveAMM.u64ToTokenDecimal(sellTokenAmountU64))
265
+ .mul(100)
266
+ .toFixed(2)
267
+ );
268
+ }
269
+
270
+ // 重新计算理论参数(基于实际可达到的数量)
271
+ const theoreticalTradeResult = CurveAMM.sellFromPriceWithTokenInput(currentPriceU64, actualConsumedTokenAmount);
272
+ if (theoreticalTradeResult) {
273
+ const [, theoreticalSolObtained] = theoreticalTradeResult;
274
+ // 更新理论SOL数量
275
+ }
276
+ }
277
+
278
+ // 计算最小滑点百分比
279
+ const minimumSlippagePercentage = Math.abs(
280
+ 100.0 * (
281
+ CurveAMM.u64ToSolDecimal(idealSolAmount)
282
+ .minus(CurveAMM.u64ToSolDecimal(actualObtainedSolAmount))
283
+ .div(CurveAMM.u64ToSolDecimal(idealSolAmount))
284
+ .toNumber()
285
+ )
286
+ );
287
+
288
+ // 6. 返回分析结果 / Return analysis result
289
+ return {
290
+ success: true,
291
+ errorCode: null,
292
+ errorMessage: null,
293
+ data: {
294
+ inputType: 'token',
295
+ inputAmount: sellTokenAmountU64,
296
+ minAllowedPrice: minAllowedPrice,
297
+ totalPriceSpan: totalPriceSpan,
298
+ transactionCompletionRate: transactionCompletionRate,
299
+ idealSolAmount: idealSolAmount,
300
+ idealTokenAmount: idealTokenAmount,
301
+ actualObtainedSolAmount: actualObtainedSolAmount,
302
+ actualConsumedTokenAmount: actualConsumedTokenAmount,
303
+ theoreticalSolAmount: idealSolAmount, // 理论目标SOL数量
304
+ minimumSlippagePercentage: minimumSlippagePercentage,
305
+ totalLiquiditySolAmount: totalLiquiditySolAmount,
306
+ totalLiquidityTokenAmount: totalLiquidityTokenAmount
307
+ }
308
+ };
309
+
310
+ } catch (error) {
311
+ return {
312
+ success: false,
313
+ errorCode: 'DATA_ERROR',
314
+ errorMessage: `计算过程中发生错误: ${error.message} / Error during calculation: ${error.message}`,
315
+ data: null
316
+ };
317
+ }
318
+ }
319
+
320
+
321
+ module.exports = {
322
+ simulateSell
323
+ };
@@ -0,0 +1,223 @@
1
+
2
+
3
+ // Liquidity reservation ratio - how much liquidity to reserve relative to the last locked liquidity
4
+ const LIQUIDITY_RESERVATION = 100; // 100%
5
+
6
+ /**
7
+ * Transform orders data format
8
+ * @param {Object} ordersData - Raw orders data
9
+ * @returns {Array} Transformed orders array
10
+ */
11
+ function transformOrdersData(ordersData) {
12
+ if (!ordersData || !ordersData.success || !ordersData.data || !ordersData.data.orders) {
13
+ throw new Error('Invalid orders data format');
14
+ }
15
+
16
+ return ordersData.data.orders.map(order => ({
17
+ order_type: order.order_type,
18
+ lock_lp_start_price: BigInt(order.lock_lp_start_price),
19
+ lock_lp_end_price: BigInt(order.lock_lp_end_price),
20
+ lock_lp_sol_amount: order.lock_lp_sol_amount,
21
+ lock_lp_token_amount: order.lock_lp_token_amount,
22
+ order_pda: order.order_pda
23
+ }));
24
+ }
25
+
26
+ /**
27
+ * @typedef {Object} Order
28
+ * @property {number} order_type - Order type (e.g., 1 for down_orders, 2 for up_orders).
29
+ * @property {bigint} lock_lp_start_price - Locked liquidity start price.
30
+ * @property {bigint} lock_lp_end_price - Locked liquidity end price.
31
+ * @property {number} lock_lp_sol_amount - 锁定的SOL数量。
32
+ * @property {number} lock_lp_token_amount - 锁定的代币数量。
33
+ * @property {string} order_pda - 订单的PDA地址。
34
+ */
35
+
36
+ /**
37
+ * @typedef {Object} OverlapResult
38
+ * @property {boolean} no_overlap - 是否没有重叠。`true` 表示没有重叠(可以安全插入),`false` 表示有重叠。
39
+ * @property {string|null} prev_order_pda - 如果没有重叠,表示新区间逻辑上的前一个订单PDA。如果新区间是第一个,则为 `null`。
40
+ * @property {string|null} next_order_pda - 如果没有重叠,表示新区间逻辑上的后一个订单PDA。如果新区间是最后一个,则为 `null`。
41
+ * @property {string} overlap_reason - 重叠原因说明。当没有重叠时为空字符串,有重叠时说明具体原因。
42
+ */
43
+
44
+ /**
45
+ * 检查给定价格区间是否与已排序的订单列表中的任何区间发生重叠。
46
+ * 此函数利用列表已排序的特性,通过优化的查找算法实现高性能。
47
+ *
48
+ * @param {'down_orders' | 'up_orders'} order_type - 订单类型。'down_orders' 表示价格从高到低排序,'up_orders' 表示价格从低到高排序。
49
+ * @param {Order[]} order_list - 已排序的订单对象数组。
50
+ * @param {bigint | number | string} lp_start_price - 需要检查的区间的起始价格。
51
+ * @param {bigint | number | string} lp_end_price - 需要检查的区间的结束价格。
52
+ * @returns {OverlapResult} - 返回一个包含重叠检查结果和相邻订单PDA的对象。
53
+ *
54
+ * @example
55
+ * // down_orders 示例 (价格从高到低)
56
+ * const downOrders = [
57
+ * { lock_lp_start_price: 100n, lock_lp_end_price: 90n, order_pda: 'pda1' },
58
+ * { lock_lp_start_price: 80n, lock_lp_end_price: 70n, order_pda: 'pda2' }
59
+ * ];
60
+ * // 无重叠,插入中间。逻辑上前一个是pda2(价格更低),后一个是pda1(价格更高)
61
+ * checkPriceRangeOverlap('down_orders', downOrders, 85n, 82n);
62
+ * // 返回: { no_overlap: true, prev_order_pda: 'pda2', next_order_pda: 'pda1' }
63
+ *
64
+ * // 有重叠
65
+ * checkPriceRangeOverlap('down_orders', downOrders, 95n, 85n);
66
+ * // 返回: { no_overlap: false, prev_order_pda: null, next_order_pda: null }
67
+ *
68
+ * @example
69
+ * // up_orders 示例 (价格从低到高)
70
+ * const upOrders = [
71
+ * { lock_lp_start_price: 70n, lock_lp_end_price: 80n, order_pda: 'pda2' },
72
+ * { lock_lp_start_price: 90n, lock_lp_end_price: 100n, order_pda: 'pda1' }
73
+ * ];
74
+ * // 无重叠,插入末尾
75
+ * checkPriceRangeOverlap('up_orders', upOrders, 110n, 120n);
76
+ * // 返回: { no_overlap: true, prev_order_pda: 'pda1', next_order_pda: null }
77
+ */
78
+ function checkPriceRangeOverlap(order_type, order_list, lp_start_price, lp_end_price) {
79
+ const startPrice = BigInt(lp_start_price);
80
+ const endPrice = BigInt(lp_end_price);
81
+
82
+ if (order_list.length === 0) {
83
+ return { no_overlap: true, prev_order_pda: null, next_order_pda: null, overlap_reason: "" };
84
+ }
85
+
86
+ const isDown = order_type === 'down_orders';
87
+
88
+ // 验证并规范化输入价格区间,确保 minPrice <= maxPrice
89
+ if ((isDown && startPrice < endPrice) || (!isDown && startPrice > endPrice)) {
90
+ throw new Error('输入的起始和结束价格与订单类型规则不匹配。');
91
+ }
92
+ const minPrice = isDown ? endPrice : startPrice;
93
+ const maxPrice = isDown ? startPrice : endPrice;
94
+
95
+ let low = 0;
96
+ let high = order_list.length - 1;
97
+ let insertionIndex = order_list.length; // 默认插入到最后
98
+
99
+ while (low <= high) {
100
+ const mid = Math.floor((low + high) / 2);
101
+ const order = order_list[mid];
102
+ const orderStart = BigInt(order.lock_lp_start_price);
103
+ const orderEnd = BigInt(order.lock_lp_end_price);
104
+
105
+ const orderMin = isDown ? orderEnd : orderStart;
106
+ const orderMax = isDown ? orderStart : orderEnd;
107
+
108
+ // 核心重叠判断: (StartA < EndB) and (EndA > StartB)
109
+ if (minPrice < orderMax && maxPrice > orderMin) {
110
+ // 发生基础重叠,根据当前位置确定正确的插入位置和PDA
111
+ let overlapInsertionIndex;
112
+ if (isDown) {
113
+ overlapInsertionIndex = maxPrice > orderMax ? mid : mid + 1;
114
+ } else {
115
+ overlapInsertionIndex = minPrice < orderMin ? mid : mid + 1;
116
+ }
117
+
118
+ const nextOrder = order_list[overlapInsertionIndex] || null;
119
+ const prevOrder = order_list[overlapInsertionIndex - 1] || null;
120
+
121
+ return {
122
+ no_overlap: false,
123
+ prev_order_pda: prevOrder ? prevOrder.order_pda : null,
124
+ next_order_pda: nextOrder ? nextOrder.order_pda : null,
125
+ overlap_reason: "Overlaps with existing order range"
126
+ };
127
+ }
128
+
129
+ if (isDown) {
130
+ // down_orders: 价格从大到小 (orderMax 递减)
131
+ if (maxPrice > orderMax) { // 新区间在当前区间的“左边”(价格更高)
132
+ insertionIndex = mid;
133
+ high = mid - 1;
134
+ } else {
135
+ low = mid + 1;
136
+ }
137
+ } else {
138
+ // up_orders: 价格从小到大 (orderMin 递增)
139
+ if (minPrice < orderMin) { // 新区间在当前区间的“左边”(价格更低)
140
+ insertionIndex = mid;
141
+ high = mid - 1;
142
+ } else {
143
+ low = mid + 1;
144
+ }
145
+ }
146
+ }
147
+
148
+ // 根据找到的插入点,确定逻辑上的前后 PDA
149
+ // insertionIndex 是新区间应该插入的位置,使得列表依然有序
150
+ const nextOrder = order_list[insertionIndex] || null;
151
+ const prevOrder = order_list[insertionIndex - 1] || null;
152
+
153
+ // 检查流动性预留重叠
154
+ function checkLiquidityReservationOverlap(checkOrder) {
155
+ if (!checkOrder) return false;
156
+
157
+ const orderStart = BigInt(checkOrder.lock_lp_start_price);
158
+ const orderEnd = BigInt(checkOrder.lock_lp_end_price);
159
+ const orderMin = isDown ? orderEnd : orderStart;
160
+ const orderMax = isDown ? orderStart : orderEnd;
161
+
162
+ // 计算扩大区间值
163
+ const expansionAmount = (orderMax - orderMin) * BigInt(Math.floor(LIQUIDITY_RESERVATION)) / 100n;
164
+
165
+ let expandedStart, expandedEnd,hasOverlap;
166
+ if (isDown) {
167
+ // down_orders: start不变,end向下扩大
168
+ expandedStart = orderMax;
169
+ expandedEnd = orderMin - expansionAmount;
170
+ if (startPrice < expandedEnd){
171
+ hasOverlap = false;
172
+ }else{
173
+ hasOverlap = true;
174
+ }
175
+ } else {
176
+ // up_orders: start不变,end向上扩大
177
+ expandedStart = orderMin;
178
+ expandedEnd = orderMax + expansionAmount;
179
+ if (startPrice > expandedEnd){
180
+ hasOverlap = false;
181
+ }else{
182
+ hasOverlap = true;
183
+ }
184
+ }
185
+
186
+
187
+ // if (hasOverlap) {
188
+ // console.log(` Order range: ${orderMin}-${orderMax}`);
189
+ // console.log(` Expanded range: ${expandedStart}-${expandedEnd}`);
190
+ // console.log(` New range: ${minPrice}-${maxPrice}`);
191
+ // }
192
+
193
+ return hasOverlap;
194
+ }
195
+
196
+ // 检查与前一个订单的流动性预留重叠
197
+ if (prevOrder && checkLiquidityReservationOverlap(prevOrder)) {
198
+ return {
199
+ no_overlap: false,
200
+ prev_order_pda: prevOrder ? prevOrder.order_pda : null,
201
+ next_order_pda: nextOrder ? nextOrder.order_pda : null,
202
+ overlap_reason: "Overlaps with previous order's liquidity reservation range"
203
+ };
204
+ }
205
+
206
+
207
+
208
+ // 无重叠,返回正确的PDA值
209
+ // prev_order_pda: 插入位置的前一个节点 (索引更小)
210
+ // next_order_pda: 插入位置的后一个节点 (索引更大)
211
+ return {
212
+ no_overlap: true,
213
+ prev_order_pda: prevOrder ? prevOrder.order_pda : null,
214
+ next_order_pda: nextOrder ? nextOrder.order_pda : null,
215
+ overlap_reason: ""
216
+ };
217
+ }
218
+
219
+
220
+ module.exports = {
221
+ transformOrdersData,
222
+ checkPriceRangeOverlap
223
+ };
@@ -0,0 +1,44 @@
1
+
2
+
3
+ // Liquidity reservation ratio - how much liquidity to reserve relative to the last locked liquidity
4
+ const LIQUIDITY_RESERVATION = 100; // 100%
5
+
6
+ // Price adjustment percentage
7
+ const PRICE_ADJUSTMENT_PERCENTAGE = 15; // 5就是 0.5%
8
+
9
+
10
+ /**
11
+ * Convert API order format to expected format
12
+ * @param {Array} apiOrders - Orders returned from API
13
+ * @returns {Array} Converted order list
14
+ */
15
+ function convertApiOrdersFormat(apiOrders) {
16
+ if (!apiOrders || !Array.isArray(apiOrders)) {
17
+ return [];
18
+ }
19
+
20
+ return apiOrders.map(order => ({
21
+ ...order,
22
+ lockLpStartPrice: order.lock_lp_start_price,
23
+ lockLpEndPrice: order.lock_lp_end_price
24
+ }));
25
+ }
26
+
27
+
28
+ /**
29
+ * Handle BigInt absolute value
30
+ * @param {BigInt} value - BigInt value to calculate absolute value
31
+ * @returns {BigInt} Absolute value result
32
+ */
33
+ function absoluteValue(value) {
34
+ return value < 0n ? -value : value;
35
+ }
36
+
37
+
38
+
39
+ module.exports = {
40
+ convertApiOrdersFormat,
41
+ absoluteValue,
42
+ LIQUIDITY_RESERVATION,
43
+ PRICE_ADJUSTMENT_PERCENTAGE
44
+ };