tradeV4.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. return len(set(arr1) & set(arr2)) > 0
  10. # 若二维数组中所有数组与指定数组没有交集,返回True
  11. @staticmethod
  12. def all_has_no_intersection(swap_details_list, check_swap_details):
  13. for swap_details in swap_details_list:
  14. if MaxProfitUtils.has_intersection(swap_details['swapPath'], check_swap_details['swapPath']):
  15. return False
  16. return True
  17. # 求和swap详情list的利润并返回
  18. @staticmethod
  19. def get_profit_sum(swap_details_list):
  20. profit_sum = 0
  21. for swap_details in swap_details_list:
  22. profit_sum += swap_details['profit']
  23. return profit_sum
  24. @staticmethod
  25. def get_max_profit_swap_details_list(x, y):
  26. return x if MaxProfitUtils.get_profit_sum(x) > MaxProfitUtils.get_profit_sum(y) else y
  27. # 得出指定数据结构中的最大利润path
  28. @staticmethod
  29. def max_profit_path_list(origin_swap_details_list, level=0, swap_details_list=None):
  30. # first level
  31. if time.time() - initbestTradeListTime>60:
  32. printTime('max_profit_path_list loong')
  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. new_swap_details_list = json.loads(json.dumps(swap_details_list))
  43. new_swap_details_list.append(origin_swap_details_list[level])
  44. x = MaxProfitUtils.max_profit_path_list(origin_swap_details_list, level+1, new_swap_details_list)
  45. # 不将此层加入的y路径
  46. y = MaxProfitUtils.max_profit_path_list(origin_swap_details_list, level+1, swap_details_list)
  47. return MaxProfitUtils.get_max_profit_swap_details_list(x, y)
  48. def calOnlyLpInRouterArray(routerArray):
  49. onlyLpArray = []
  50. for router in routerArray:
  51. for lpInfo in router:
  52. lp = lpInfo['lp']
  53. if lp in onlyLpArray:
  54. continue
  55. if lp in nowBlockLpRes:
  56. continue
  57. onlyLpArray.append(lp)
  58. return onlyLpArray
  59. def calRouterProfit(router):
  60. # 整理LP信息
  61. for lpInfo in router:
  62. lpAddress = lpInfo['lp']
  63. r0 = nowBlockLpRes[lpAddress][0]
  64. r1 = nowBlockLpRes[lpAddress][1]
  65. if lpAddress not in topLpInfo:
  66. return False
  67. lpInfo['fee'] = topLpInfo[lpAddress]['fee']
  68. if lpInfo['inToken'] < lpInfo['outToken']:
  69. lpInfo['rIn'] = r0
  70. lpInfo['rOut'] = r1
  71. else:
  72. lpInfo['rIn'] = r1
  73. lpInfo['rOut'] = r0
  74. # ABA
  75. if len(router) == 2:
  76. tokenLoan = router[1]['outToken']
  77. tokenIn0 = router[0]['inToken']
  78. tokenLoanETHPrice = baseTokenInfo[tokenLoan]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** baseTokenInfo[tokenLoan]['decimals']
  79. # WETH - A - WETH
  80. tradeInfo = calBestAmountV2V2(router[0]['rIn'], router[0]['rOut'], router[0]['fee'], router[1]['rIn'], router[1]['rOut'], router[1]['fee'] )
  81. profit = tradeInfo['profit']
  82. tradeInfo['ethProfit'] = profit * tokenLoanETHPrice
  83. #printTime(router, amountIn0, amountOut0, amountOut1, profit)
  84. if tradeInfo['ethProfit'] < tradeMinProfit:
  85. return False
  86. else:
  87. tradeInfo['lp0'] = router[0]['lp']
  88. tradeInfo['lp1'] = router[1]['lp']
  89. tradeInfo['fee0'] = router[0]['fee']
  90. tradeInfo['fee1'] = router[1]['fee']
  91. tradeInfo['indexIn0'] = router[0]['indexIn']
  92. tradeInfo['indexIn1'] = router[1]['indexIn']
  93. tradeInfo['tokenLoan'] = tokenLoan
  94. tradeInfo['tokenIn0'] = tokenIn0
  95. tradeInfo['tradeToken0'] = router[0]['outToken']
  96. tradeInfo['type'] = 'type2'
  97. return tradeInfo
  98. # ABCA
  99. elif len(router) == 3:
  100. tokenLoan = router[2]['outToken']
  101. tokenIn0 = router[0]['inToken']
  102. tokenLoanETHPrice = baseTokenInfo[tokenLoan]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** baseTokenInfo[tokenLoan]['decimals']
  103. tradeInfo = calBestAmountABCA(router[0]['rIn'], router[0]['rOut'], router[0]['fee'],
  104. router[1]['rIn'], router[1]['rOut'], router[1]['fee'],
  105. router[2]['rIn'], router[2]['rOut'], router[2]['fee'])
  106. profit = tradeInfo['profit']
  107. tradeInfo['ethProfit'] = profit * tokenLoanETHPrice
  108. #printTime(router, amountIn0, amountOut0, amountOut1, amountOut2, profit)
  109. if tradeInfo['ethProfit'] < tradeMinProfit:
  110. return False
  111. else:
  112. tradeInfo['lp0'] = router[0]['lp']
  113. tradeInfo['lp1'] = router[1]['lp']
  114. tradeInfo['lp2'] = router[2]['lp']
  115. tradeInfo['fee0'] = router[0]['fee']
  116. tradeInfo['fee1'] = router[1]['fee']
  117. tradeInfo['fee2'] = router[2]['fee']
  118. tradeInfo['indexIn0'] = router[0]['indexIn']
  119. tradeInfo['indexIn1'] = router[1]['indexIn']
  120. tradeInfo['indexIn2'] = router[2]['indexIn']
  121. tradeInfo['tokenLoan'] = tokenLoan
  122. tradeInfo['tokenIn0'] = tokenIn0
  123. tradeInfo['tradeToken0'] = router[0]['outToken']
  124. tradeInfo['tradeToken1'] = router[1]['outToken']
  125. tradeInfo['type'] = 'type3'
  126. return tradeInfo
  127. def callABCA(lp0, lp1, lpLoan, tokenIn0, tokenLoan, fee0, fee1, fee2, lp0InIndex, lp1InIndex, lp2InIndex, minProfit, block = 'latest'):
  128. function = '0x1d650c35'
  129. inputData = encodeInput(lp0, lp1, lpLoan, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  130. fee0, fee1, fee2, lp0InIndex, lp1InIndex, lp2InIndex,
  131. tokenIn0, tokenLoan, minProfit, 0)
  132. inputData = function + inputData
  133. r = gasCal(myAddress, v3CCAddress, inputData, block)
  134. tradeInfo = {'type':3, 'lp0': lp0, 'lp1': lp1, 'lpLoan':lpLoan, 'tokenIn':tokenIn0, 'tokenLoan':tokenLoan, 'profit':0, 'sumValue':0}
  135. if len(r['info']) != 23 or r['info'][-1] == 0:
  136. return tradeInfo
  137. sumValue = r['info'][3] + r['info'][4] + r['info'][5] + r['info'][6] + r['info'][7] + r['info'][8]
  138. tradeInfo['sumValue'] = sumValue
  139. tradeInfo['profit'] = r['info'][-1]
  140. tradeInfo['gasUsed'] = r['gasUsed']
  141. tradeInfo['inputData'] = inputData
  142. return tradeInfo
  143. def callABA(lp0, lp1, tokenIn0, tokenLoan, fee0, fee1, lp0InIndex, lp1InIndex, minProfit, block = 'latest'):
  144. function = '0xa628df6d'
  145. inputData = encodeInput(lp0, lp1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  146. fee0, fee1, 0, lp0InIndex, lp1InIndex, 0,
  147. tokenIn0, tokenLoan, minProfit, 0)
  148. inputData = function + inputData
  149. r = gasCal(myAddress, v3CCAddress, inputData, block)
  150. tradeInfo = {'type':2, 'lp0': lp0, 'lpLoan':lp1, 'tokenIn':tokenIn0, 'tokenLoan':tokenLoan, 'profit':0, 'sumValue':0}
  151. if len(r['info']) != 23 or r['info'][-1] == 0:
  152. return tradeInfo
  153. sumValue = r['info'][3] + r['info'][4] + r['info'][5] + r['info'][6] + r['info'][7] + r['info'][8]
  154. tradeInfo['sumValue'] = sumValue
  155. tradeInfo['profit'] = r['info'][-1]
  156. tradeInfo['gasUsed'] = r['gasUsed']
  157. tradeInfo['inputData'] = inputData
  158. return tradeInfo
  159. def readABA():
  160. array = []
  161. for i in baseTokenInfo:
  162. i = i.lower()
  163. iiArray = readText('aba' + i + i)
  164. array = array + iiArray
  165. array = array + readText('aba' + WETHW + WETH)
  166. array = array + readText('aba' + WETH + WETHW)
  167. return array
  168. def readABCA():
  169. array = []
  170. for i in baseTokenInfo:
  171. i = i.lower()
  172. iiArray = readText('abca' + i + i)
  173. array = array + iiArray
  174. array = array + readText('abca' + WETHW + WETH)
  175. array = array + readText('abca' + WETH + WETHW)
  176. return array
  177. topLpInfo = readText('topLP')
  178. lpRouterInfo = readABCA()
  179. lpRouterInfo = lpRouterInfo + readABA()
  180. printTime('v3 start')
  181. v3CCAddress = '0xB4971D0dda22359Ad86867362b7FC3206eA0d86B'
  182. dataUsed = []
  183. nowBlockLpRes = {}
  184. initBlock = w3.eth.blockNumber
  185. while True:
  186. try:
  187. if time.time() % 60 > 50 and time.time() % 60 < 55:
  188. topLpInfo = readText('topLP')
  189. lpRouterInfo = readABCA()
  190. lpRouterInfo = lpRouterInfo + readABA()
  191. blockNumber = w3.eth.blockNumber
  192. if blockNumber == initBlock:
  193. time.sleep(0.1)
  194. continue
  195. initBlock = blockNumber
  196. baseGasPrice = getBaseGasPrice(blockNumber) * 1.5
  197. if baseGasPrice < 1.6 * 1e9:
  198. baseGasPrice = 1.6 * 1e9
  199. nowBlockLpRes = {}
  200. # 找出有关该LP的路由
  201. lpRouterArray = lpRouterInfo # findLpRouter(nowBlockLpTradeI, lpRouterInfo)
  202. # 过滤需要获取余额的路由
  203. lpOnlyRouterArray = calOnlyLpInRouterArray(lpRouterArray)
  204. # printTime(len(lpOnlyRouterArray), len(lpRouterArray))
  205. # 获取余额
  206. lpResArray = w3.dex.getPairSBalance(lpOnlyRouterArray, blockNumber)
  207. # 余额进行缓存
  208. for lpResI in lpResArray:
  209. nowBlockLpRes[lpResI] = lpResArray[lpResI]
  210. #计算每一个路由的利润
  211. tradeInfoArray = []
  212. initForTime = time.time()
  213. for lpRouter in lpRouterArray:
  214. if time.time() - initForTime > 60:
  215. printTime('lpRouterArray long time')
  216. tradeInfo = calRouterProfit(lpRouter)
  217. if tradeInfo:
  218. type = tradeInfo['type']
  219. if type == 'type2':
  220. result = callABA(tradeInfo['lp0'], tradeInfo['lp1'],
  221. tradeInfo['tokenIn0'], tradeInfo['tokenLoan'],
  222. tradeInfo['fee0'], tradeInfo['fee1'],
  223. tradeInfo['indexIn0'], tradeInfo['indexIn1'],
  224. baseTokenInfo[tradeInfo['tokenIn0']]['profitMin'], blockNumber)
  225. if result['profit'] < tradeMinProfit:
  226. continue
  227. sumValue = result['sumValue']
  228. if sumValue in dataUsed:
  229. continue
  230. tokenLoanETHPrice = baseTokenInfo[tradeInfo['tokenLoan']]['price'] * 10 ** baseTokenInfo[WETH]['decimals'] / 10 ** \
  231. baseTokenInfo[tradeInfo['tokenLoan']]['decimals']
  232. result['ethProfit'] = result['profit'] * tokenLoanETHPrice
  233. result['ethProfit-E18'] = result['profit'] * tokenLoanETHPrice / 1e18
  234. result['tureProfit'] = result['ethProfit'] - result['gasUsed'] * baseGasPrice
  235. result['maxPrice'] = result['ethProfit'] / result['gasUsed']
  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 tradeInfoI['type'] == 2:
  266. tradeList.append({"swapPath": [tradeInfoI['lp0'], tradeInfoI['lpLoan']], "profit": tradeInfoI['tureProfit'], 'data':tradeInfoI})
  267. if tradeInfoI['type'] == 3:
  268. tradeList.append(
  269. {"swapPath": [tradeInfoI['lp0'], tradeInfoI['lp1'],tradeInfoI['lpLoan']], "profit": tradeInfoI['tureProfit'], 'data':tradeInfoI})
  270. newMinProfit = tradeMinProfit
  271. while len(tradeList) > 50:
  272. totalProfit = 0
  273. newMinProfit = newMinProfit * 1.1
  274. newTradeList = []
  275. for tradeI in tradeList:
  276. if tradeI['profit'] > newMinProfit:
  277. newTradeList.append(tradeI)
  278. totalProfit = totalProfit + tradeI['profit']
  279. tradeList = newTradeList
  280. initbestTradeListTime = time.time()
  281. bestTradeList = MaxProfitUtils.max_profit_path_list(tradeList)
  282. initForTime = time.time()
  283. for bestTradeI in bestTradeList:
  284. if time.time() - initForTime > 60:
  285. printTime('bestTradeList long time')
  286. printTime(blockNumber, 'bset', bestTradeI['swapPath'], bestTradeI['profit'])
  287. sumValue = bestTradeI['data']['sumValue']
  288. inputData = bestTradeI['data']['inputData']
  289. nowGasPrice = bestTradeI['data']['maxPrice'] * 0.2
  290. if nowGasPrice < baseGasPrice *1.2:
  291. nowGasPrice = baseGasPrice * 1.2
  292. if nowGasPrice > baseGasPrice * 1.8:
  293. nowGasPrice = baseGasPrice * 1.8
  294. gasLimit = 1e6 + bestTradeI['data']['maxPrice'] / 1e9 + 1
  295. if gasLimit > 1e6 + 2000:
  296. gasLimit = 1e6 + 2000
  297. params = {}
  298. params['method'] = 'send'
  299. params['params'] = {'toAddress': v3CCAddress, 'inputData': inputData,
  300. 'gasPrice': nowGasPrice, 'gasLimit': gasLimit}
  301. try:
  302. hs = requests.post(url='http://127.0.0.1:410/operation', json=params).text
  303. if len(hs)>50:
  304. printTime(blockNumber, 'https://www.oklink.com/en/ethw/tx/' + str(hs))
  305. dataUsed.append(sumValue)
  306. except:
  307. hs = 'err seed'
  308. printTime('-' * 50)
  309. except BaseException as err:
  310. printTime(traceback.format_exc())
  311. printTime('loop', )
  312. time.sleep(20)