tickMath.test.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import { Decimal } from 'decimal.js';
  2. import { describe, test, expect } from 'vitest';
  3. import { SqrtPriceMath } from './sqrtPriceMath';
  4. import { TickMath } from './tickMath';
  5. describe('TickMath basic functionality test', () => {
  6. test('getTickWithPriceAndTickspacing basic functionality', () => {
  7. const priceList = [
  8. new Decimal('0.001'),
  9. new Decimal('0.01'),
  10. new Decimal('0.1'),
  11. new Decimal('1'),
  12. new Decimal('10'),
  13. new Decimal('100'),
  14. new Decimal('1000'),
  15. new Decimal('10000'),
  16. ];
  17. const tickSpacing = 10;
  18. const decimalsA = 6;
  19. const decimalsB = 6;
  20. for (const price of priceList) {
  21. const tick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
  22. expect(Math.abs(tick % tickSpacing)).toBe(0);
  23. }
  24. });
  25. test('getPriceFromTick basic functionality', () => {
  26. const tick = 100;
  27. const decimalsA = 6;
  28. const decimalsB = 6;
  29. const price = TickMath.getPriceFromTick({ tick, decimalsA, decimalsB });
  30. // Verify that the price is positive
  31. expect(price.isPositive()).toBeTruthy();
  32. });
  33. test('getTickAlignedPriceDetails returns the correct structure', () => {
  34. const price = new Decimal('1.5');
  35. const tickSpacing = 10;
  36. const decimalsA = 6;
  37. const decimalsB = 6;
  38. const details = TickMath.getTickAlignedPriceDetails(price, tickSpacing, decimalsA, decimalsB);
  39. // Verify that the returned object contains all necessary fields
  40. expect(details).toHaveProperty('tick');
  41. expect(details).toHaveProperty('sqrtPriceX64');
  42. expect(details).toHaveProperty('price');
  43. expect(details.tick % tickSpacing).toBe(0);
  44. });
  45. });
  46. describe('TickMath rounding behavior test', () => {
  47. test('positive tick rounding up', () => {
  48. // Select a positive tick that is not a multiple of tickSpacing
  49. const originalTick = 15;
  50. const tickSpacing = 10;
  51. const decimalsA = 9;
  52. const decimalsB = 6;
  53. const price = TickMath.getPriceFromTick({ tick: originalTick, decimalsA, decimalsB });
  54. const alignedTick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
  55. expect(alignedTick).toBe(20);
  56. });
  57. test('negative tick rounding down', () => {
  58. // Similar to above, but using a negative tick
  59. const originalTick = -15;
  60. const tickSpacing = 10;
  61. const decimalsA = 9;
  62. const decimalsB = 6;
  63. const price = TickMath.getPriceFromTick({ tick: originalTick, decimalsA, decimalsB });
  64. const alignedTick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
  65. // 负数应该向下舍入到 -20
  66. expect(alignedTick).toBe(-20);
  67. });
  68. });
  69. describe('TickMath boundary condition test', () => {
  70. test('extreme price values', () => {
  71. const extremePrices = [
  72. new Decimal('0.0000001'), // Very small price
  73. new Decimal('1000000'), // Very large price
  74. ];
  75. const tickSpacing = 60;
  76. const decimalsA = 6;
  77. const decimalsB = 6;
  78. for (const price of extremePrices) {
  79. const tick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
  80. // Verify that the tick is a multiple of tickSpacing
  81. expect(Math.abs(tick % tickSpacing)).toBe(0);
  82. // Verify that there is no exception when converting back from tick to price
  83. const details = TickMath.getTickAlignedPriceDetails(price, tickSpacing, decimalsA, decimalsB);
  84. expect(details.price.isPositive()).toBeTruthy();
  85. }
  86. });
  87. test('different tickSpacing values', () => {
  88. const price = new Decimal('1.5');
  89. const decimalsA = 6;
  90. const decimalsB = 6;
  91. const tickSpacings = [1, 10, 60, 200];
  92. for (const spacing of tickSpacings) {
  93. const tick = TickMath.getTickWithPriceAndTickspacing(price, spacing, decimalsA, decimalsB);
  94. // Verify that the tick is a multiple of tickSpacing
  95. expect(tick % spacing).toBe(0);
  96. // Verify that the difference between the original tick and the aligned tick should not exceed tickSpacing
  97. const originalTick = SqrtPriceMath.getTickFromPrice(price, decimalsA, decimalsB);
  98. expect(Math.abs(tick - originalTick)).toBeLessThan(spacing);
  99. }
  100. });
  101. });
  102. describe('TickMath consistency test', () => {
  103. test('price -> tick -> price conversion consistency', () => {
  104. const testPrices = [new Decimal('0.01'), new Decimal('0.5'), new Decimal('1'), new Decimal('2'), new Decimal('10')];
  105. const tickSpacing = 10;
  106. const decimalsA = 6;
  107. const decimalsB = 6;
  108. for (const initialPrice of testPrices) {
  109. // Get the aligned price information
  110. const details = TickMath.getTickAlignedPriceDetails(initialPrice, tickSpacing, decimalsA, decimalsB);
  111. // Convert back from tick to price
  112. const recalculatedPrice = TickMath.getPriceFromTick({
  113. tick: details.tick,
  114. decimalsA,
  115. decimalsB,
  116. });
  117. // Verify that the two price values are equal within a reasonable error range
  118. expect(details.price.toString()).toBe(recalculatedPrice.toString());
  119. }
  120. });
  121. test('different decimals combinations', () => {
  122. const price = new Decimal('2.5');
  123. const tickSpacing = 60;
  124. const decimalCombinations = [
  125. { decimalsA: 6, decimalsB: 6 },
  126. { decimalsA: 8, decimalsB: 6 },
  127. { decimalsA: 6, decimalsB: 8 },
  128. { decimalsA: 9, decimalsB: 18 },
  129. ];
  130. for (const { decimalsA, decimalsB } of decimalCombinations) {
  131. const details = TickMath.getTickAlignedPriceDetails(price, tickSpacing, decimalsA, decimalsB);
  132. // Verify that the tick is a multiple of tickSpacing
  133. expect(Math.abs(details.tick % tickSpacing)).toBe(0);
  134. // Verify that the magnitude of the adjusted price is similar to the original price
  135. const magnitudeDiff = Math.abs(details.price.div(price).toNumber() - 1);
  136. expect(magnitudeDiff).toBeLessThan(0.01);
  137. }
  138. });
  139. });
  140. describe('TickMath and SqrtPriceMath combination test', () => {
  141. test('getTickWithPriceAndTickspacing and SqrtPriceMath consistency', () => {
  142. const price = new Decimal('1.2345');
  143. const tickSpacing = 60;
  144. const decimalsA = 6;
  145. const decimalsB = 6;
  146. // Get the original tick
  147. const originalTick = SqrtPriceMath.getTickFromPrice(price, decimalsA, decimalsB);
  148. // Get the aligned tick
  149. const alignedTick = TickMath.getTickWithPriceAndTickspacing(price, tickSpacing, decimalsA, decimalsB);
  150. // Verify that the aligned tick is the closest multiple of tickSpacing to the original tick
  151. const expectedAlignedTick =
  152. originalTick >= 0
  153. ? Math.ceil(originalTick / tickSpacing) * tickSpacing
  154. : Math.floor(originalTick / tickSpacing) * tickSpacing;
  155. expect(alignedTick).toBe(expectedAlignedTick);
  156. });
  157. test('the effect of baseIn parameter on price calculation', () => {
  158. const tick = 100;
  159. const decimalsA = 6;
  160. const decimalsB = 6;
  161. // baseIn = true
  162. const priceBaseIn = TickMath.getPriceFromTick({
  163. tick,
  164. decimalsA,
  165. decimalsB,
  166. baseIn: true,
  167. });
  168. // baseIn = false
  169. const priceBaseOut = TickMath.getPriceFromTick({
  170. tick,
  171. decimalsA,
  172. decimalsB,
  173. baseIn: false,
  174. });
  175. // Verify that the price when baseIn = false should be the reciprocal of the price when baseIn = true
  176. expect(priceBaseIn.mul(priceBaseOut).toNumber()).toBeCloseTo(1, 0.00001);
  177. });
  178. });