4
\$\begingroup\$

I have created a simple tool that takes in a dictionary of cryptocurrencies and finds the ones with the highest % differences between exchanges. I would be grateful if someone could help me optimize this code and perhaps make it less complicated. The structure of the code is:

for coin in data:
    for market in coin.markets()
        if conditions_are_met:
           if is_highest_price:
              high_price = new_high_price
           if is_lowest_price:
              low_price = new_low_price
    coin_differences = append([coin.name, high_price-low_price])

display_top_10(coin_differences)

This is the actual code:

#!/usr/bin/env python3

import decimal
import argparse
from operator import itemgetter

try:
    import pymarketcap
except Exception as e:
    print('Make sure to install {0} (python3 -m pip {0}.'.format(str(e).split("'")[1]))
    exit()

# arguments
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--minimum_vol', type=float, help='Minimum Percent volume per exchange to have to count', default=1)
parser.add_argument('-p', '--pairs', nargs='*', default=[], help='Pairs the coins can be arbitraged with - default=all')
parser.add_argument('-c', '--coins_shown', type=int, default=10, help='Number of coins to show')
parser.add_argument('-e', '--exchanges', nargs='*', help='Acceptable Exchanges - default=all', default=[])
parser.add_argument('-s', '--simple', help='Toggle off errors and settings', default=False, action="store_true")
parser.add_argument('-t', '--top', type=int, help='Set the number of coins to run with', default=100)
args = parser.parse_args()

# setup
cmc = pymarketcap.Pymarketcap()
info = []
count = 1
lowercase_exchanges = [x.lower() for x in args.exchanges]
all_exchanges = not bool(args.exchanges)
all_trading_pairs = not bool(args.pairs)
coin_format = '{: <25} {: >6}% {: >10} {: >15} {: <10} {: <10} {: <15} {: <5}'

if not args.simple:
    print('CURRENT SETTINGS',
          '* MINIMUM_PERCENT_VOL: {}'.format(args.minimum_vol),
          '* TRADING_PAIRS: {}'.format(args.pairs),
          '* COINS_SHOWN: {}'.format(args.coins_shown),
          '* EXCHANGES: {}'.format(lowercase_exchanges),
          '* ALL_TRADING_PAIRS: {}'.format(all_trading_pairs),
          '* ALL_EXCHANGES: {}'.format(all_exchanges),
          '* TOP: {}'.format(args.top),
          sep='\n')

# retrieve coin data
coin_data = cmc.ticker(limit=args.top)['data']

for id_, coin in coin_data.items():
    try:
        markets = cmc.markets(coin["id"])
    except Exception as e:
        markets = cmc.markets(coin["symbol"])

    best_price = 0
    best_exchange = ''
    best_pair = ''
    worst_price = 999999  # TODO is this value high enough? one day some cryptocurrencies will be worth several millions... i hope :)
    worst_exchange = ''
    worst_pair = ''
    has_markets = False
    for market in markets["markets"]:
        trades_into = market["pair"].replace(coin["symbol"],"").replace("-","").replace("/","")
        if (market['percent_volume'] >= args.minimum_vol and 
                market['updated'] and 
                (trades_into in args.pairs or all_trading_pairs) and 
                (market['source'].lower() in lowercase_exchanges or all_exchanges)
           ):
            has_markets = True
            if market['price'] >= best_price:
                best_price = market['price']
                best_exchange = market['source']
                best_pair = trades_into
            if market['price'] <= worst_price:
                worst_price = market['price']
                worst_exchange = market['source']
                worst_pair = trades_into
    if has_markets:
        info.append([coin['name'],
                     round((best_price/worst_price-1)*100,2),
                     worst_price,
                     worst_exchange,
                     worst_pair,
                     best_price,
                     best_exchange,
                     best_pair
                    ])
    elif not args.simple:
        print(coin['name'],'had no markets that fit the criteria.')

    print('[{}/{}]'.format(count, args.top),end='\r')
    count += 1

# show data
info = sorted(info, key=itemgetter(1), reverse=True)
print(coin_format.format("COIN","CHANGE","BUY PRICE","BUY AT","BUY WITH","SELL PRICE","SELL AT","SELL WITH"))
for coin in info[0:args.coins_shown]:
    print(coin_format.format(*coin))
\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review! I hope you get some good answers. \$\endgroup\$ – Phrancis May 29 '18 at 18:57
1
\$\begingroup\$

Some random advice:

Consider using ArgumentDefaultsHelpFormatter. It's a nice thing to use when you have so many defaults set.

I would lose the try/import/except. It's just as good to let the default failure take place. You can specify pip requirements in requirements.txt.

Group up your code into functions, even if they're only called once. There are many reasons for this - stack traces are improved, code folding is actually useful, and code legibility goes up.

Your explicit calls to bool() are no-ops. Python already treats everything as truthy/falsy.

Indexing [0 :x] is the same as indexing [:x].

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.