arbitrage.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. # Copyright (C) 2017 Alpha Griffin
  2. # @%@~LICENSE~@%@
  3. import os
  4. import sys
  5. from decimal import Decimal
  6. from time import sleep, time
  7. from datetime import datetime, timedelta
  8. from poloniex import Options, Trader
  9. import ag.logging as log
  10. # GLOBAL
  11. IS_LIVE = True
  12. FEE = Decimal('0.0025')
  13. max_volume = Decimal('0.01')
  14. thresh = 1.00001
  15. markets = ['BTC', 'USDT', 'ETH', 'XMR'] # not implemented.
  16. def scan(polo=None, db=None, alert=False):
  17. try:
  18. ticker = polo.call_ticker()
  19. #if polo is None:
  20. # ticker = __ticker__()
  21. #else:
  22. # ticker = polo.ticker()
  23. except Exception as e:
  24. print("Errors: {}".format(e))
  25. return None
  26. pairs = {}
  27. for pair, data in ticker.items():
  28. pair = pair.split('_')
  29. symbol = pair[1]
  30. against = pair[0]
  31. if symbol in pairs:
  32. values = pairs[symbol]
  33. else:
  34. values = {}
  35. pairs[symbol] = values
  36. values[against] = Decimal(data['highestBid'])
  37. if against in pairs:
  38. values = pairs[against]
  39. else:
  40. values = {}
  41. pairs[against] = values
  42. values[symbol] = 1 / Decimal(data['lowestAsk'])
  43. # print("{} / {} TICKER bid: {} ask: {}".format(symbol, against, data['highestBid'], data['lowestAsk']))
  44. pairs = pairs
  45. found = {}
  46. # print(pairs)
  47. for symbol in pairs:
  48. print("$$$ running -> {} ".format(symbol), end='\r')
  49. results = evaluate(pairs, symbol, symbol)
  50. if results is not None:
  51. log.info("Found positive arbitrage candidate!", symbol=symbol, trades=results)
  52. found[symbol] = results
  53. # if alert:
  54. # signal(Source.ARBITRAGE, "Positive arbitrage candidate: {} -> {}".format(symbol, results), symbol=symbol)
  55. # REPORT TO ANOTHER THREAD
  56. #if len(found) < 1:
  57. # log.info("No positive arbitrage candidates were found")
  58. #else:
  59. # print("successes")
  60. log.info("FINISHED")
  61. return found
  62. def evaluate(pairs, symbol, final, qty=1, step=0, msg=''):
  63. values = pairs[symbol]
  64. successes = {}
  65. for against, price in values.items():
  66. if step < 3:
  67. if against == final:
  68. continue
  69. msg += "{} -> {}\n".format(symbol, against)
  70. result = evaluate(pairs, against, final, qty * price * (1 - FEE), step+1, msg)
  71. if result is not None:
  72. successes[against] = result
  73. elif against == final:
  74. qty *= price * (1 - FEE)
  75. # print(msg)
  76. # print("final quantity:{:.2f} started with 1 | delta: {:.2f}".format(qty, qty/1*100))
  77. if (qty > thresh):
  78. return qty
  79. else:
  80. return None
  81. if len(successes) > 0:
  82. return successes
  83. else:
  84. return None
  85. def print_map(map):
  86. for coin1, map2 in map.items():
  87. for coin2, map3 in map2.items():
  88. for coin3, map4 in map3.items():
  89. for coin4, value in map4.items():
  90. if coin1 == 'BTC' or coin1 == 'USDT':
  91. print("{} -> {} -> {} -> {} -> {} | Expected: {:.5f}".format(coin1, coin2, coin3, coin4, coin1, float(value))) #, end='\r')
  92. pairs_set = [[coin1, coin2],[coin2, coin3],[coin3, coin4],[coin4, coin1]]
  93. return pairs_set
  94. def main():
  95. os.system('clear')
  96. startup_time = time()
  97. print("#"*40)
  98. print("# AG_Arber | Commandline - Python3 | 2017")
  99. print("# Start time: {}".format(datetime.now()))
  100. print("# Looking for {} Threshold".format(thresh))
  101. print("# Currently in ::LIVE:: Trading Mode" if IS_LIVE else "# Currently in ::DEMO:: Trading Mode")
  102. print("# Configuring options...", end="\r")
  103. config = Options()
  104. polo = Trader(config)
  105. print("# Getting Balances... ", end="\r")
  106. start_bal = current_bal = polo.balances()
  107. print("# Start BTC balance: {}".format(current_bal['BTC']))
  108. # print("# Start USDT balance: {}".format(current_bal['USDT']))
  109. if False:
  110. ticker = polo()
  111. else:
  112. ticker = polo.call_ticker()
  113. sleep(2)
  114. found = 0
  115. rounds = 0
  116. while True:
  117. cur_time = time()
  118. cur_runtime = timedelta(seconds=int(cur_time - startup_time))
  119. print("# Searching for: {} | Loop {} | Found {} ".format(cur_runtime, rounds, found), end='\r')
  120. sleep(2)
  121. rounds += 1
  122. chain_found = scan(polo=polo)
  123. if chain_found:
  124. #print("found a Chain ->", chain_found)
  125. trade_sequence = print_map(chain_found)
  126. current_bal = polo.balances()
  127. ticker = polo.call_ticker()
  128. chain_elapsed = 0
  129. for index, trade in enumerate(trade_sequence):
  130. start_time = time()
  131. # if live - else use a more global reference
  132. if IS_LIVE:
  133. current_bal = polo.balances()
  134. ticker = polo.call_ticker()
  135. if_buy = True
  136. # do buys first
  137. if 'USDT' in trade[0] or 'BTC' in trade[0] or 'XMR' in trade[0] or 'ETH' in trade[0]:
  138. trade_pair = "{}_{}".format(trade[0], trade[1])
  139. else:
  140. trade_pair = "{}_{}".format(trade[1], trade[0])
  141. if_buy = False
  142. # resplit the new trade pair
  143. coins = trade_pair.split('_')
  144. ## FIXME:: This is a list of exceptions that can probably be cured another way.
  145. # catch if 2 markets are both coins
  146. if 'USDT' in coins[1]:
  147. trade_pair = "{}_{}".format(coins[1], coins[0])
  148. if_buy = False
  149. # catch for stupid coin names
  150. if 'BTCD' in trade_pair:
  151. trade_pair = "BTC_BTCD"
  152. if_buy = False
  153. if 'ETH_BTC' in trade_pair:
  154. trade_pair = "BTC_ETH"
  155. if_buy = False
  156. if 'XMR_BTC' in trade_pair:
  157. trade_pair = "BTC_XMR"
  158. if_buy = False
  159. if 'XMR_BTC' in trade_pair:
  160. trade_pair = "BTC_XMR"
  161. if_buy = False
  162. if 'ETH_USDT' in trade_pair:
  163. trade_pair = "USDT_ETH"
  164. if_buy = False
  165. # if Buy or Sell
  166. if if_buy:
  167. buying_power = Decimal(current_bal[coins[0]]) # if current_bal['BTC'] <= max_volume else max_volume
  168. rate = Decimal(ticker[trade_pair]['lowestAsk'])
  169. #FIXME :: is this legit? i was trying to round to the 6th place
  170. volume = Decimal('{:.6f}00'.format(buying_power / rate))
  171. print("## Buying | {:.8f} of {} @ {:.8f} ${}".format(volume, coins[1], rate, coins[0]), end='\r')
  172. if IS_LIVE:
  173. order_num = polo.buy(rate=rate, amount=volume, coin=trade_pair)
  174. else:
  175. selling_power = Decimal(current_bal[coins[1]]) # if current_bal['BTC'] <= max_volume else max_volume
  176. rate = Decimal(ticker[trade_pair]['highestBid'])
  177. print("## Selling | {:.8f} of {} @ {:.8f} ${}".format(selling_power, coins[1], rate, coins[0]), end='\r')
  178. if IS_LIVE:
  179. order_num = polo.sell(rate=rate, amount=selling_power, coin=trade_pair)
  180. # get time and finish loop
  181. end_time = time()
  182. uptime = end_time - start_time
  183. chain_elapsed += uptime
  184. trade_time = timedelta(seconds=int(chain_elapsed))
  185. print('# Ending Trade chain {}.\nElaspsed: {}'.format(
  186. trade_sequence, trade_time
  187. ))
  188. new_bal = Decimal(polo.balances()['BTC'])
  189. net_diff = new_bal - Decimal(current_bal['BTC'])
  190. print("# New BTC balance: {}".format(new_bal))
  191. # this math is broken... #FIXME
  192. print("# round P/L: {:.3f}".format(1 - (net_diff * Decimal(current_bal['BTC'])) ))
  193. # print("# New USDT balance: {}".format(new_bal['USDT']))
  194. print('#'*45)
  195. found += 1
  196. if __name__ == '__main__':
  197. try:
  198. main()
  199. except KeyboardInterrupt as k:
  200. sys.exit('#'*25+'\nAlphagriffin.com | 2017')
  201. sys.exit('#'*25+'\nAlphagriffin.com | 2017')