Skip to content

Commit

Permalink
Implement dumping of initial orders into state
Browse files Browse the repository at this point in the history
Closes: #636
  • Loading branch information
bitphage committed Aug 7, 2019
1 parent 7af08b4 commit 09cf799
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 17 deletions.
54 changes: 42 additions & 12 deletions dexbot/strategies/staggered_orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ def __init__(self, *args, **kwargs):
self.disabled = True

# Strategy variables
# Assume we are in bootstrap mode by default. This prevents weird things when bootstrap was interrupted
self.bootstrapping = True
self.market_center_price = None
self.old_center_price = None
self.buy_orders = []
Expand Down Expand Up @@ -112,6 +110,9 @@ def __init__(self, *args, **kwargs):
self.max_check_interval = 120
self.current_check_interval = self.min_check_interval

# If no bootstrap state is recorded, assume we're in bootstrap
self.get('bootstrapping', True)

if self.view:
self.update_gui_profit()
self.update_gui_slider()
Expand Down Expand Up @@ -199,7 +200,7 @@ def maintain_strategy(self, *args, **kwargs):
# Check for operational depth, buy side
if (self.virtual_buy_orders and
len(self.real_buy_orders) < self.operational_depth and
not self.bootstrapping):
not self['bootstrapping']):
"""
Note: if boostrap is on and there is nothing to allocate, this check would not work until some orders
will be filled. This means that changing `operational_depth` config param will not work immediately.
Expand All @@ -214,11 +215,14 @@ def maintain_strategy(self, *args, **kwargs):
# Check for operational depth, sell side
if (self.virtual_sell_orders and
len(self.real_sell_orders) < self.operational_depth and
not self.bootstrapping):
not self['bootstrapping']):
self.replace_virtual_order_with_real(self.virtual_sell_orders[0])
self.log_maintenance_time()
return

# Remember current boostrapping state before sending transactions
previous_bootstrap_state = self['bootstrapping']

# Prepare to bundle operations into single transaction
self.bitshares.bundle = True

Expand Down Expand Up @@ -279,8 +283,12 @@ def maintain_strategy(self, *args, **kwargs):
'balances'.format(self.min_check_interval))
self.current_check_interval = self.min_check_interval

if previous_bootstrap_state is True and self['bootstrapping'] is False:
# Bootstrap was turned off, dump initial orders
self.dump_initial_orders()

# Do not continue whether balances are changing or bootstrap is on
if (self.bootstrapping or
if (self['bootstrapping'] or
self.base_balance_history[0] != self.base_balance_history[2] or
self.quote_balance_history[0] != self.quote_balance_history[2] or
trx_executed):
Expand Down Expand Up @@ -439,6 +447,26 @@ def sync_current_orders(self):
for order in to_add_orders:
self.save_order_extended(order, custom='current')

def dump_initial_orders(self):
""" Save orders after initial placement for later use (visualization and so on)
"""
self.refresh_orders()
orders = self.buy_orders + self.sell_orders
self.log.info('Dumping initial orders into db')
# Ids should be changed to avoid ids intersection with "current" orders
for order in orders:
order['id'] = str(uuid.uuid4())
if isinstance(order, VirtualOrder):
self.save_order_extended(order, virtual=True, custom='initial')
else:
self.save_order_extended(order, virtual=False, custom='initial')

def drop_initial_orders(self):
""" Drop old "initial" orders from the db
"""
self.log.debug('Removing initial orders from the db')
self.clear_orders_extended(custom='initial')

def remove_outside_orders(self, sell_orders, buy_orders):
""" Remove orders that exceed boundaries
:param list | sell_orders: User's sell orders
Expand Down Expand Up @@ -715,14 +743,14 @@ def allocate_asset(self, asset, asset_balance):
self.replace_partially_filled_order(closest_own_order)
return

