def cancel(self, session, order): if not order: # Generate an Order Cancel Reject - Order not found return # let's find the order position self_side = [] if order.is_buy: self_side = self.buy_side elif order.is_sell: self_side = self.sell_side order_found = False order_pos = bisect.bisect_left(self_side, order) for x in xrange( order_pos, len(self_side)): tmp_order = self_side[x] if tmp_order.id == order.id: order_found = True break if tmp_order.price != order.price: break order_pos += 1 if not order_found: # Generate an Order Cancel Reject - Order not found return # update the order order.cancel_qty( order.leaves_qty ) session.commit() # remove the order from the book self_side.pop( order_pos ) # Generate a cancel report cancel_rpt = ExecutionReport( order, '1' if order.is_buy else '2' ) execution_report_signal(order.user_id, cancel_rpt ) # market data md_entry_type = '0' if order.is_buy else '1' MdSubscriptionHelper.publish_cancel_order( self.symbol, md_entry_type, order_pos+1 )
def cancel(self, session, order): if not order: # Generate an Order Cancel Reject - Order not found return # let's find the order position self_side = [] if order.is_buy: self_side = self.buy_side elif order.is_sell: self_side = self.sell_side order_found = False order_pos = bisect.bisect_left(self_side, order) for x in xrange(order_pos, len(self_side)): tmp_order = self_side[x] if tmp_order.id == order.id: order_found = True break if tmp_order.price != order.price: break order_pos += 1 if not order_found: # Generate an Order Cancel Reject - Order not found return # update the order order.cancel_qty(order.leaves_qty) session.commit() # remove the order from the book self_side.pop(order_pos) # Generate a cancel report cancel_rpt = ExecutionReport(order, '1' if order.is_buy else '2') execution_report_signal(order.user_id, cancel_rpt) # market data md_entry_type = '0' if order.is_buy else '1' MdSubscriptionHelper.publish_cancel_order(self.symbol, md_entry_type, order_pos + 1)
def match(self, session, order): other_side = [] self_side = [] if order.is_buy: self_side = self.buy_side other_side = self.sell_side elif order.is_sell: other_side = self.buy_side self_side = self.sell_side execution_reports = [] trades_to_publish = [] execution_side = '1' if order.is_buy else '2' rpt_order = ExecutionReport( order, execution_side ) execution_reports.append( ( order.user_id, rpt_order ) ) is_last_match_a_partial_execution_on_counter_order = False execution_counter = 0 for execution_counter in xrange(0, len(other_side) + 1): if execution_counter == len(other_side): break # workaround to make the execution_counter be counted until the last order. counter_order = other_side[execution_counter] if not order.has_match(counter_order): break # check for self execution if order.account_id == counter_order.account_id: # self execution.... let's cancel the counter order counter_order.cancel_qty( counter_order.leaves_qty ) # generate a cancel report cancel_rpt_counter_order = ExecutionReport( counter_order, execution_side ) execution_reports.append( ( counter_order.user_id, cancel_rpt_counter_order ) ) # go to the next order is_last_match_a_partial_execution_on_counter_order = False continue # Get the desired executed price and qty, by matching agains the counter_order executed_qty = order.match( counter_order, order.leaves_qty) executed_price = counter_order.price # let's get the available qty to execute on the order side available_qty_on_order_side = order.get_available_qty_to_execute('1' if order.is_buy else '2', executed_qty, executed_price ) qty_to_cancel_from_order = 0 if available_qty_on_order_side < executed_qty: # ops ... looks like the order.user didn't have enough to execute the order executed_qty = available_qty_on_order_side # cancel the remaining qty qty_to_cancel_from_order = order.leaves_qty - executed_qty # check if the order got fully cancelled if not executed_qty: order.cancel_qty( qty_to_cancel_from_order ) cancel_rpt_order = ExecutionReport( order, execution_side ) execution_reports.append( ( order.user_id, cancel_rpt_order ) ) break # let's get the available qty to execute on the counter side available_qty_on_counter_side = counter_order.get_available_qty_to_execute('1' if counter_order.is_buy else '2', executed_qty, executed_price ) qty_to_cancel_from_counter_order = 0 if available_qty_on_counter_side < executed_qty: if qty_to_cancel_from_order: qty_to_cancel_from_order -= executed_qty - available_qty_on_order_side # ops ... looks like the counter_order.user didn't have enough to execute the order executed_qty = available_qty_on_counter_side # cancel the remaining qty qty_to_cancel_from_counter_order = counter_order.leaves_qty - executed_qty # check if the counter order was fully cancelled due the lack if not executed_qty: # just cancel the counter order, and go to the next order. counter_order.cancel_qty( qty_to_cancel_from_counter_order ) # generate a cancel report cancel_rpt_counter_order = ExecutionReport( counter_order, execution_side ) execution_reports.append( ( counter_order.user_id, cancel_rpt_counter_order ) ) # go to the next order is_last_match_a_partial_execution_on_counter_order = False continue # lets perform the execution if executed_qty: order.execute( executed_qty, executed_price ) counter_order.execute(executed_qty, executed_price ) # create a Trade record buyer_username = order.user.username seller_username = counter_order.user.username if order.is_sell: tmp_username = buyer_username buyer_username = seller_username seller_username = tmp_username trade = Trade( id = str(order.id) + '.' + str(counter_order.id), order_id = order.id, counter_order_id = counter_order.id, buyer_username = buyer_username, seller_username = seller_username, side = order.side, symbol = self.symbol, size = executed_qty, price = executed_price, created = datetime.datetime.now()) session.add(trade) trades_to_publish.append(trade) rpt_order = ExecutionReport( order, execution_side ) execution_reports.append( ( order.user_id, rpt_order ) ) def generate_email_subject_and_body( order, trade ): formatted_btc = u'฿ {:,.8f}'.format(order.order_qty / 1.e8) formatted_btc = formatted_btc.replace(',', '#') formatted_btc = formatted_btc.replace('.', ',') formatted_btc = formatted_btc.replace('#', '.') formatted_brl = u'R$ {:,.2f}'.format(order.order_price / 1.e5) formatted_brl = formatted_brl.replace(',', '#') formatted_brl = formatted_brl.replace('.', ',') formatted_brl = formatted_brl.replace('#', '.') formatted_total_price = u'R$ {:,.2f}'.format( order.order_qty/1.e8 * order.order_price / 1.e5) formatted_total_price = formatted_total_price.replace(',', '#') formatted_total_price = formatted_total_price.replace('.', ',') formatted_total_price = formatted_total_price.replace('#', '.') email_subject = u"Sua oferta número #%s de %s à %s foi executada!" % (order.id, formatted_btc, formatted_brl) email_body = u"""Olá %s Houve nova atividade em sua conta Bitex. Detalhes: Número da Ordem: %d Número da execução: %s Ordem Executada em: %s (UTC) Quantidade: %s Preço: %s Total %s"""% ( order.user.username, order.id, trade.id, trade.created, formatted_btc, formatted_brl, formatted_total_price ) return email_subject, email_body UserEmail.create( session = session, user_id = order.user_id, subject = generate_email_subject_and_body(order, trade)[0], body = generate_email_subject_and_body(order, trade)[1] ) rpt_counter_order = ExecutionReport( counter_order, execution_side ) execution_reports.append( ( counter_order.user_id, rpt_counter_order ) ) UserEmail.create( session = session, user_id = counter_order.user_id, subject = generate_email_subject_and_body(counter_order, trade)[0], body = generate_email_subject_and_body(counter_order, trade)[1] ) execution_reports.append( ( counter_order.user_id, rpt_counter_order ) ) # # let's do the partial cancels # # Cancel the qty from the current order if qty_to_cancel_from_order: order.cancel_qty(qty_to_cancel_from_order) # generate a cancel report cancel_rpt_order = ExecutionReport( order, execution_side ) execution_reports.append( ( order.user_id, cancel_rpt_order ) ) if qty_to_cancel_from_counter_order: counter_order.cancel_qty(qty_to_cancel_from_counter_order) # generate a cancel report cancel_rpt_counter_order = ExecutionReport( counter_order, execution_side ) execution_reports.append( ( counter_order.user_id, cancel_rpt_counter_order ) ) if counter_order.has_leaves_qty: is_last_match_a_partial_execution_on_counter_order = True md_entry_type = '0' if order.is_buy else '1' counter_md_entry_type = '1' if order.is_buy else '0' # let's include the order in the book if the order is not fully executed. if order.has_leaves_qty: insert_pos = bisect.bisect_right(self_side, order) self_side.insert( insert_pos, order ) MdSubscriptionHelper.publish_new_order( self.symbol, md_entry_type , insert_pos, order) # don't send the first execution report (NEW) if the order was fully cancelled if order.is_cancelled and order.cum_qty == 0: execution_reports.pop(0) # Publish all execution reports for user_id, execution_report in execution_reports: execution_report_signal( user_id, execution_report ) # Publish Market Data for the counter order if execution_counter: if is_last_match_a_partial_execution_on_counter_order: del other_side[0: execution_counter-1] MdSubscriptionHelper.publish_executions( self.symbol, counter_md_entry_type, execution_counter - 1, other_side[0] ) else: del other_side[0: execution_counter] MdSubscriptionHelper.publish_executions( self.symbol, counter_md_entry_type, execution_counter )
def match(self, session, order): other_side = [] self_side = [] if order.is_buy: self_side = self.buy_side other_side = self.sell_side elif order.is_sell: other_side = self.buy_side self_side = self.sell_side execution_reports = [] trades_to_publish = [] execution_side = '1' if order.is_buy else '2' rpt_order = ExecutionReport(order, execution_side) execution_reports.append((order.user_id, rpt_order)) is_last_match_a_partial_execution_on_counter_order = False execution_counter = 0 for execution_counter in xrange(0, len(other_side) + 1): if execution_counter == len(other_side): break # workaround to make the execution_counter be counted until the last order. counter_order = other_side[execution_counter] if not order.has_match(counter_order): break # check for self execution if order.account_id == counter_order.account_id: # self execution.... let's cancel the counter order counter_order.cancel_qty(counter_order.leaves_qty) # generate a cancel report cancel_rpt_counter_order = ExecutionReport( counter_order, execution_side) execution_reports.append( (counter_order.user_id, cancel_rpt_counter_order)) # go to the next order is_last_match_a_partial_execution_on_counter_order = False continue # Get the desired executed price and qty, by matching agains the counter_order executed_qty = order.match(counter_order, order.leaves_qty) executed_price = counter_order.price # let's get the available qty to execute on the order side available_qty_on_order_side = order.get_available_qty_to_execute( '1' if order.is_buy else '2', executed_qty, executed_price) qty_to_cancel_from_order = 0 if available_qty_on_order_side < executed_qty: # ops ... looks like the order.user didn't have enough to execute the order executed_qty = available_qty_on_order_side # cancel the remaining qty qty_to_cancel_from_order = order.leaves_qty - executed_qty # check if the order got fully cancelled if not executed_qty: order.cancel_qty(qty_to_cancel_from_order) cancel_rpt_order = ExecutionReport(order, execution_side) execution_reports.append((order.user_id, cancel_rpt_order)) break # let's get the available qty to execute on the counter side available_qty_on_counter_side = counter_order.get_available_qty_to_execute( '1' if counter_order.is_buy else '2', executed_qty, executed_price) qty_to_cancel_from_counter_order = 0 if available_qty_on_counter_side < executed_qty: if qty_to_cancel_from_order: qty_to_cancel_from_order -= executed_qty - available_qty_on_order_side # ops ... looks like the counter_order.user didn't have enough to execute the order executed_qty = available_qty_on_counter_side # cancel the remaining qty qty_to_cancel_from_counter_order = counter_order.leaves_qty - executed_qty # check if the counter order was fully cancelled due the lack if not executed_qty: # just cancel the counter order, and go to the next order. counter_order.cancel_qty(qty_to_cancel_from_counter_order) # generate a cancel report cancel_rpt_counter_order = ExecutionReport( counter_order, execution_side) execution_reports.append( (counter_order.user_id, cancel_rpt_counter_order)) # go to the next order is_last_match_a_partial_execution_on_counter_order = False continue # lets perform the execution if executed_qty: order.execute(executed_qty, executed_price) counter_order.execute(executed_qty, executed_price) # create a Trade record buyer_username = order.user.username seller_username = counter_order.user.username if order.is_sell: tmp_username = buyer_username buyer_username = seller_username seller_username = tmp_username trade = Trade(id=str(order.id) + '.' + str(counter_order.id), order_id=order.id, counter_order_id=counter_order.id, buyer_username=buyer_username, seller_username=seller_username, side=order.side, symbol=self.symbol, size=executed_qty, price=executed_price, created=datetime.datetime.now()) session.add(trade) trades_to_publish.append(trade) rpt_order = ExecutionReport(order, execution_side) execution_reports.append((order.user_id, rpt_order)) def generate_email_subject_and_body(order, trade): formatted_btc = u'฿ {:,.8f}'.format(order.order_qty / 1.e8) formatted_btc = formatted_btc.replace(',', '#') formatted_btc = formatted_btc.replace('.', ',') formatted_btc = formatted_btc.replace('#', '.') formatted_brl = u'R$ {:,.2f}'.format(order.order_price / 1.e5) formatted_brl = formatted_brl.replace(',', '#') formatted_brl = formatted_brl.replace('.', ',') formatted_brl = formatted_brl.replace('#', '.') formatted_total_price = u'R$ {:,.2f}'.format( order.order_qty / 1.e8 * order.order_price / 1.e5) formatted_total_price = formatted_total_price.replace( ',', '#') formatted_total_price = formatted_total_price.replace( '.', ',') formatted_total_price = formatted_total_price.replace( '#', '.') email_subject = u"Sua oferta número #%s de %s à %s foi executada!" % ( order.id, formatted_btc, formatted_brl) email_body = u"""Olá %s Houve nova atividade em sua conta Bitex. Detalhes: Número da Ordem: %d Número da execução: %s Ordem Executada em: %s (UTC) Quantidade: %s Preço: %s Total %s""" % (order.user.username, order.id, trade.id, trade.created, formatted_btc, formatted_brl, formatted_total_price) return email_subject, email_body UserEmail.create( session=session, user_id=order.user_id, subject=generate_email_subject_and_body(order, trade)[0], body=generate_email_subject_and_body(order, trade)[1]) rpt_counter_order = ExecutionReport(counter_order, execution_side) execution_reports.append( (counter_order.user_id, rpt_counter_order)) UserEmail.create(session=session, user_id=counter_order.user_id, subject=generate_email_subject_and_body( counter_order, trade)[0], body=generate_email_subject_and_body( counter_order, trade)[1]) execution_reports.append( (counter_order.user_id, rpt_counter_order)) # # let's do the partial cancels # # Cancel the qty from the current order if qty_to_cancel_from_order: order.cancel_qty(qty_to_cancel_from_order) # generate a cancel report cancel_rpt_order = ExecutionReport(order, execution_side) execution_reports.append((order.user_id, cancel_rpt_order)) if qty_to_cancel_from_counter_order: counter_order.cancel_qty(qty_to_cancel_from_counter_order) # generate a cancel report cancel_rpt_counter_order = ExecutionReport( counter_order, execution_side) execution_reports.append( (counter_order.user_id, cancel_rpt_counter_order)) if counter_order.has_leaves_qty: is_last_match_a_partial_execution_on_counter_order = True md_entry_type = '0' if order.is_buy else '1' counter_md_entry_type = '1' if order.is_buy else '0' # let's include the order in the book if the order is not fully executed. if order.has_leaves_qty: insert_pos = bisect.bisect_right(self_side, order) self_side.insert(insert_pos, order) MdSubscriptionHelper.publish_new_order(self.symbol, md_entry_type, insert_pos, order) # don't send the first execution report (NEW) if the order was fully cancelled if order.is_cancelled and order.cum_qty == 0: execution_reports.pop(0) # Publish all execution reports for user_id, execution_report in execution_reports: execution_report_signal(user_id, execution_report) # Publish Market Data for the counter order if execution_counter: if is_last_match_a_partial_execution_on_counter_order: del other_side[0:execution_counter - 1] MdSubscriptionHelper.publish_executions( self.symbol, counter_md_entry_type, execution_counter - 1, other_side[0]) else: del other_side[0:execution_counter] MdSubscriptionHelper.publish_executions( self.symbol, counter_md_entry_type, execution_counter)