import random from datetime import timedelta, datetime from math import log2, ceil import model from debug import debug from game import DEFAULT_ORDER_EXPIRY def place_order(ownable_id): """ places a new order according to the algorithm described in `assets/follower.py` :param ownable_id: on which ownable to place the order :return: True iff a new order was placed """ best_buy_order, cheapest_sell_order = model.abs_spread(ownable_id) if best_buy_order is None or cheapest_sell_order is None: return False investors_id = model.bank_id() ownership_id = model.get_ownership_id(ownable_id, investors_id) if debug: # the bot should only have one order model.cursor.execute(''' SELECT COUNT(*) = 0 FROM orders WHERE ownership_id = ? ''', (ownership_id,)) if not model.cursor.fetchone()[0]: raise AssertionError('The bot should no orders at this point.') amounts = model.cursor.execute(''' SELECT ordered_amount FROM orders, ownership WHERE orders.ownership_id = ownership.rowid AND ownership.ownable_id = ? ''', (ownable_id,)).fetchall() if len(amounts) < 2: raise AssertionError('We should have found at least two orders.') amounts = [random.choice(amounts)[0] for _ in range(int(ceil(log2(len(amounts)))))] amount = ceil(sum(amounts) / len(amounts)) expiry = datetime.strptime(model.current_db_time(), '%Y-%m-%d %H:%M:%S') + timedelta(minutes=DEFAULT_ORDER_EXPIRY) limit = round(random.uniform(best_buy_order, cheapest_sell_order) * 10000) / 10000 if limit - best_buy_order < cheapest_sell_order - limit: model.place_order(buy=True, ownership_id=ownership_id, limit=limit, stop_loss=False, amount=amount, expiry=expiry) else: model.place_order(buy=False, ownership_id=ownership_id, limit=limit, stop_loss=False, amount=amount, expiry=expiry) return True def notify_expired_orders(orders): for order in orders: # order_id = order[0] ownership_id = order[1] # check if that was one of the bots orders bank_ownership_id = model.get_ownership_id(model.ownable_id_by_ownership_id(ownership_id), model.bank_id()) if ownership_id != bank_ownership_id: continue # create a new order ownable_id = model.ownable_id_by_ownership_id(ownership_id) place_order(ownable_id) def notify_order_traded(ownable_id): """ Called after a trade has been done and now the auctions are finished. :param ownable_id: the ownable that was traded :return: True iff a new order was placed """ model.connect() if ownable_id == model.currency_id(): return False ownership_id = model.get_ownership_id(ownable_id, model.bank_id()) if debug: # the bot should only have one order model.cursor.execute(''' SELECT COUNT(*) <= 1 FROM orders WHERE ownership_id = ? ''', (ownership_id,)) if not model.cursor.fetchone()[0]: raise AssertionError('The bot should have at most one order.') model.cursor.execute(''' SELECT rowid, ordered_amount, expiry_dt FROM orders WHERE ownership_id = ? -- no need for ORDER since the bot should have only one order UNION ALL SELECT * FROM ( SELECT NULL, ordered_amount, expiry_dt FROM order_history WHERE ownership_id = ? ORDER BY rowid DESC -- equivalent to ordering by time created ) LIMIT 1 ''', (ownership_id, ownership_id,)) data = model.cursor.fetchall() if not data: return place_order(ownable_id) my_last_order = data[0] last_order_open = my_last_order[0] is not None last_order_id = my_last_order[0] last_amount = my_last_order[1] expiry = my_last_order[2] dt_order_placed = datetime.strptime(expiry, '%Y-%m-%d %H:%M:%S') - timedelta(minutes=DEFAULT_ORDER_EXPIRY) model.cursor.execute(''' SELECT COALESCE(SUM(amount), 0) >= 2 * ? FROM transactions WHERE ownable_id = ? AND dt > ? -- interestingly >= would be problematic ''', (last_amount, ownable_id, dt_order_placed)) if model.cursor.fetchone()[0]: if last_order_open: model.delete_order(last_order_id, 'Canceled') return place_order(ownable_id) return False def main(): """the initial part of the trading bot algorithm""" if model.get_user_orders(model.bank_id()): raise AssertionError('The trading bot already has some orders.') if input('Are you sure you want to place the initial orders? (type in "yes" or something else):') == 'yes': for ownable_id in model.ownable_ids(): if ownable_id != model.currency_id(): place_order(ownable_id) else: print('Not placing orders.') model.cleanup() if __name__ == '__main__': main()