123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- 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()
|