if (self.bootstrapping and
if (self['bootstrapping'] and
self.base_balance_history[2] == self.base_balance_history[0] and
self.quote_balance_history[2] == self.quote_balance_history[0] and
opposite_orders):
# Turn off bootstrap mode whether we're didn't allocated assets during previous 3 maintenance
self.log.debug('Turning bootstrapping off: actual_spread > target_spread, we have free '
'balances and cannot allocate them normally 3 times in a row')
self.bootstrapping = False
self['bootstrapping'] = False

""" Note: because we're using operations batching, there is possible a situation when we will have
both free balances and `self.actual_spread >= self.target_spread + self.increment`. In such case
Expand All @@ -736,7 +764,7 @@ def allocate_asset(self, asset, asset_balance):
# Place order closer to the center price
self.log.debug('Placing closer {} order; actual spread: {:.4%}, target + increment: {:.4%}'
.format(order_type, self.actual_spread, self.target_spread + self.increment))
if self.bootstrapping:
if self['bootstrapping']:
self.place_closer_order(asset, closest_own_order)
elif opposite_orders and self.actual_spread - self.increment < self.target_spread + self.increment:
""" Place max-sized closer order if only one order needed to reach target spread (avoid unneeded
Expand Down Expand Up @@ -819,12 +847,12 @@ def allocate_asset(self, asset, asset_balance):
(asset == 'quote' and furthest_own_order_price *
(1 + self.increment) > self.upper_bound)):
# Lower/upper bound has been reached and now will start allocating rest of the balance.
self.bootstrapping = False
self['bootstrapping'] = False
self.log.debug('Increasing sizes of {} orders'.format(order_type))
increase_finished = self.increase_order_sizes(asset, asset_balance, own_orders)
else:
# Range bound is not reached, we need to add additional orders at the extremes
self.bootstrapping = False
self['bootstrapping'] = False
self.log.debug('Placing further order than current furthest {} order'.format(order_type))
self.place_further_order(asset, furthest_own_order, allow_partial=True)
else:
Expand Down Expand Up @@ -861,8 +889,10 @@ def allocate_asset(self, asset, asset_balance):
self.refresh_orders()
self.bitshares.bundle = previous_bundle
else:
# Place first buy order as close to the lower bound as possible
self.bootstrapping = True
# Place furthest order as close to the bound as possible
if not opposite_orders:
self['bootstrapping'] = True
self.drop_initial_orders()
order = None
self.log.debug('Placing first {} order'.format(order_type))
if asset == 'base':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def test_maintain_strategy_fallback_logic(asset, mode, worker, do_initial_alloca
"""
do_initial_allocation(worker, worker.mode)
# TODO: strategy must turn off bootstrapping once target spread is reached
worker.bootstrapping = False
worker['bootstrapping'] = False

if asset == 'base':
worker.cancel_orders_wrapper(worker.buy_orders[0])
Expand Down Expand Up @@ -831,7 +831,7 @@ def test_allocate_asset_replace_partially_filled_orders(
"""
do_initial_allocation(worker, worker.mode)
# TODO: automatically turn off bootstrapping after target spread is closed?
worker.bootstrapping = False
worker['bootstrapping'] = False
additional_account = base_account()

# Partially fill closest orders
Expand Down Expand Up @@ -936,7 +936,7 @@ def test_allocate_asset_filled_orders(worker, do_initial_allocation, base_accoun
"""
do_initial_allocation(worker, worker.mode)
# TODO: automatically turn off bootstrapping after target spread is closed?
worker.bootstrapping = False
worker['bootstrapping'] = False
additional_account = base_account()
num_sell_orders_before = len(worker.sell_orders)

Expand All @@ -959,7 +959,7 @@ def test_allocate_asset_limiting_on_sell_side(mode, worker, do_initial_allocatio
"""
do_initial_allocation(worker, worker.mode)
# TODO: automatically turn off bootstrapping after target spread is closed?
worker.bootstrapping = False
worker['bootstrapping'] = False
additional_account = base_account()

# Fill several orders
Expand Down Expand Up @@ -1014,7 +1014,7 @@ def test_allocate_asset_limiting_on_buy_side(mode, worker, do_initial_allocation
worker.upper_bound = 1.4
do_initial_allocation(worker, worker.mode)
# TODO: automatically turn off bootstrapping after target spread is closed?
worker.bootstrapping = False
worker['bootstrapping'] = False
additional_account = base_account()

# Fill several orders
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,25 @@ def test_sync_current_orders(orders1):
current_all_orders = worker.buy_orders + worker.sell_orders
current_ids = set([order['id'] for order in current_all_orders])
assert fetched == current_ids


def test_dump_initial_orders(orders1):
worker = orders1
worker.dump_initial_orders()
fetched = worker.fetch_orders_extended(custom='initial')
current = worker.buy_orders + worker.sell_orders
assert len(fetched) == len(current)

# Compare orders bases to be identical (cannot compare order ids because they are different for initial orders)
bases_fetched = [entry['order']['base'] for entry in fetched]
bases_actual = [dict(order['base']) for order in current]
for element in bases_actual:
assert element in bases_fetched


def test_drop_initial_orders(orders1):
worker = orders1
worker.dump_initial_orders()
worker.drop_initial_orders()
fetched = worker.fetch_orders_extended(custom='initial')
assert len(fetched) == 0

0 comments on commit 09cf799

Please sign in to comment.