|
@@ -1,154 +0,0 @@
|
|
|
-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 have 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 = model.current_db_timestamp() + 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,
|
|
|
- ioc=False)
|
|
|
- else:
|
|
|
- model.place_order(buy=False,
|
|
|
- ownership_id=ownership_id,
|
|
|
- limit=limit,
|
|
|
- stop_loss=False,
|
|
|
- amount=amount,
|
|
|
- expiry=expiry,
|
|
|
- ioc=False)
|
|
|
- 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()
|