debugTradeV4.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. import time
  2. from calcTools import *
  3. from osTools import *
  4. from web3Tools import *
  5. class MaxProfitUtils:
  6. # 判断两个数组是否有交集
  7. @staticmethod
  8. def has_intersection(arr1, arr2):
  9. # 1.这段感觉会慢
  10. return len(set(arr1) & set(arr2)) > 0
  11. # 若二维数组中所有数组与指定数组没有交集,返回True
  12. # 2.已经判断没有交集的路径,就不需要再去互相判断了。
  13. @staticmethod
  14. def all_has_no_intersection(swap_details_list, check_swap_details):
  15. for swap_details in swap_details_list:
  16. if MaxProfitUtils.has_intersection(swap_details['swapPath'], check_swap_details['swapPath']):
  17. return False
  18. return True
  19. # 求和swap详情list的利润并返回
  20. @staticmethod
  21. def get_profit_sum(swap_details_list):
  22. profit_sum = 0
  23. for swap_details in swap_details_list:
  24. profit_sum += swap_details['profit']
  25. return profit_sum
  26. @staticmethod
  27. def get_max_profit_swap_details_list(x, y):
  28. return x if MaxProfitUtils.get_profit_sum(x) > MaxProfitUtils.get_profit_sum(y) else y
  29. # 得出指定数据结构中的最大利润path
  30. @staticmethod
  31. def max_profit_path_list(origin_swap_details_list, level=0, swap_details_list=None):
  32. # first level
  33. if swap_details_list is None:
  34. swap_details_list = []
  35. # last level
  36. # >=
  37. if level == len(origin_swap_details_list):
  38. return swap_details_list
  39. # 此层的swap_path与当前的swap_path_list内的所有swap_path都没有交集的话,将此层的加入为x路径
  40. x = []
  41. if MaxProfitUtils.all_has_no_intersection(swap_details_list, origin_swap_details_list[level]):
  42. # 3.这段会很卡
  43. new_swap_details_list = json.loads(json.dumps(swap_details_list))
  44. new_swap_details_list.append(origin_swap_details_list[level])
  45. x = MaxProfitUtils.max_profit_path_list(origin_swap_details_list, level+1, new_swap_details_list)
  46. # 不将此层加入的y路径
  47. y = MaxProfitUtils.max_profit_path_list(origin_swap_details_list, level+1, swap_details_list)
  48. return MaxProfitUtils.get_max_profit_swap_details_list(x, y)
  49. def calOnlyLpInRouterArray(routerArray):
  50. onlyLpArray = []
  51. for router in routerArray:
  52. for lpInfo in router:
  53. lp = lpInfo['lp']
  54. if lp in onlyLpArray:
  55. continue
  56. if lp in nowBlockLpRes:
  57. continue
  58. onlyLpArray.append(lp)
  59. return onlyLpArray
  60. def calRouterProfit(router):
  61. # 整理LP信息
  62. for lpInfo in router:
  63. lpAddress = lpInfo['lp']
  64. r0 = nowBlockLpRes[lpAddress][0]
  65. r1 = nowBlockLpRes[lpAddress][1]
  66. if lpAddress not in topLpInfo:
  67. return False
  68. lpInfo['fee'] = topLpInfo[lpAddress]['fee']
  69. if lpInfo['inToken'] < lpInfo['outToken']:
  70. lpInfo['rIn'] = r0
  71. lpInfo['rOut'] = r1
  72. else:
  73. lpInfo['rIn'] = r1
  74. lpInfo['rOut'] = r0
  75. # ABA
  76. if len(router) == 2:
  77. tokenLoan = router[1]['outToken']
  78. tokenIn0 = router[0]['inToken']
  79. tokenLoanETHPrice = baseTokenInfo[tokenLoan]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** baseTokenInfo[tokenLoan]['decimals']
  80. # WETH - A - WETH
  81. tradeInfo = calBestAmountV2V2(router[0]['rIn'], router[0]['rOut'], router[0]['fee'], router[1]['rIn'], router[1]['rOut'], router[1]['fee'] )
  82. profit = tradeInfo['profit']
  83. tradeInfo['ethProfit'] = profit * tokenLoanETHPrice
  84. #printTime(router, amountIn0, amountOut0, amountOut1, profit)
  85. if tradeInfo['ethProfit'] < tradeMinProfit:
  86. return False
  87. else:
  88. tradeInfo['lp0'] = router[0]['lp']
  89. tradeInfo['lp1'] = router[1]['lp']
  90. tradeInfo['fee0'] = router[0]['fee']
  91. tradeInfo['fee1'] = router[1]['fee']
  92. tradeInfo['indexIn0'] = router[0]['indexIn']
  93. tradeInfo['indexIn1'] = router[1]['indexIn']
  94. tradeInfo['tokenLoan'] = tokenLoan
  95. tradeInfo['tokenIn0'] = tokenIn0
  96. tradeInfo['tradeToken0'] = router[0]['outToken']
  97. tradeInfo['type'] = 'type2'
  98. return tradeInfo
  99. # ABCA
  100. elif len(router) == 3:
  101. tokenLoan = router[2]['outToken']
  102. tokenIn0 = router[0]['inToken']
  103. tokenLoanETHPrice = baseTokenInfo[tokenLoan]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** baseTokenInfo[tokenLoan]['decimals']
  104. tradeInfo = calBestAmountABCA(router[0]['rIn'], router[0]['rOut'], router[0]['fee'],
  105. router[1]['rIn'], router[1]['rOut'], router[1]['fee'],
  106. router[2]['rIn'], router[2]['rOut'], router[2]['fee'])
  107. profit = tradeInfo['profit']
  108. tradeInfo['ethProfit'] = profit * tokenLoanETHPrice
  109. #printTime(router, amountIn0, amountOut0, amountOut1, amountOut2, profit)
  110. if tradeInfo['ethProfit'] < tradeMinProfit:
  111. return False
  112. else:
  113. tradeInfo['lp0'] = router[0]['lp']
  114. tradeInfo['lp1'] = router[1]['lp']
  115. tradeInfo['lp2'] = router[2]['lp']
  116. tradeInfo['fee0'] = router[0]['fee']
  117. tradeInfo['fee1'] = router[1]['fee']
  118. tradeInfo['fee2'] = router[2]['fee']
  119. tradeInfo['indexIn0'] = router[0]['indexIn']
  120. tradeInfo['indexIn1'] = router[1]['indexIn']
  121. tradeInfo['indexIn2'] = router[2]['indexIn']
  122. tradeInfo['tokenLoan'] = tokenLoan
  123. tradeInfo['tokenIn0'] = tokenIn0
  124. tradeInfo['tradeToken0'] = router[0]['outToken']
  125. tradeInfo['tradeToken1'] = router[1]['outToken']
  126. tradeInfo['type'] = 'type3'
  127. return tradeInfo
  128. def callABCA(lp0, lp1, lpLoan, tokenIn0, tokenLoan, fee0, fee1, fee2, lp0InIndex, lp1InIndex, lp2InIndex, minProfit, block = 'latest'):
  129. function = '0x1d650c35'
  130. inputData = encodeInput(lp0, lp1, lpLoan, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  131. fee0, fee1, fee2, lp0InIndex, lp1InIndex, lp2InIndex,
  132. tokenIn0, tokenLoan, minProfit, 0)
  133. inputData = function + inputData
  134. r = gasCal(myAddress, v3CCAddress, inputData, block)
  135. tradeInfo = {'type':3, 'lp0': lp0, 'lp1': lp1, 'lpLoan':lpLoan, 'tokenIn':tokenIn0, 'tokenLoan':tokenLoan, 'profit':0, 'sumValue':0}
  136. if len(r['info']) != 23 or r['info'][-1] == 0:
  137. return tradeInfo
  138. sumValue = r['info'][3] + r['info'][4] + r['info'][5] + r['info'][6] + r['info'][7] + r['info'][8]
  139. tradeInfo['sumValue'] = sumValue
  140. tradeInfo['profit'] = r['info'][-1]
  141. tradeInfo['gasUsed'] = r['gasUsed']
  142. tradeInfo['inputData'] = inputData
  143. return tradeInfo
  144. def callABA(lp0, lp1, tokenIn0, tokenLoan, fee0, fee1, lp0InIndex, lp1InIndex, minProfit, block = 'latest'):
  145. function = '0xa628df6d'
  146. inputData = encodeInput(lp0, lp1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  147. fee0, fee1, 0, lp0InIndex, lp1InIndex, 0,
  148. tokenIn0, tokenLoan, minProfit, 0)
  149. inputData = function + inputData
  150. r = gasCal(myAddress, v3CCAddress, inputData, block)
  151. tradeInfo = {'type':2, 'lp0': lp0, 'lpLoan':lp1, 'tokenIn':tokenIn0, 'tokenLoan':tokenLoan, 'profit':0, 'sumValue':0}
  152. if len(r['info']) != 23 or r['info'][-1] == 0:
  153. return tradeInfo
  154. sumValue = r['info'][3] + r['info'][4] + r['info'][5] + r['info'][6] + r['info'][7] + r['info'][8]
  155. tradeInfo['sumValue'] = sumValue
  156. tradeInfo['profit'] = r['info'][-1]
  157. tradeInfo['gasUsed'] = r['gasUsed']
  158. tradeInfo['inputData'] = inputData
  159. return tradeInfo
  160. def readABA():
  161. array = []
  162. for i in baseTokenInfo:
  163. i = i.lower()
  164. iiArray = readText('aba' + i + i)
  165. array = array + iiArray
  166. array = array + readText('aba' + WETHW + WETH)
  167. array = array + readText('aba' + WETH + WETHW)
  168. return array
  169. def readABCA():
  170. array = []
  171. for i in baseTokenInfo:
  172. i = i.lower()
  173. iiArray = readText('abca' + i + i)
  174. array = array + iiArray
  175. array = array + readText('abca' + WETHW + WETH)
  176. array = array + readText('abca' + WETH + WETHW)
  177. return array
  178. topLpInfo = readText('topLP')
  179. lpRouterInfo = readABCA()
  180. lpRouterInfo = lpRouterInfo + readABA()
  181. printTime('v3 start')
  182. v3CCAddress = '0xB4971D0dda22359Ad86867362b7FC3206eA0d86B'
  183. dataUsed = []
  184. nowBlockLpRes = {}
  185. initBlock = 15879488
  186. while True:
  187. try:
  188. blockNumber = initBlock + 1
  189. if blockNumber == initBlock:
  190. time.sleep(0.1)
  191. continue
  192. printTime(blockNumber, 'start')
  193. initBlock = blockNumber
  194. baseGasPrice = getBaseGasPrice(blockNumber) * 1.5
  195. if baseGasPrice < 1.6 * 1e9:
  196. baseGasPrice = 1.6 * 1e9
  197. nowBlockLpRes = {}
  198. # 找出有关该LP的路由
  199. lpRouterArray = lpRouterInfo # findLpRouter(nowBlockLpTradeI, lpRouterInfo)
  200. # 过滤需要获取余额的路由
  201. lpOnlyRouterArray = calOnlyLpInRouterArray(lpRouterArray)
  202. # printTime(len(lpOnlyRouterArray), len(lpRouterArray))
  203. # 获取余额
  204. lpResArray = w3.dex.getPairSBalance(lpOnlyRouterArray, blockNumber)
  205. # 余额进行缓存
  206. for lpResI in lpResArray:
  207. nowBlockLpRes[lpResI] = lpResArray[lpResI]
  208. #计算每一个路由的利润
  209. tradeInfoArray = []
  210. initForTime = time.time()
  211. for lpRouter in lpRouterArray:
  212. if time.time() - initForTime > 60:
  213. printTime(blockNumber, 'lpRouterArray long time')
  214. tradeInfo = calRouterProfit(lpRouter)
  215. if tradeInfo:
  216. type = tradeInfo['type']
  217. if type == 'type2':
  218. result = callABA(tradeInfo['lp0'], tradeInfo['lp1'],
  219. tradeInfo['tokenIn0'], tradeInfo['tokenLoan'],
  220. tradeInfo['fee0'], tradeInfo['fee1'],
  221. tradeInfo['indexIn0'], tradeInfo['indexIn1'],
  222. baseTokenInfo[tradeInfo['tokenIn0']]['profitMin'], blockNumber)
  223. if result['profit'] < tradeMinProfit:
  224. continue
  225. sumValue = result['sumValue']
  226. if sumValue in dataUsed:
  227. continue
  228. tokenLoanETHPrice = baseTokenInfo[tradeInfo['tokenLoan']]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** \
  229. baseTokenInfo[tradeInfo['tokenLoan']]['decimals']
  230. result['ethProfit'] = result['profit'] * tokenLoanETHPrice
  231. result['ethProfit-E18'] = result['profit'] * tokenLoanETHPrice / 1e18
  232. result['tureProfit'] = result['ethProfit'] - result['gasUsed'] * baseGasPrice
  233. result['maxPrice'] = result['ethProfit'] / result['gasUsed']
  234. # if tradeInfo['lp1'] =='0xd75ea151a61d06868e31f8988d28dfe5e9df57b4':
  235. # printTime(result)
  236. if result['tureProfit'] < tradeMinProfit:
  237. continue
  238. tradeInfoArray.append(result)
  239. if type == 'type3':
  240. result = callABCA(tradeInfo['lp0'], tradeInfo['lp1'], tradeInfo['lp2'],
  241. tradeInfo['tokenIn0'], tradeInfo['tokenLoan'],
  242. tradeInfo['fee0'], tradeInfo['fee1'],tradeInfo['fee2'],
  243. tradeInfo['indexIn0'], tradeInfo['indexIn1'], tradeInfo['indexIn2'],
  244. baseTokenInfo[tradeInfo['tokenIn0']]['profitMin'], blockNumber)
  245. if result['profit'] < tradeMinProfit:
  246. continue
  247. sumValue = result['sumValue']
  248. if sumValue in dataUsed:
  249. continue
  250. tokenLoanETHPrice = baseTokenInfo[tradeInfo['tokenLoan']]['price'] * 10 ** baseTokenInfo[WETH][
  251. 'decimals'] / 10 ** \
  252. baseTokenInfo[tradeInfo['tokenLoan']]['decimals']
  253. result['ethProfit'] = result['profit'] * tokenLoanETHPrice
  254. result['ethProfit-E18'] = result['profit'] * tokenLoanETHPrice / 1e18
  255. result['tureProfit'] = result['ethProfit'] - result['gasUsed'] * baseGasPrice
  256. result['maxPrice'] = result['ethProfit'] / result['gasUsed']
  257. if result['tureProfit'] < tradeMinProfit:
  258. continue
  259. tradeInfoArray.append(result)
  260. if len(tradeInfoArray) != 0 :
  261. #printTime('trade List' ,blockNumber, tradeInfoArray)
  262. tradeList = []
  263. initForTime = time.time()
  264. for tradeInfoI in tradeInfoArray:
  265. if time.time() - initForTime > 60:
  266. printTime(blockNumber, 'tradeInfoArray long time')
  267. if tradeInfoI['type'] == 2:
  268. tradeList.append({"swapPath": [tradeInfoI['lp0'], tradeInfoI['lpLoan']], "profit": tradeInfoI['tureProfit'], 'data':tradeInfoI})
  269. if tradeInfoI['type'] == 3:
  270. tradeList.append(
  271. {"swapPath": [tradeInfoI['lp0'], tradeInfoI['lp1'],tradeInfoI['lpLoan']], "profit": tradeInfoI['tureProfit'], 'data':tradeInfoI})
  272. initbestTradeListTime = time.time()
  273. initTradeList = tradeList
  274. tradeList = sorted(tradeList, key=lambda k: k['profit'], reverse=True)
  275. params = []
  276. for info in tradeList:
  277. paramsI = {}
  278. paramsI['from'] = myAddress
  279. paramsI['to'] = '0xEAf90BcB75e7B92900678c4aBD80883b414bEDD6'
  280. tradeInputData = info['data']['inputData']
  281. paramsI['data'] = '0x4327f0db' + encodeInput(v4Address, 0x40, (len(tradeInputData) - 2) / 2, tradeInputData)
  282. params.append(paramsI)
  283. batchCallResult = batchCall(params, blockNumber)
  284. batchCallResult = decodeBatchCall(batchCallResult['result'])
  285. totalProfit = 0
  286. length = len(tradeList)
  287. bestIndex = []
  288. for i in range(0, length):
  289. profit = batchCallResult[i][-1]
  290. if profit > 0:
  291. tokenLoan = batchCallResult[i][-3]
  292. gasUsed = batchCallResult[i][1]
  293. tokenLoanETHPrice = baseTokenInfo[tokenLoan]['price'] * 10 ** baseTokenInfo[WETH][
  294. 'decimals'] / 10 ** \
  295. baseTokenInfo[tokenLoan]['decimals']
  296. profit = profit * tokenLoanETHPrice
  297. profit = profit - gasUsed * baseGasPrice
  298. if profit < tradeMinProfit:
  299. continue
  300. totalProfit = totalProfit + profit
  301. bestIndex.append(i)
  302. printTime(blockNumber, 'batch', time.time() - initbestTradeListTime, ' s totalProfit', totalProfit/1e18)
  303. bestTradeList = []
  304. for index in bestIndex:
  305. bestTradeList.append(tradeList[index])
  306. for i in bestTradeList:
  307. printTime(i['data']['tokenIn'])
  308. # 最优解法
  309. initbestTradeListTime = time.time()
  310. newMinProfit = tradeMinProfit
  311. while len(tradeList) > 50:
  312. newMinProfit = newMinProfit * 1.1
  313. newTradeList = []
  314. for tradeI in tradeList:
  315. if tradeI['profit'] > newMinProfit:
  316. newTradeList.append(tradeI)
  317. tradeList = newTradeList
  318. bestTradeList = MaxProfitUtils.max_profit_path_list(tradeList)
  319. totalProfit = 0
  320. for tradeI in bestTradeList:
  321. totalProfit = totalProfit + tradeI['profit']
  322. printTime(blockNumber, 'best ', time.time() - initbestTradeListTime, ' s totalProfit', totalProfit/1e18)
  323. for i in bestTradeList:
  324. printTime(i['data']['tokenIn'])
  325. printTime('-' * 50)
  326. except BaseException as err:
  327. printTime(traceback.format_exc())
  328. printTime('loop', )
  329. time.sleep(1)