from collections import defaultdict
from operator import itemgetter
from time import time

from binance.client import Client

FEE = 0.00075
PRIMARY = ['ETH', 'USDT', 'BTC', 'BNB']


def main():
    while(1):
        start_time = time()

        prices = get_prices()
        prices_time = time()
        print(f"Downloaded in: {prices_time - start_time:.4f}s")

        triangles = list(find_triangles(prices))
        print(triangles)
        print(f"Computed in: {time() - prices_time:.4f}s")

        if triangles:
            for triangle in sorted(triangles, key=itemgetter('profit'), reverse=True):
                describe_triangle(prices, triangle)
        else:
            print("No triangles found, try again!")

import os
def get_prices():
    client = Client(os.environ["BINANCE_ACCESS_KEY"], os.environ["BINANCE_ACCESS_SECRET_KEY"])
    prices = client.get_orderbook_tickers()
    prepared = defaultdict(dict)
    for ticker in prices:
        pair = ticker['symbol']
        ask = float(ticker['askPrice'])
        bid = float(ticker['bidPrice'])
        for primary in PRIMARY:
            if pair.endswith(primary) and ask > 0:
                secondary = pair[:-len(primary)]
                prepared[primary][secondary] = 1 / ask
                prepared[secondary][primary] = bid
    return prepared


def find_triangles(prices):
    triangles = []
    starting_coin = 'USDT'
    for triangle in recurse_triangle(prices, starting_coin, starting_coin):
        coins = set(triangle['coins'])
        if not any(prev_triangle == coins for prev_triangle in triangles):
            yield triangle
            triangles.append(coins)


def recurse_triangle(prices, current_coin, starting_coin, depth_left=3, amount=1.0):
    if depth_left > 0:
        pairs = prices[current_coin]
        for coin, price in pairs.items():
            new_price = (amount * price) * (1.0 - FEE)
            for triangle in recurse_triangle(prices, coin, starting_coin, depth_left - 1, new_price):
                triangle['coins'] = triangle['coins'] + [current_coin]
                yield triangle
    elif current_coin == starting_coin and amount > 1.0:
        yield {
            'coins': [current_coin],
            'profit': amount
        }


def describe_triangle(prices, triangle):
    coins = triangle['coins']
    price_percentage = (triangle['profit'] - 1.0) * 100
    print(f"{'->'.join(coins):26} {round(price_percentage, 4):-7}% <- profit!")
    for i in range(len(coins) - 1):
        first = coins[i]
        second = coins[i + 1]
        print(f"     {second:4} / {first:4}: {prices[first][second]:-17.8f}")
    print('')


if __name__ == '__main__':
    main()
