lp_utils.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. package arbitrage
  2. import (
  3. "github.com/ethereum/go-ethereum/arbitrage/api"
  4. "github.com/ethereum/go-ethereum/log"
  5. "github.com/shopspring/decimal"
  6. )
  7. // FundMovements 池子资金动向
  8. type FundMovements struct {
  9. LpHash string
  10. Fee int64
  11. InIndex uint64
  12. InToken string
  13. InReserve decimal.Decimal
  14. OutIndex uint64
  15. OutToken string
  16. OutReserve decimal.Decimal
  17. }
  18. // Path 交易路径
  19. type Path struct {
  20. // path.FirstLpInTokenHash = firstFm.InToken
  21. FirstLpInTokenHash string
  22. // path.LastLpOutTokenHash = lastFm.OutToken
  23. LastLpOutTokenHash string
  24. LoanLp string
  25. LoanIndex int64
  26. LoanTokenHash string
  27. Level int64
  28. Profit decimal.Decimal
  29. FeeTokenProfit decimal.Decimal
  30. Sim Simulation
  31. LpHashList []string
  32. FmList []FundMovements
  33. }
  34. // InsertionSort 不适合大量数据的排序,暂时可以先用着
  35. func InsertionSort(paths []Path) {
  36. for i := 1; i < len(paths); i++ {
  37. key := paths[i]
  38. j := i - 1
  39. // Move elements of paths[0..i-1], that are
  40. // greater than key, to one position ahead
  41. // of their current position
  42. for j >= 0 && paths[j].Profit.LessThan(key.Profit) {
  43. paths[j+1] = paths[j]
  44. j = j - 1
  45. }
  46. paths[j+1] = key
  47. }
  48. }
  49. func PutSimulationToPathList(pathList []Path) {
  50. for index, path := range pathList {
  51. if path.Level == 2 {
  52. SimulationLevel2Path(&path)
  53. } else if path.Level == 3 {
  54. SimulationLevel3Path(&path)
  55. }
  56. pathList[index] = path
  57. }
  58. }
  59. func GetFinalPathList(pathList []Path) []Path {
  60. var finalPathList []Path
  61. for _, path := range pathList {
  62. if path.Profit.GreaterThan(MinProfit) {
  63. finalPathList = append(finalPathList, path)
  64. }
  65. }
  66. return finalPathList
  67. }
  68. func GenerateTokenLpListMapping(lpList []api.V2Lp) map[string][]api.V2Lp {
  69. mapping := make(map[string][]api.V2Lp)
  70. for _, lp := range lpList {
  71. mapping[lp.Token0] = append(mapping[lp.Token0], lp)
  72. mapping[lp.Token1] = append(mapping[lp.Token1], lp)
  73. }
  74. return mapping
  75. }
  76. func LpListFilter(lpList []api.V2Lp, unfilteredMapping map[string][]api.V2Lp) []api.V2Lp {
  77. var filteredLpList []api.V2Lp
  78. for _, lp := range lpList {
  79. isToken0NoCorrelation := len(unfilteredMapping[lp.Token0]) <= 1
  80. isToken1NoCorrelation := len(unfilteredMapping[lp.Token1]) <= 1
  81. // 有任意一个token是孤儿token(与其他lp没有关系),则该lp是无效lp
  82. if isToken0NoCorrelation || isToken1NoCorrelation {
  83. continue
  84. }
  85. filteredLpList = append(filteredLpList, lp)
  86. }
  87. return filteredLpList
  88. }
  89. func GeneratePathListCore(finalMapping map[string][]api.V2Lp, level int64, maxLevel int64, path Path) []Path {
  90. var pathList []Path
  91. // 1. path闭环判断
  92. firstFm := path.FmList[0]
  93. lastFm := path.FmList[len(path.FmList)-1]
  94. // 要同时满足进出一致和LastOutToken为MagicToken的条件
  95. if firstFm.InToken == lastFm.OutToken && IsMagicToken(lastFm.OutToken) {
  96. path.Level = level - 1
  97. path.FirstLpInTokenHash = firstFm.InToken
  98. path.LastLpOutTokenHash = lastFm.OutToken
  99. pathList = append(pathList, path)
  100. return pathList
  101. }
  102. // 2. level溢出判断
  103. if maxLevel < level {
  104. return pathList
  105. }
  106. // 3. 不闭环或不溢出,继续递归
  107. relatedLpList := finalMapping[lastFm.OutToken]
  108. for _, lp := range relatedLpList {
  109. // 3.1 lp去重(用过的lp不再用)
  110. isLpUsed := false
  111. for _, fm := range path.FmList {
  112. if lp.Hash == fm.LpHash {
  113. isLpUsed = true
  114. }
  115. }
  116. if isLpUsed {
  117. continue
  118. }
  119. newPath := path
  120. // 3.2 构造FundMovements,必须要
  121. if lastFm.OutToken == lp.Token0 {
  122. newPath.FmList = append(path.FmList, FundMovements{
  123. LpHash: lp.Hash,
  124. Fee: lp.Fee,
  125. InIndex: 0,
  126. InToken: lp.Token0,
  127. InReserve: lp.R0,
  128. OutIndex: 1,
  129. OutToken: lp.Token1,
  130. OutReserve: lp.R1,
  131. })
  132. newPath.LpHashList = append(newPath.LpHashList, lp.Hash)
  133. } else if lastFm.OutToken == lp.Token1 {
  134. newPath.FmList = append(path.FmList, FundMovements{
  135. LpHash: lp.Hash,
  136. Fee: lp.Fee,
  137. InIndex: 1,
  138. InToken: lp.Token1,
  139. InReserve: lp.R1,
  140. OutIndex: 0,
  141. OutToken: lp.Token0,
  142. OutReserve: lp.R0,
  143. })
  144. newPath.LpHashList = append(newPath.LpHashList, lp.Hash)
  145. } else {
  146. log.Error("path生成有错误,请重新debug查询。", "lp hash", lp.Hash)
  147. continue
  148. }
  149. newPathList := GeneratePathListCore(finalMapping, level+1, maxLevel, newPath)
  150. pathList = append(pathList, newPathList...)
  151. }
  152. return pathList
  153. }
  154. func GeneratePathList(finalLpList []api.V2Lp, finalMapping map[string][]api.V2Lp, maxLevel int64) []Path {
  155. var pathList []Path
  156. NowLevel := int64(1)
  157. for _, lp := range finalLpList {
  158. pathA := Path{
  159. Level: NowLevel,
  160. LoanTokenHash: lp.Token1,
  161. LoanLp: lp.Hash,
  162. LoanIndex: 1,
  163. FmList: []FundMovements{
  164. {
  165. LpHash: lp.Hash,
  166. Fee: lp.Fee,
  167. InIndex: 0,
  168. InToken: lp.Token0,
  169. InReserve: lp.R0,
  170. OutIndex: 1,
  171. OutToken: lp.Token1,
  172. OutReserve: lp.R1,
  173. },
  174. },
  175. }
  176. pathA.LpHashList = append(pathA.LpHashList, lp.Hash)
  177. pathListA := GeneratePathListCore(finalMapping, NowLevel+1, maxLevel, pathA)
  178. pathB := Path{
  179. Level: NowLevel,
  180. LoanTokenHash: lp.Token0,
  181. LoanLp: lp.Hash,
  182. LoanIndex: 0,
  183. FmList: []FundMovements{
  184. {
  185. LpHash: lp.Hash,
  186. Fee: lp.Fee,
  187. InIndex: 1,
  188. InToken: lp.Token1,
  189. InReserve: lp.R1,
  190. OutIndex: 0,
  191. OutToken: lp.Token0,
  192. OutReserve: lp.R0,
  193. },
  194. },
  195. }
  196. pathB.LpHashList = append(pathB.LpHashList, lp.Hash)
  197. pathListB := GeneratePathListCore(finalMapping, NowLevel+1, maxLevel, pathB)
  198. pathList = append(pathList, pathListA...)
  199. pathList = append(pathList, pathListB...)
  200. }
  201. return pathList
  202. }
  203. func ParseLpToFinalLp(lpList []api.V2Lp) []api.V2Lp {
  204. // 1. 生成token->relatedLpList的映射结构,token为tokenHash,lpList为与该token相关的所有池子。
  205. //
  206. // 生成这个映射结构有两个目的:
  207. // a. 基础过滤无效lp(主要是过滤与其他lp无关的lp)
  208. // b. 之后可以用于生成path
  209. unfilteredMapping := GenerateTokenLpListMapping(lpList)
  210. // 2. 剔除len(lpList)<=1的映射;第二层意思:如果某个token只有一个lp,那么,该lp不属于有效池子;用于生成有效lp
  211. finalLpList := LpListFilter(lpList, unfilteredMapping)
  212. return finalLpList
  213. }
  214. func ParseLpListToPathList(finalLpList []api.V2Lp, maxLevel int64) []Path {
  215. // 1. 重新生成映射结构,形成最终的映射结构(token->relatedLpList)
  216. finalMapping := GenerateTokenLpListMapping(finalLpList)
  217. // 2. 根据资金流概念生成path
  218. pathList := GeneratePathList(finalLpList, finalMapping, maxLevel)
  219. return pathList
  220. }
  221. func ParseLpListToLpHashList(lpList []api.V2Lp) []string {
  222. var rst []string
  223. for _, lp := range lpList {
  224. rst = append(rst, lp.Hash)
  225. }
  226. return rst
  227. }
  228. func (h *HistoryArbitrage) GetLpList() []api.V2Lp {
  229. return h.javaApi.GetAllV2LpList(V2LpPath)
  230. }
  231. func PutR0R1ToLpList(lpList []api.V2Lp, balanceMapping map[string][2]decimal.Decimal) {
  232. for i := range lpList {
  233. lp := &(lpList[i])
  234. reserves := balanceMapping[lp.Hash]
  235. lp.R0 = reserves[0]
  236. lp.R1 = reserves[1]
  237. }
  238. }
  239. func FilterLpList(tempLpList []api.V2Lp) []api.V2Lp {
  240. var finalLp []api.V2Lp
  241. for _, lp := range tempLpList {
  242. if lp.R0.Equals(decimal.Zero) || lp.R1.Equals(decimal.Zero) {
  243. continue
  244. }
  245. finalLp = append(finalLp, lp)
  246. }
  247. return finalLp
  248. }