def publish_executions(symbol, entry_type, executed_count, order = None): entry_list = [] if executed_count: entry_list.append( { "MDUpdateAction":"3", # Delete Thru "Symbol": symbol, "MDEntryType": entry_type, "MDEntryPositionNo": executed_count, }) if order: entry_list.append( { "MDUpdateAction":"1", # Update "Symbol": symbol, "MDEntryType": entry_type, "MDEntryPositionNo": 1, "MDEntryID": order.id, "MDEntryPx": order.price, "MDEntrySize": order.leaves_qty, "MDEntryDate": order.created.date(), "MDEntryTime": order.created.time(), "OrderID": order.id, "Username": order.account_username, "Broker": order.broker_username }) if entry_list: md = { "MsgType":"X", "MDBkTyp": '3', # Order Depth "MDIncGrp": entry_list } application.publish( 'MD_INCREMENTAL_' + symbol + '.' + entry_type , md )
def publish_executions(symbol, entry_type, executed_count, order=None): entry_list = [] if executed_count: entry_list.append({ "MDUpdateAction": "3", # Delete Thru "Symbol": symbol, "MDEntryType": entry_type, "MDEntryPositionNo": executed_count, }) if order: entry_list.append({ "MDUpdateAction": "1", # Update "Symbol": symbol, "MDEntryType": entry_type, "MDEntryPositionNo": 1, "MDEntryID": order.id, "MDEntryPx": order.price, "MDEntrySize": order.leaves_qty, "MDEntryDate": order.created.date(), "MDEntryTime": order.created.time(), "OrderID": order.id, "Username": order.account_username, "Broker": order.broker_username }) if entry_list: md = { "MsgType": "X", "MDBkTyp": '3', # Order Depth "MDIncGrp": entry_list } application.publish('MD_INCREMENTAL_' + symbol + '.' + entry_type, md)
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' ) application.publish(order.user_id, cancel_rpt.toJson() ) if order.user_id != order.account_id: application.publish(order.account_id, cancel_rpt.toJson() ) # market data md_entry_type = '0' if order.is_buy else '1' MarketDataPublisher.publish_cancel_order( self.symbol, md_entry_type, order_pos+1 ) return ""
def publish_cancel_order(symbol, entry_type, order_position ): md = { "MsgType":"X", "MDBkTyp": '3', # Order Depth "MDIncGrp": [{ "MDUpdateAction":"2", # Delete "Symbol": symbol, "MDEntryType": entry_type, "MDEntryPositionNo": order_position, }] } application.publish( 'MD_INCREMENTAL_' + symbol + '.' + entry_type , md )
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') application.publish(order.user_id, cancel_rpt.toJson()) if order.user_id != order.account_id: application.publish(order.account_id, cancel_rpt.toJson()) # market data md_entry_type = '0' if order.is_buy else '1' MarketDataPublisher.publish_cancel_order(self.symbol, md_entry_type, order_pos + 1) return ""
def publish_cancel_order(symbol, entry_type, order_position): md = { "MsgType": "X", "MDBkTyp": '3', # Order Depth "MDIncGrp": [{ "MDUpdateAction": "2", # Delete "Symbol": symbol, "MDEntryType": entry_type, "MDEntryPositionNo": order_position, }] } application.publish('MD_INCREMENTAL_' + symbol + '.' + entry_type, md)
def generate_md_full_refresh(session, symbol, market_depth, om, entries, req_id, timestamp): entry_list = [] for entry_type in entries: if entry_type == '0' or entry_type == '1': if entry_type == '0': # Bid orders = om.buy_side else: # Offer orders = om.sell_side entry_position = 0 for order in orders: if order.type == '1': # Hide the market orders continue entry_position += 1 entry_list.append({ "MDEntryType": entry_type, "MDEntryPositionNo": entry_position, "MDEntryID": order.id, "MDEntryPx": order.price, "MDEntrySize": order.leaves_qty, "MDEntryDate": order.created.date(), "MDEntryTime": order.created.time(), "OrderID": order.id, "Username": order.account_username, "Broker": order.broker_username }) if entry_position >= market_depth > 0: break md = { "MsgType": "W", "MDReqID": req_id, "MarketDepth": market_depth, "Symbol": symbol, "MDFullGrp": entry_list } application.publish('MD_FULL_REFRESH_' + symbol, md) return md
def generate_md_full_refresh( session, symbol, market_depth, om, entries, req_id, timestamp ): entry_list = [] for entry_type in entries: if entry_type == '0' or entry_type == '1': if entry_type == '0': # Bid orders = om.buy_side else: # Offer orders = om.sell_side entry_position = 0 for order in orders: if order.type == '1': # Hide the market orders continue entry_position += 1 entry_list.append( { "MDEntryType": entry_type, "MDEntryPositionNo": entry_position, "MDEntryID": order.id, "MDEntryPx": order.price, "MDEntrySize": order.leaves_qty, "MDEntryDate": order.created.date(), "MDEntryTime": order.created.time(), "OrderID": order.id, "Username": order.account_username, "Broker": order.broker_username }) if entry_position >= market_depth > 0: break md = { "MsgType":"W", "MDReqID": req_id, "MarketDepth": market_depth, "Symbol": symbol, "MDFullGrp": entry_list } application.publish( 'MD_FULL_REFRESH_' + symbol , md ) return md
def publish_new_order(symbol, entry_type, order_position, order ): md = { "MsgType":"X", "MDBkTyp": '3', # Order Depth "MDIncGrp": [{ "MDUpdateAction":"0", # new "Symbol": symbol, "MDEntryType": entry_type, "MDEntryPositionNo": order_position + 1, "MDEntryID": order.id, "MDEntryPx": order.price, "MDEntrySize": order.leaves_qty, "MDEntryDate": order.created.date(), "MDEntryTime": order.created.time(), "OrderID": order.id, "Username": order.account_username, "Broker": order.broker_username }] } application.publish( 'MD_INCREMENTAL_' + symbol + '.' + entry_type , md )
def publish_new_order(symbol, entry_type, order_position, order): md = { "MsgType": "X", "MDBkTyp": '3', # Order Depth "MDIncGrp": [{ "MDUpdateAction": "0", # new "Symbol": symbol, "MDEntryType": entry_type, "MDEntryPositionNo": order_position + 1, "MDEntryID": order.id, "MDEntryPx": order.price, "MDEntrySize": order.leaves_qty, "MDEntryDate": order.created.date(), "MDEntryTime": order.created.time(), "OrderID": order.id, "Username": order.account_username, "Broker": order.broker_username }] } application.publish('MD_INCREMENTAL_' + symbol + '.' + entry_type, md)
def publish_trades(symbol, trades): md_trades = [] for trade in trades: md_trades.append({ "MDUpdateAction":"0", "MDEntryType": "2", # Trade "Symbol": trade.symbol, "MDEntryPx": trade.price, "MDEntrySize": trade.size, "MDEntryDate": trade.created.date(), "MDEntryTime": trade.created.time(), "OrderID": trade.order_id, "Side": trade.side, "SecondaryOrderID": trade.counter_order_id, "TradeID": trade.id, "MDEntryBuyer": trade.buyer_username, "MDEntrySeller": trade.seller_username, }) md = { "MsgType":"X", "MDBkTyp": '3', # Order Depth "MDIncGrp": md_trades } application.publish( 'MD_TRADE_' + symbol , md )
def publish_trades(symbol, trades): md_trades = [] for trade in trades: md_trades.append({ "MDUpdateAction": "0", "MDEntryType": "2", # Trade "Symbol": trade.symbol, "MDEntryPx": trade.price, "MDEntrySize": trade.size, "MDEntryDate": trade.created.date(), "MDEntryTime": trade.created.time(), "OrderID": trade.order_id, "Side": trade.side, "SecondaryOrderID": trade.counter_order_id, "TradeID": trade.id, "MDEntryBuyer": trade.buyer_username, "MDEntrySeller": trade.seller_username, }) md = { "MsgType": "X", "MDBkTyp": '3', # Order Depth "MDIncGrp": md_trades } application.publish('MD_TRADE_' + symbol, md)
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.toJson())) if order.user_id != order.account_id: execution_reports.append((order.account_id, rpt_order.toJson())) is_last_match_a_partial_execution_on_counter_order = False execution_counter = 0 number_of_filled_counter_market_orders = 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.toJson())) if counter_order.user_id != counter_order.account_id: execution_reports.append( (counter_order.account_id, cancel_rpt_counter_order.toJson())) # 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 against the counter_order executed_qty = order.match(counter_order, order.leaves_qty) if counter_order.type == '1': # Market Order executed_price = order.price number_of_filled_counter_market_orders += 1 else: 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( session, '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.toJson())) if order.user_id != order.account_id: execution_reports.append( (order.account_id, cancel_rpt_order.toJson())) 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( session, '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.toJson())) if counter_order.user_id != counter_order.account_id: execution_reports.append( (counter_order.account_id, cancel_rpt_counter_order.toJson())) # 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) trade = Trade.create(session, order, counter_order, self.symbol, executed_qty, executed_price) trades_to_publish.append(trade) rpt_order = ExecutionReport(order, execution_side) execution_reports.append((order.user_id, rpt_order.toJson())) if order.user_id != order.account_id: execution_reports.append( (order.account_id, rpt_order.toJson())) rpt_counter_order = ExecutionReport(counter_order, execution_side) execution_reports.append( (counter_order.user_id, rpt_counter_order.toJson())) if counter_order.user_id != counter_order.account_id: execution_reports.append( (counter_order.account_id, rpt_counter_order.toJson())) def generate_email_subject_and_body(session, order, trade): from json import dumps from bitex.json_encoder import JsonEncoder from models import Currency qty_currency = order.symbol[:3] formatted_qty = Currency.format_number( session, qty_currency, order.order_qty / 1.e8) price_currency = order.symbol[3:] formatted_price = Currency.format_number( session, price_currency, order.price / 1.e8) formatted_total_price = Currency.format_number( session, price_currency, order.order_qty / 1.e8 * order.price / 1.e8) email_subject = 'E' email_template = "order-execution" email_params = { 'username': order.user.username, 'order_id': order.id, 'trade_id': trade.id, 'executed_when': trade.created, 'qty': formatted_qty, 'price': formatted_price, 'total': formatted_total_price } return email_subject, email_template, dumps( email_params, cls=JsonEncoder) email_data = generate_email_subject_and_body( session, order, trade) UserEmail.create(session=session, user_id=order.user_id, subject=email_data[0], template=email_data[1], language=options.global_email_language, params=email_data[2]) email_data = generate_email_subject_and_body( session, counter_order, trade) UserEmail.create(session=session, user_id=counter_order.user_id, subject=email_data[0], template=email_data[1], language=options.global_email_language, params=email_data[2]) # # 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.toJson())) if order.user_id != order.account_id: execution_reports.append( (order.account_id, cancel_rpt_order.toJson())) 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.toJson())) if counter_order.user_id != counter_order.account_id: execution_reports.append( (counter_order.account_id, cancel_rpt_counter_order.toJson())) 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) if order.type == '2': # Limited orders go to the book. MarketDataPublisher.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: application.publish(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] MarketDataPublisher.publish_executions( self.symbol, counter_md_entry_type, execution_counter - 1 - number_of_filled_counter_market_orders, other_side[0]) else: del other_side[0:execution_counter] MarketDataPublisher.publish_executions( self.symbol, counter_md_entry_type, execution_counter - number_of_filled_counter_market_orders) if trades_to_publish: MarketDataPublisher.publish_trades(self.symbol, trades_to_publish) return ""
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.toJson() ) ) if order.user_id != order.account_id: execution_reports.append( ( order.account_id, rpt_order.toJson() ) ) is_last_match_a_partial_execution_on_counter_order = False execution_counter = 0 number_of_filled_counter_market_orders = 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.toJson() ) ) if counter_order.user_id != counter_order.account_id: execution_reports.append( ( counter_order.account_id, cancel_rpt_counter_order.toJson() ) ) # 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 against the counter_order executed_qty = order.match( counter_order, order.leaves_qty) if counter_order.type == '1': # Market Order executed_price = order.price number_of_filled_counter_market_orders += 1 else: 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(session, '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.toJson() ) ) if order.user_id != order.account_id: execution_reports.append( ( order.account_id, cancel_rpt_order.toJson() ) ) 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(session, '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.toJson() ) ) if counter_order.user_id != counter_order.account_id: execution_reports.append( ( counter_order.account_id, cancel_rpt_counter_order.toJson() ) ) # 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 ) trade = Trade.create(session, order, counter_order, self.symbol, executed_qty, executed_price ) trades_to_publish.append(trade) rpt_order = ExecutionReport( order, execution_side ) execution_reports.append( ( order.user_id, rpt_order.toJson() ) ) if order.user_id != order.account_id: execution_reports.append( ( order.account_id, rpt_order.toJson() ) ) rpt_counter_order = ExecutionReport( counter_order, execution_side ) execution_reports.append( ( counter_order.user_id, rpt_counter_order.toJson() ) ) if counter_order.user_id != counter_order.account_id: execution_reports.append( ( counter_order.account_id, rpt_counter_order.toJson() ) ) def generate_email_subject_and_body( session, order, trade ): from json import dumps from bitex.json_encoder import JsonEncoder from models import Currency qty_currency = order.symbol[:3] formatted_qty = Currency.format_number( session, qty_currency, order.order_qty / 1.e8 ) price_currency = order.symbol[3:] formatted_price = Currency.format_number( session, price_currency, order.price / 1.e8 ) formatted_total_price = Currency.format_number( session, price_currency, order.order_qty/1.e8 * order.price / 1.e8 ) email_subject = 'E' email_template = "order-execution" email_params = { 'username': order.user.username, 'order_id': order.id, 'trade_id': trade.id, 'executed_when': trade.created, 'qty': formatted_qty, 'price': formatted_price, 'total': formatted_total_price } return email_subject, email_template, dumps(email_params, cls=JsonEncoder) email_data = generate_email_subject_and_body(session, order, trade) UserEmail.create( session = session, user_id = order.user_id, subject = email_data[0], template= email_data[1], language= options.global_email_language, params = email_data[2]) email_data = generate_email_subject_and_body(session, counter_order, trade) UserEmail.create( session = session, user_id = counter_order.user_id, subject = email_data[0], template= email_data[1], language= options.global_email_language, params = email_data[2]) # # 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.toJson() ) ) if order.user_id != order.account_id: execution_reports.append( ( order.account_id, cancel_rpt_order.toJson() ) ) 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.toJson() ) ) if counter_order.user_id != counter_order.account_id: execution_reports.append( ( counter_order.account_id, cancel_rpt_counter_order.toJson() ) ) 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 ) if order.type == '2': # Limited orders go to the book. MarketDataPublisher.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: application.publish( 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] MarketDataPublisher.publish_executions( self.symbol, counter_md_entry_type, execution_counter - 1 - number_of_filled_counter_market_orders, other_side[0] ) else: del other_side[0: execution_counter] MarketDataPublisher.publish_executions( self.symbol, counter_md_entry_type, execution_counter - number_of_filled_counter_market_orders ) if trades_to_publish: MarketDataPublisher.publish_trades(self.symbol, trades_to_publish) return ""