-
Notifications
You must be signed in to change notification settings - Fork 55
/
tick.go
114 lines (103 loc) 路 2.71 KB
/
tick.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package types
import (
"math"
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// char returns the characteristic(integral part) of
// log10(x * pow(10, sdk.Precision)).
func char(x sdk.Dec) int {
if x.IsZero() {
panic("cannot calculate log10 for 0")
}
return len(x.BigInt().Text(10)) - 1
}
// pow10 returns pow(10, n - sdk.Precision).
func pow10(n int) sdk.Dec {
x := big.NewInt(10)
x.Exp(x, big.NewInt(int64(n)), nil)
return sdk.NewDecFromBigIntWithPrec(x, sdk.Precision)
}
// isPow10 returns whether x is a power of 10 or not.
func isPow10(x sdk.Dec) bool {
b := x.BigInt()
if b.Sign() <= 0 {
return false
}
ten := big.NewInt(10)
if b.Cmp(ten) == 0 {
return true
}
zero := big.NewInt(0)
m := new(big.Int)
for b.Cmp(ten) >= 0 {
b.DivMod(b, ten, m)
if m.Cmp(zero) != 0 {
return false
}
}
return b.Cmp(big.NewInt(1)) == 0
}
// PriceToTick returns the highest price tick under(or equal to) the price.
func PriceToTick(price sdk.Dec, prec int) sdk.Dec {
b := price.BigInt()
l := char(price)
d := int64(l - prec)
if d > 0 {
p := big.NewInt(10)
p.Exp(p, big.NewInt(d), nil)
b.Quo(b, p).Mul(b, p)
}
return sdk.NewDecFromBigIntWithPrec(b, sdk.Precision)
}
// UpTick returns the next lowest price tick above the price.
// UpTick guarantees that the price is already fit in ticks.
func UpTick(price sdk.Dec, prec int) sdk.Dec {
l := char(price)
return price.Add(pow10(l - prec))
}
// DownTick returns the next highest price tick under the price.
// DownTick guarantees that the price is already fit in ticks.
// DownTick doesn't check if the price is the lowest price tick.
func DownTick(price sdk.Dec, prec int) sdk.Dec {
l := char(price)
var d sdk.Dec
if isPow10(price) {
d = pow10(l - prec - 1)
} else {
d = pow10(l - prec)
}
return price.Sub(d)
}
// LowestTick returns the lowest possible price tick.
func LowestTick(prec int) sdk.Dec {
return sdk.NewDecWithPrec(1, int64(sdk.Precision-prec))
}
// TickToIndex returns a tick index for given price.
// Tick index 0 means the lowest possible price fit in ticks.
func TickToIndex(price sdk.Dec, prec int) int {
b := price.BigInt()
l := len(b.Text(10)) - 1
d := int64(l - prec)
if d > 0 {
q := big.NewInt(10)
q.Exp(q, big.NewInt(d), nil)
b.Quo(b, q)
}
p := int(math.Pow10(prec))
b.Sub(b, big.NewInt(int64(p)))
return (l-prec)*9*p + int(b.Int64())
}
// TickFromIndex returns a price for given tick index.
// See TickToIndex for more details about tick indices.
func TickFromIndex(i, prec int) sdk.Dec {
p := int(math.Pow10(prec))
l := i/(9*p) + prec
t := big.NewInt(int64(p + i%(p*9)))
if l > prec {
m := big.NewInt(10)
m.Exp(m, big.NewInt(int64(l-prec)), nil)
t.Mul(t, m)
}
return sdk.NewDecFromBigIntWithPrec(t, sdk.Precision)
}