#!/usr/bin/env python import logging from os import getenv from argparse import ArgumentParser from datetime import datetime from decimal import Decimal from random import uniform from time import sleep from db import Ticker, Balance, Order from tradeogre import TradeOgre logging.basicConfig( level=getenv('LOGLEVEL', 'INFO'), format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) class Trader(TradeOgre): satoshi = .00000001 base_currency = 'BTC' trade_currency = 'WOW' trade_pair = f'{base_currency}-{trade_currency}' trade_amount = 250 def get_market_data(self): logging.info(f'[MARKET] Getting market data for trade pair {self.trade_pair}') res = self.get_trade_pair(self.trade_pair) spread_btc = Decimal(res['ask']) - Decimal(res['bid']) spread_sats = float(spread_btc / Decimal(self.satoshi)) spread_perc = (spread_btc / Decimal(res['ask'])) * 100 res['spread_btc'] = spread_btc res['spread_sats'] = spread_sats res['spread_perc'] = spread_perc logging.debug(res) return res def store_market_data(self): res = self.get_market_data() t = Ticker( trade_pair=self.trade_pair, initial_price=res['initialprice'], current_price=res['price'], high_price=res['high'], low_price=res['low'], volume=res['volume'], bid=res['bid'], ask=res['ask'], spread_btc=res['spread_btc'], spread_sats=res['spread_sats'], spread_perc=res['spread_perc'] ) t.save() logging.info(f'[MARKET] Stored market data as ID {t.id}') return t def store_balance(self, currency): logging.info(f'[BALANCE] Storing balance for currency {currency}') res = self.get_balance(currency) logging.debug(res) b = Balance( currency=currency, total=res['balance'], available=res['available'] ) b.save() logging.info(f'[BALANCE] Stored market data as ID {b.id}') return b def store_balances(self): for cur in self.base_currency, self.trade_currency: self.store_balance(cur) sleep(3) def get_active_orders(self): logging.info('[ORDERS] Getting active orders in local database') orders = Order.select().where(Order.active==True) logging.debug(f'Found {len(orders)} in database') return orders def reconcile_orders(self): logging.info('[ORDERS] Reconciling orders on TradeOgre with local database') to_orders = self.get_orders(self.trade_pair) for order in to_orders: if not Order.filter(Order.uuid==order['uuid']): o = Order( trade_pair=order['market'], trade_type='manual', buy=order['type'] == 'buy', quantity=float(order['quantity']), price=float(order['price']), uuid=order['uuid'], date=datetime.utcfromtimestamp(order['date']) ) o.save() logging.info(f'[ORDERS] Saved order {order["uuid"]} to the database') def update_orders(self): logging.info('[ORDERS] Updating orders in local database against TradeOgre') for order in self.get_active_orders(): logging.info(f'Checking order {order.uuid}') o = self.get_order(order.uuid) logging.info(f'Found order: {o}') if o['success'] is False: order.active = False order.save() logging.info(f'Order {order.uuid} no longer active on TradeOgre. Setting inactive.') sleep(5) def start_market_maker(self): logging.info('[MARKET MAKER] Starting market maker') latest = Ticker.select().order_by(Ticker.date.desc()).get() trade_type = 'market_maker' if len(self.get_active_orders()) > 5: logging.info('[MARKET MAKER] Too many active orders in place. Skipping.') return False if latest.spread_sats > 4: bid_amount = uniform(self.trade_amount, self.trade_amount + 30) ask_amount = uniform(bid_amount - 6, bid_amount + 6) bid_price = '{:.8f}'.format(latest.bid + self.satoshi) ask_price = '{:.8f}'.format(latest.ask - self.satoshi) logging.info(f'[MARKET MAKER] Submitting buy order for {bid_amount} at {bid_price} {self.trade_pair}') buy = self.submit_order('buy', self.trade_pair, bid_amount, bid_price) logging.debug(buy) if buy['success']: o = Order( trade_pair=self.trade_pair, trade_type=trade_type, buy=True, quantity=bid_amount, price=bid_price, uuid=buy['uuid'] ) o.save() logging.info(f'[MARKET MAKER] Stored buy order as ID {o.id}') sleep(3) logging.info(f'[MARKET MAKER] Submitting sell order for {ask_amount} at {ask_price} {self.trade_pair}') sell = self.submit_order('sell', self.trade_pair, ask_amount, ask_price) logging.debug(sell) if sell['success']: o = Order( trade_pair=self.trade_pair, trade_type=trade_type, buy=False, quantity=ask_amount, price=ask_price, uuid=sell['uuid'] ) o.save() logging.info(f'[MARKET MAKER] Stored sell order as ID {o.id}') else: logging.info(f'[MARKET MAKER] Not enough bid-ask spread ({latest.spread_sats} sats). Skipping market maker.') if __name__ == '__main__': parser = ArgumentParser(description='Helpful TradeOgre trading script') parser.add_argument('-m', '--market-maker', action='store_true', help='Put in buy/sell orders') parser.add_argument('-b', '--balances', action='store_true', help='Update coin balances of both base and currency') parser.add_argument('-u', '--update-orders', action='store_true', help='Update status of orders') args = parser.parse_args() t = Trader() orders_counter = 0 balances_counter = 0 market_maker_counter = 0 if args.market_maker: t.start_market_maker() exit() if args.update_orders: t.reconcile_orders() t.update_orders() exit() if args.balances: t.store_balances() exit() while True: try: t.store_market_data() except Exception as e: logging.info('[ERROR] Unable to store market data!', e) # update orders every 5 minutes if orders_counter == 5: try: t.update_orders() logging.info('[ORDERS] Resetting orders counter') orders_counter = 0 except Exception as e: logging.info('[ERROR] Unable to update orders!', e) # update balances every 6 minutes if balances_counter == 6: try: t.store_balances() logging.info('[BALANCE] Resetting balances counter') balances_counter = 0 except Exception as e: logging.info('[ERROR] Unable to update balances!', e) # start market makers every 2 minutes if market_maker_counter == 2: try: t.start_market_maker() logging.info('[MARKET MAKER] Resetting market maker counter') market_maker_counter = 0 except Exception as e: logging.info('[ERROR] Unable to start market maker!', e) orders_counter += 1 balances_counter += 1 market_maker_counter += 1 # sleep 1 minute sleep(60)