diff --git a/totrader/factory.py b/totrader/factory.py index 326f395..78c1d0f 100644 --- a/totrader/factory.py +++ b/totrader/factory.py @@ -8,10 +8,11 @@ def create_app(): @app.before_serving async def startup(): - from totrader.routes import meta, tasks + from totrader.routes import meta, tasks, api from totrader import filters app.register_blueprint(meta.bp) app.register_blueprint(tasks.bp) + app.register_blueprint(api.bp) app.register_blueprint(filters.bp) return app \ No newline at end of file diff --git a/totrader/models.py b/totrader/models.py index 77342e1..6b3b7f4 100644 --- a/totrader/models.py +++ b/totrader/models.py @@ -51,6 +51,24 @@ class Order(Model): class Meta: database = db +class Trade(Model): + trade_pair = CharField() + trade_type = CharField() + buy = BooleanField() + quantity = DoubleField() + price = DoubleField() + date = DateTimeField() + + class Meta: + database = db + +class BitcoinPrice(Model): + price = DoubleField() + date = DateTimeField(default=datetime.utcnow) + + class Meta: + database = db + class Earning(Model): trade_pair = CharField() quantity = DoubleField() @@ -59,4 +77,4 @@ class Earning(Model): class Meta: database = db -db.create_tables([Ticker, Balance, Order, Earning]) +db.create_tables([Ticker, Balance, Order, Earning, Trade, BitcoinPrice]) diff --git a/totrader/routes/api.py b/totrader/routes/api.py index d1d8c62..f50e789 100644 --- a/totrader/routes/api.py +++ b/totrader/routes/api.py @@ -1,13 +1,38 @@ -from quart import Blueprint, current_app +from quart import Blueprint, current_app, jsonify from totrader.models import * from totrader.tasks import trader -bp = Blueprint('tasks', 'tasks', url_prefix='/api/tasks') +bp = Blueprint('api', 'api', url_prefix='/api/v1') -@bp.route('/store_ticker_data') -async def store_ticker_data(): - current_app.add_background_task(trader.store_ticker_data) - return '1' +@bp.route('/get_ticker_data') +async def get_ticker_data(): + ticker = Ticker.select().order_by(Ticker.date.desc()).limit(1).first() + return jsonify({ + 'price': ticker.current_price, + 'volume': ticker.volume, + 'bid': ticker.bid, + 'ask': ticker.ask, + 'spread_sats': ticker.spread_sats, + 'spread_btc': ticker.spread_btc, + 'spread_perc': ticker.spread_perc, + 'date': ticker.date + }) + +@bp.route('/get_balances') +async def get_balances(): + return jsonify({}) + +@bp.route('/get_bitcoin_price') +async def get_bitcoin_price(): + return jsonify({}) + +@bp.route('/get_orders') +async def get_orders(): + return jsonify({}) + +@bp.route('/get_trade_history') +async def get_trade_history(): + return jsonify({}) diff --git a/totrader/routes/tasks.py b/totrader/routes/tasks.py index d1d8c62..6a5ad9c 100644 --- a/totrader/routes/tasks.py +++ b/totrader/routes/tasks.py @@ -10,4 +10,25 @@ bp = Blueprint('tasks', 'tasks', url_prefix='/api/tasks') @bp.route('/store_ticker_data') async def store_ticker_data(): current_app.add_background_task(trader.store_ticker_data) - return '1' + return 'ok' + +@bp.route('/store_balances') +async def store_balances(): + current_app.add_background_task(trader.store_balances) + return 'ok' + +@bp.route('/store_orders') +async def store_orders(): + current_app.add_background_task(trader.reconcile_orders) + current_app.add_background_task(trader.update_orders) + return 'ok' + +@bp.route('/store_trade_history') +async def store_trade_history(): + current_app.add_background_task(trader.update_trade_history) + return 'ok' + +@bp.route('/store_bitcoin_price') +async def store_bitcoin_price(): + current_app.add_background_task(trader.update_bitcoin_price) + return 'ok' diff --git a/totrader/tasks.py b/totrader/tasks.py index 8130df8..c88dda2 100644 --- a/totrader/tasks.py +++ b/totrader/tasks.py @@ -45,8 +45,76 @@ class Trader(TradeOgre): spread_perc=res['spread_perc'] ) t.save() - logging.info(f'[MARKET] Stored market data as ID {t.id}') - return True + logging.info(f'[MARKET] Stored ticker data: {t.id}') + + def store_balances(self): + for currency in self.base_currency, self.trade_currency: + logging.info(f'[BALANCE] Storing balances 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 balances: {b.id}') + + 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.select().where(Order.uuid == order['uuid']).first(): + 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.') + + def update_trade_history(self): + logging.info('Updating trade history for the ticker') + for trade in self.get_history(self.trade_pair): + tr = Trade( + trade_pair=self.trade_pair, + trade_type=trade['type'], + buy=trade['type'] == 'buy', + quantity=float(trade['quantity']), + price=float(trade['price']), + date=datetime.utcfromtimestamp(trade['date']) + ) + tr.save() + logging.info('Trade added to the database') + + def update_bitcoin_price(self): + logging.info('Updating Bitcoin price') + r = self.get_bitcoin_price() + bp = BitcoinPrice( + price=float(r['price']) + ) + bp.save() trader = Trader() \ No newline at end of file diff --git a/totrader/templates/index.html b/totrader/templates/index.html index f88b2e3..a5cfa18 100644 --- a/totrader/templates/index.html +++ b/totrader/templates/index.html @@ -27,23 +27,83 @@ } } + // new Noty({ + // theme: 'relax', + // layout: 'topCenter', + // text: 'yo doggy', + // timeout: 4500 + // }).show(); + render() { + // fetch latest data for the UI + let updateAllData; + // trigger background tasks to store data + let storeMarketData; + let storeBalances; + let storeBitcoinPrice; + let storeOrderStatuses; + let storeTradeHistory; + + function getAllData() { + console.log('updating data'); + fetch('{{ url_for("api.get_ticker_data") }}') + .then((response) => response.json()) + .then((res) => { + console.log(res); + }); + fetch('{{ url_for("api.get_balances") }}'); + fetch('{{ url_for("api.get_bitcoin_price") }}'); + fetch('{{ url_for("api.get_orders") }}'); + fetch('{{ url_for("api.get_trade_history") }}'); + } + + if (this.state.looping) { + storeMarketData = setInterval(function() { + console.log('updating ticker data'); + fetch('{{ url_for("tasks.store_ticker_data") }}'); + }, 30000); + storeBalances = setInterval(function() { + console.log('storing balances'); + fetch('{{ url_for("tasks.store_balances") }}'); + }, 50000); + storeBitcoinPrice = setInterval(function() { + console.log('storing bitcoin price'); + fetch('{{ url_for("tasks.store_bitcoin_price") }}'); + }, 300000); + storeOrderStatuses = setInterval(function() { + console.log('storing order statuses'); + fetch('{{ url_for("tasks.store_orders") }}'); + }, 30000); + storeTradeHistory = setInterval(function() { + console.log('storing trade history'); + fetch('{{ url_for("tasks.store_trade_history") }}'); + }, 60000); + updateAllData = setInterval(getAllData(), 10000); + } + return(
market making is started
+market making is started
+ +market making is paused
+market making is stopped
+ this.setState({looping: true}); + getAllData(); + }}>Start