totrader/trade.py

236 lines
9.1 KiB
Python
Raw Normal View History

2020-09-06 13:27:19 +00:00
#!/usr/bin/env python
import logging
2020-09-06 13:27:19 +00:00
from os import getenv
2020-09-09 06:34:21 +00:00
from dotenv import load_dotenv
from argparse import ArgumentParser
from datetime import datetime
2020-09-06 13:27:19 +00:00
from decimal import Decimal
from random import uniform
2020-09-06 13:27:19 +00:00
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):
2020-09-09 06:34:21 +00:00
load_dotenv('.env')
satoshi = .00000001
2020-09-09 06:34:21 +00:00
base_currency = getenv('BASE_CURRENCY', 'BTC')
trade_currency = getenv('TRADE_CURRENCY', 'WOW')
trade_pair = f'{base_currency}-{trade_currency}'
trade_amount = float(getenv('TRADE_AMOUNT', 200))
spread_target = float(getenv('SPREAD_TARGET', 4))
amount_multiplier = float(getenv('AMOUNT_MULTIPLIER', 1.2))
active_order_limit = float(getenv('ACTIVE_ORDER_LIMIT', 10))
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']
2020-09-06 13:27:19 +00:00
)
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)
2020-09-06 13:27:19 +00:00
b = Balance(
currency=currency,
total=res['balance'],
available=res['available']
2020-09-06 13:27:19 +00:00
)
b.save()
logging.info(f'[BALANCE] Stored market data as ID {b.id}')
return b
2020-09-06 13:27:19 +00:00
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, Order.trade_pair==self.trade_pair)
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(3)
def start_market_maker(self):
logging.info('[MARKET MAKER] Starting market maker')
latest = Ticker.select().where(Ticker.trade_pair==self.trade_pair).order_by(Ticker.date.desc()).get()
trade_type = 'market_maker'
active_orders = len(self.get_active_orders())
if active_orders > self.active_order_limit:
logging.info(f'[MARKET MAKER] Too many active orders in place ({active_orders}). Skipping.')
return False
2020-09-09 06:34:21 +00:00
if latest.spread_sats > self.spread_target:
bid_amount = uniform(self.trade_amount, self.trade_amount * self.amount_multiplier)
ask_amount = uniform(self.trade_amount, self.trade_amount * self.amount_multiplier)
bid_price = '{:.8f}'.format(latest.bid + self.satoshi * 100)
ask_price = '{:.8f}'.format(latest.ask - self.satoshi * 100)
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.')
2020-09-06 13:27:19 +00:00
if __name__ == '__main__':
parser = ArgumentParser(description='Helpful TradeOgre trading script')
parser.add_argument('--market-maker', action='store_true', help='Put in buy/sell orders')
parser.add_argument('--balances', action='store_true', help='Update coin balances of both base and currency')
parser.add_argument('--update-orders', action='store_true', help='Update status of orders')
parser.add_argument('--market-data', action='store_true', help='Update market data')
parser.add_argument('--run', action='store_true', help='Run continuously')
args = parser.parse_args()
t = Trader()
orders_counter = 0
balances_counter = 0
market_maker_counter = 0
2020-09-09 04:47:41 +00:00
if not args.run and args.update_orders:
t.reconcile_orders()
t.update_orders()
if not args.run and args.market_maker:
t.start_market_maker()
2020-09-09 04:47:41 +00:00
if not args.run and args.balances:
t.store_balances()
if not args.run and args.market_data:
t.store_market_data()
if args.run:
while True:
try:
t.store_market_data()
except Exception as e:
logging.info('[ERROR] Unable to store market data!', e)
# update orders every 4 minutes
if args.update_orders:
if orders_counter == 4:
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 2 minutes
if args.balances:
if balances_counter == 2:
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 3 minutes
if args.market_maker:
if market_maker_counter == 3:
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)