| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- package arbitrage
- import (
- "encoding/json"
- "fmt"
- "github.com/shopspring/decimal"
- )
- type Simulation struct {
- AmountIn decimal.Decimal
- AmountInStr string `json:"AmountIn"`
- AmountOut0 decimal.Decimal
- AmountOut0Str string `json:"AmountOut0"`
- AmountOut1 decimal.Decimal
- AmountOut1Str string `json:"AmountOut1"`
- AmountOut2 decimal.Decimal
- AmountOut2Str string `json:"AmountOut2"`
- Profit decimal.Decimal
- ProfitStr string `json:"Profit"`
- }
- func (s *Simulation) ToJsonString() string {
- jsonBytes, _ := json.Marshal(s)
- return string(jsonBytes)
- }
- func SimulationLevel2Path(path *Path) {
- if path.Level != 2 {
- HistoryError(fmt.Sprintf("Level error. Need %v, but got %v", 2, path.Level))
- return
- }
- fm0 := path.FmList[0]
- fm1 := path.FmList[1]
- // fee0, fee1
- fee0 := decimal.NewFromInt(fm0.Fee)
- fee1 := decimal.NewFromInt(fm1.Fee)
- decimalNumber1 := decimal.NewFromInt(1)
- decimalNumber1e6 := decimal.NewFromInt(1e6)
- // F0 = 1 - fee0/1e6
- F0 := decimalNumber1.Sub(fee0.Div(decimalNumber1e6))
- // F1 = 1 - fee1/1e6
- F1 := decimalNumber1.Sub(fee1.Div(decimalNumber1e6))
- // B0, S0
- B0, _ := decimal.NewFromString(fm0.InReserve.String())
- S0, _ := decimal.NewFromString(fm0.OutReserve.String())
- // B1, S1
- B1, _ := decimal.NewFromString(fm1.InReserve.String())
- S1, _ := decimal.NewFromString(fm1.OutReserve.String())
- // if F0*F1*S0 + B1*F0 == 0
- partX := F0.Mul(F1).Mul(S0).Add(B1.Mul(F0))
- if partX.Equals(decimal.NewFromInt(0)) {
- HistoryError(fmt.Sprintf("Is error partX. F0=%v, F1=%v, S0=%v, B1=%v", F0, F1, S0, B1))
- return
- }
- // (B0 * B1 * F0 * F1 * S0 * S1) ** 0.5
- partB0 := Sqrt(B0.Mul(B1).Mul(F0).Mul(F1).Mul(S0).Mul(S1), 0)
- // B0 * B1 * F0 * F1 * S0 * S1 - B0 * B1
- partB := partB0.Sub(B0.Mul(B1))
- // best input number
- I := partB.Div(partX).Truncate(0)
- amountIn := I
- amountOut0 := CalcOutByIn(amountIn, B0, S0, fee0)
- amountOut1 := CalcOutByIn(amountOut0, B1, S1, fee1)
- profit := amountOut1.Sub(amountIn)
- path.Profit = profit
- path.Sim = Simulation{
- AmountIn: amountIn,
- AmountInStr: amountIn.String(),
- AmountOut0: amountOut0,
- AmountOut0Str: amountOut0.String(),
- AmountOut1: amountOut1,
- AmountOut1Str: amountOut1.String(),
- Profit: profit,
- ProfitStr: profit.String(),
- }
- }
- func SimulationLevel3Path(path *Path) {
- if path.Level != 3 {
- HistoryError(fmt.Sprintf("Level error. Need %v, but got %v", 3, path.Level))
- return
- }
- fm0 := path.FmList[0]
- fm1 := path.FmList[1]
- fm2 := path.FmList[2]
- // fee0, fee1
- fee0 := decimal.NewFromInt(fm0.Fee)
- fee1 := decimal.NewFromInt(fm1.Fee)
- fee2 := decimal.NewFromInt(fm2.Fee)
- decimalNumber1 := decimal.NewFromInt(1)
- decimalNumber1e6 := decimal.NewFromInt(1e6)
- // F0 = 1 - fee0/1e6
- F0 := decimalNumber1.Sub(fee0.Div(decimalNumber1e6))
- // F1 = 1 - fee1/1e6
- F1 := decimalNumber1.Sub(fee1.Div(decimalNumber1e6))
- // F2 = 1 - fee2 / 1e6
- F2 := decimalNumber1.Sub(fee2.Div(decimalNumber1e6))
- // B0, S0
- B0, _ := decimal.NewFromString(fm0.InReserve.String())
- S0, _ := decimal.NewFromString(fm0.OutReserve.String())
- // B1, S1
- B1, _ := decimal.NewFromString(fm1.InReserve.String())
- S1, _ := decimal.NewFromString(fm1.OutReserve.String())
- // B2, S2
- B2, _ := decimal.NewFromString(fm2.InReserve.String())
- S2, _ := decimal.NewFromString(fm2.OutReserve.String())
- // if (F0 * F1 * F2 * S0 * S1 + B2 * F0 * F1 * S0 + B1 * B2 * F0) == 0
- partX := F0.Mul(F1).Mul(F2).Mul(S0).Mul(S1).Add(B2.Mul(F0).Mul(F1).Mul(S0).Add(B1.Mul(B2).Mul(F0)))
- if partX.Equals(decimal.NewFromInt(0)) {
- HistoryError(fmt.Sprintf("Is error partX. F0=%v, F1=%v, F2=%v, S0=%v, S1=%v, B1=%v, B2=%v", F0, F1, F2, S0, S1, B1, B2))
- return
- }
- // (B0 * B1 * B2 * F0 * F1 * F2 * S0 * S1 * S2) ** 0.5
- partA0 := Sqrt(B0.Mul(B1).Mul(B2).Mul(S0).Mul(S1).Mul(S2).Mul(F0).Mul(F1).Mul(F2), 0)
- // B0 * B1 * B2
- partA1 := B0.Mul(B1).Mul(B2)
- // ((B0 * B1 * B2 * F0 * F1 * F2 * S0 * S1 * S2) ** 0.5 - B0 * B1 * B2) / F0 * F1 * F2 * S0 * S1 + B2 * F0 * F1 * S0 + B1 * B2 * F0
- amountIn := partA0.Sub(partA1).Div(partX).Truncate(0)
- //HistoryInfo(fmt.Sprintf("amountIn=%v", amountIn))
- decimalNumber2 := decimal.NewFromInt(2)
- amountOut0 := CalcOutByIn(amountIn, B0, S0, fee0).Sub(decimalNumber2)
- amountOut1 := CalcOutByIn(amountOut0, B1, S1, fee1).Sub(decimalNumber2)
- amountOut2 := CalcOutByIn(amountOut1, B2, S2, fee2).Sub(decimalNumber2)
- profit := amountOut2.Sub(amountIn)
- path.Profit = profit
- path.Sim = Simulation{
- AmountIn: amountIn,
- AmountInStr: amountIn.String(),
- AmountOut0: amountOut0,
- AmountOut0Str: amountOut0.String(),
- AmountOut1: amountOut1,
- AmountOut1Str: amountOut1.String(),
- AmountOut2: amountOut2,
- AmountOut2Str: amountOut2.String(),
- Profit: profit,
- ProfitStr: profit.String(),
- }
- }
- func CalcOutByIn(amountIn decimal.Decimal, reserveIn decimal.Decimal, reserveOut decimal.Decimal, fee decimal.Decimal) decimal.Decimal {
- if amountIn.LessThan(decimal.NewFromInt(1)) {
- return decimal.NewFromInt(0)
- }
- decimalNumber1e6 := decimal.NewFromInt(1e6)
- decimalNumber1e4 := decimal.NewFromInt(1e4)
- decimalNumber1e2 := decimal.NewFromInt(1e2)
- afterFee := decimalNumber1e6.Sub(fee).Div(decimalNumber1e2)
- amountInWithFee := amountIn.Mul(afterFee)
- // 分子
- numerator := amountInWithFee.Mul(reserveOut)
- // 分母
- denominator := reserveIn.Mul(decimalNumber1e4).Add(amountInWithFee)
- amountOut := numerator.Div(denominator).Sub(decimal.NewFromInt(1))
- return amountOut.Truncate(0)
- }
- // Sqrt 开平方
- func Sqrt(d decimal.Decimal, precision int32) decimal.Decimal {
- half := decimal.NewFromInt(2)
- guess := d.Div(half)
- tolerance := decimal.NewFromFloat(0.00000001)
- for {
- dGuess := d.Div(guess)
- diff := dGuess.Sub(guess).Abs()
- if diff.LessThan(tolerance) {
- break
- }
- guess = guess.Add(dGuess).Div(half)
- }
- return guess.Truncate(precision)
- }
- // Cbrt 开立方
- func Cbrt(d decimal.Decimal, precision int32) decimal.Decimal {
- two := decimal.NewFromInt(2)
- three := decimal.NewFromInt(3)
- guess := d.Div(two)
- tolerance := decimal.NewFromFloat(0.00000001)
- for {
- dGuess := d.Div(guess.Mul(guess))
- diff := dGuess.Sub(guess).Abs()
- if diff.LessThan(tolerance) {
- break
- }
- guess = guess.Mul(two).Add(dGuess).Div(three)
- }
- return guess.Truncate(precision)
- }
|