Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def test_order_5(self):
"""
A regular sell to multiple people, with some stock still left over.
"""
user = User.objects.get(pk=1)
wallet = Wallet.objects.get(user=user)
wallet.shares_owned = """{"4": 95, "1": 7}"""
future = Future.objects.get(pk=4)
# Sell 93 shares at 95 each.
response = process_order(93, 95, wallet, 'sell', future)
# Someone wants to buy 75@100, 15@275
order = Order.objects.all().order_by('-pk')[0] # My sell.
order_1 = Order.objects.get(pk=11) # 15@275
order_2 = Order.objects.get(pk=5) # 75@100
self.assertEqual(order_1.filled, True)
self.assertEqual(order_2.filled, True)
self.assertEqual(order.filled, False)
self.assertEqual(order.quantity, 3)
self.assertEqual(order_1.filled_by, user)
self.assertEqual(order_2.filled_by, user)
future = Future.objects.get(pk=4)
self.assertEqual(future.last_buy, 100)
self.assertEqual(future.ask, 95) # Lowest sale order, my own
self.assertEqual(future.bid, None) # Highest buy order
self.assertEqual(future.volume, 90)
# History
history = FutureHistory.objects.filter(future=future)
self.assertEqual(history.count(), 2)
self.assertEqual(history[0].price, 275)
self.assertEqual(history[1].price, 100) # Got the highest then lowest.
def try_next_order(open_order, order, remaining_items):
transfer = {}
quantity = open_order.quantity
filled_q = remaining_items - quantity
if order.order_type == Order.ORDER_TYPE_BUY:
# If we're buying make sure we are offering at least the lowest
# sale price.
if open_order.unit_price > order.unit_price:
# We are not offering enough; break out.
return None, None
elif order.order_type == Order.ORDER_TYPE_SELL:
# If we're selling make sure that we don't sell at less than the
# highest buy price.
if open_order.unit_price < order.unit_price:
return None, None
# We can fill the order (either fully or partially!). Set up the
# points & stock transfer.
transaction = SuccessfulTransaction(future=order.future,
quantity=open_order.quantity,
unit_price=open_order.unit_price)
if order.order_type == Order.ORDER_TYPE_BUY:
# Transfer points from order.creator to open_order.creator
transfer = {'buyer': order.creator.pk,
'buyer_order': order.pk,
'seller': open_order.creator.pk,
'seller_order': open_order.pk,
def process_order(num_shares, price, wallet, order_type, future):
# Validate these parameters.
# This is a fairly "expensive" function in that it needs to acquire
# a lock. The lock ensures that orders can be submitted
# and tested against open orders of the same future atomically.
# Additionally the entire thing is wrapped in a transaction
# (using TransactionMiddleware) so that we're not in a weird state
# if something 500s or times out.
# We acquire the lock prior to order validation as nothing can change
# between the order being validated and it possibly being matched.
lonelock(Order, future.pk)
error = validate_order_params(num_shares, price, wallet, order_type,
future)
if error:
return response(error, status=400)
open_orders = Order.objects.filter(filled=False, future=future).exclude(
creator=wallet.user)
# Create an order.
order = Order(future=future, quantity=int(num_shares),
unit_price=int(price), creator=wallet.user)
if order_type == 'buy':
order.order_type = Order.ORDER_TYPE_BUY
open_orders = open_orders.filter(order_type=Order.ORDER_TYPE_SELL)
elif order_type == 'sell':
order.order_type = Order.ORDER_TYPE_SELL
# Additionally the entire thing is wrapped in a transaction
# (using TransactionMiddleware) so that we're not in a weird state
# if something 500s or times out.
# We acquire the lock prior to order validation as nothing can change
# between the order being validated and it possibly being matched.
lonelock(Order, future.pk)
error = validate_order_params(num_shares, price, wallet, order_type,
future)
if error:
return response(error, status=400)
open_orders = Order.objects.filter(filled=False, future=future).exclude(
creator=wallet.user)
# Create an order.
order = Order(future=future, quantity=int(num_shares),
unit_price=int(price), creator=wallet.user)
if order_type == 'buy':
order.order_type = Order.ORDER_TYPE_BUY
open_orders = open_orders.filter(order_type=Order.ORDER_TYPE_SELL)
elif order_type == 'sell':
order.order_type = Order.ORDER_TYPE_SELL
open_orders = open_orders.filter(order_type=Order.ORDER_TYPE_BUY)
# Save the order, then we will see if we have a match.
order.save()
freeze_assets(order, wallet)
logger.debug('*' * 20)
logger.debug(order)
# Try to execute order now, if possible.
transfers = execute_order(order, open_orders)
# Try to execute all transfers.
def execute_order(order, open_orders):
"""
Attempts to execute an order against open_orders. Note this whole
function is protected by a transaction and a lock.
Logic for buying:
- Search for lowest prices. If several orders have the same prices
then fill them out oldest first.
Logic for selling:
- Search for highest prices. If several orders have the highest price
then fill them out oldest first.
"""
if order.order_type == Order.ORDER_TYPE_BUY:
open_orders = open_orders.order_by('unit_price', 'last_modified')
elif order.order_type == Order.ORDER_TYPE_SELL:
open_orders = open_orders.order_by('-unit_price', 'last_modified')
remaining_items = order.quantity
transfers = []
logger.debug(open_orders)
for open_order in open_orders:
if remaining_items == 0:
break
transfer, remaining_items = (
try_next_order(open_order, order, remaining_items))
if not transfer:
break
transfers.append(transfer)
logger.debug(transfers)
return transfers