def close_position(self, position_id, market=True, limit_price=None): if not self._activity: return False position = self._positions.get(position_id) if position is None or not position.is_opened(): return False if not self.has_market(position.symbol): logger.error( "%s does not support market %s on close position %s !" % (self.name, position.symbol, position.position_id)) return False ref_order_id = "siis_" + base64.b64encode( uuid.uuid4().bytes).decode('utf8').rstrip('=\n') # keep for might be useless in this case order.set_ref_order_id(ref_order_id) order = Order(self, position.symbol) order.set_position_id(position.position_id) order.quantity = position.quantity order.direction = -position.direction # neg direction # @todo return True
def close_position(self, position_id, market_or_instrument, direction, quantity, market=True, limit_price=None): if not position_id or not market_or_instrument: return False ref_order_id = "siis_" + base64.b64encode(uuid.uuid4().bytes).decode('utf8').rstrip('=\n') # keep for might be useless in this case order.set_ref_order_id(ref_order_id) order = Order(self, market_or_instrument.market_id) order.set_position_id(position_id) order.quantity = quantity order.direction = -direction # neg direction # @todo return True
def close_position(trader, market, position, close_exec_price, order_type=Order.ORDER_LIMIT): """ Close a position. """ if not position: return False trader.lock() if not position.is_opened(): trader.unlock() return False # create an order for the close order = Order(trader, position.symbol) order.set_position_id(position.position_id) order.direction = position.close_direction() order.order_type = order_type if order_type == Order.ORDER_LIMIT: order.price = close_exec_price order.quantity = position.quantity order.leverage = position.leverage order.close_only = True order.reduce_only = True # increase or reduce the current position org_quantity = position.quantity exec_price = 0.0 # price difference depending of the direction delta_price = 0 if position.direction == Position.LONG: delta_price = close_exec_price - position.entry_price elif position.direction == Position.SHORT: delta_price = position.entry_price - close_exec_price # keep for percent calculation prev_entry_price = position.entry_price or close_exec_price leverage = order.leverage # most of thoose data rarely change except the base_exchange_rate value_per_pip = market.value_per_pip contract_size = market.contract_size lot_size = market.lot_size one_pip_means = market.one_pip_means base_exchange_rate = market.base_exchange_rate margin_factor = market.margin_factor realized_position_cost = 0.0 # realized cost of the position in base currency # effective meaning of delta price in base currency effective_price = (delta_price / one_pip_means) * value_per_pip # in base currency position_gain_loss = 0.0 # the position is closed, exact quantity in the opposite direction position_gain_loss = effective_price * position.quantity position.quantity = 0.0 position.exit_price = close_exec_price # directly executed quantity order.executed = order.quantity exec_price = close_exec_price realized_position_cost = market.effective_cost(order.quantity, close_exec_price) margin_cost = market.margin_cost(order.quantity, close_exec_price) # and decrease used margin trader.account.free_margin(margin_cost) # transaction time is current timestamp order.transact_time = trader.timestamp if position_gain_loss != 0.0 and realized_position_cost > 0.0: # ratio gain_loss_rate = position_gain_loss / realized_position_cost relative_gain_loss_rate = delta_price / prev_entry_price # if maker close (limit+post-order) (for now same as market) position.profit_loss = position_gain_loss position.profit_loss_rate = gain_loss_rate # if taker close (market) position.profit_loss_market = position_gain_loss position.profit_loss_market_rate = gain_loss_rate trader.account.add_realized_profit_loss(position_gain_loss / base_exchange_rate) # display only for debug if position_gain_loss > 0.0: Terminal.inst().high( "Close profitable position with %.2f on %s (%.2fpips) (%.2f%%) at %s" % (position_gain_loss, order.symbol, delta_price / one_pip_means, gain_loss_rate * 100.0, market.format_price(close_exec_price)), view='debug') elif position_gain_loss < 0.0: Terminal.inst().low( "Close loosing position with %.2f on %s (%.2fpips) (%.2f%%) at %s" % (position_gain_loss, order.symbol, delta_price / one_pip_means, gain_loss_rate * 100.0, market.format_price(close_exec_price)), view='debug') else: gain_loss_rate = 0.0 # unlock before notify signals trader.unlock() # # order signal (SIGNAL_ORDER_OPENED+DELETED because we assume fully completed) # order_data = { 'id': order.order_id, 'symbol': order.symbol, 'type': order.order_type, 'direction': order.direction, 'timestamp': order.created_time, 'quantity': order.quantity, 'price': order.price, 'stop-price': order.stop_price, 'stop-loss': order.stop_loss, 'take-profit': order.take_profit, 'time-in-force': order.time_in_force } # signal as watcher service (opened + fully traded qty) trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_OPENED, trader.name, (order.symbol, order_data, order.ref_order_id)) order_data = { 'id': order.order_id, 'symbol': order.symbol, 'type': order.order_type, 'trade-id': 0, 'direction': order.direction, 'timestamp': order.transact_time, 'quantity': order.quantity, 'price': order.price, 'stop-price': order.stop_price, 'exec-price': exec_price, 'avg-price': position.entry_price, 'filled': order.executed, 'cumulative-filled': order.executed, 'quote-transacted': realized_position_cost, # its margin 'stop-loss': order.stop_loss, 'take-profit': order.take_profit, 'time-in-force': order.time_in_force, 'commission-amount': 0, 'commission-asset': trader.account.currency } trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_TRADED, trader.name, (order.symbol, order_data, order.ref_order_id)) # # position signal # # closed position position_data = { 'id': position.position_id, 'symbol': position.symbol, 'direction': position.direction, 'timestamp': order.transact_time, 'quantity': 0, 'avg-entry-price': position.entry_price, 'avg-exit-price': position.exit_price, 'exec-price': exec_price, 'stop-loss': None, 'take-profit': None, 'profit-loss': position.profit_loss, 'profit-loss-currency': market.quote } trader.service.watcher_service.notify( Signal.SIGNAL_POSITION_DELETED, trader.name, (order.symbol, position_data, order.ref_order_id)) # and then deleted order trader.service.watcher_service.notify(Signal.SIGNAL_ORDER_DELETED, trader.name, (order.symbol, order.order_id, "")) position.exit(exec_price) return True
def close_position(self, position_id, market=True, limit_price=None): if not self._activity: return False position = self._positions.get(position_id) if position is None or not position.is_opened(): return False if not self.has_market(position.symbol): logger.error( "%s does not support market %s on close position %s !" % (self.name, position.symbol, position.position_id)) return False ref_order_id = "siis_" + base64.b64encode( uuid.uuid4().bytes).decode('utf8').rstrip('=\n') # keep for might be useless in this case order.set_ref_order_id(ref_order_id) order = Order(self, position.symbol) order.set_position_id(position.position_id) order.quantity = position.quantity order.direction = -position.direction # neg direction postdict = { 'symbol': order.symbol, 'clOrdID': ref_order_id, 'execInst': 'Close', # 'execInst': 'ReduceOnly,Close' # @todo why rejected with ReduceOnly ? } # short mean negative quantity if order.direction == Position.SHORT: qty = -qty # fully close (using Close and need 'side' when qty is not defined) # qty = None # order type if market: order.order_type = Order.ORDER_MARKET postdict['ordType'] = "Market" postdict['orderQty'] = qty else: order.order_type = Order.ORDER_LIMIT order.price = limit_price postdict['ordType'] = "Limit" postdict['price'] = order.price postdict['orderQty'] = qty if qty is None: postdict['side'] = "Buy" if order.direction > 0 else "Sell" try: result = self._watcher.connector.request(path="order", postdict=postdict, verb='POST', max_retries=15) except Exception as e: logger.error(str(e)) return False if result and result.get('ordRejReason'): logger.error( "%s rejected closing order %s from %s %s - cause : %s !" % (self.name, order.direction_to_str(), order.quantity, order.symbol, result['ordRejReason'])) return False # store the order with its order id order.set_order_id(result['orderID']) # and store the order self._orders[order.order_id] = order # set position closing until we get confirmation on a next update position.closing(limit_price) return True
def close_position(self, position_id, market_or_instrument, direction, quantity, market=True, limit_price=None): if not position_id or not market_or_instrument: return False trader_market = self._markets.get(market_or_instrument.market_id) if not trader_market: error_logger.error( "Trader %s refuse to close position because the market %s is not found" % (self.name, market_or_instrument.market_id)) return False result = False with self._mutex: # retrieve the position position = self._positions.get(position_id) if position and position.is_opened(): # market stop order order = Order(self, position.symbol) order.set_position_id(position_id) order.direction = position.close_direction() if limit_price: order.order_type = Order.ORDER_LIMIT order.price = limit_price else: order.order_type = Order.ORDER_MARKET order.quantity = position.quantity # fully close order.leverage = position.leverage # same as open order.close_only = True order.reduce_only = True # # price according to order type # bid_price = 0 ofr_price = 0 if order.order_type == Order.ORDER_LIMIT: bid_price = order.price ofr_price = order.price elif order.order_type == Order.ORDER_MARKET: bid_price = trader_market.bid ofr_price = trader_market.ofr # open long are executed on bid and short on ofr, close the inverse if order.direction == Position.LONG: open_exec_price = ofr_price # bid_price close_exec_price = bid_price # ofr_price elif order.direction == Position.SHORT: open_exec_price = bid_price # ofr_price close_exec_price = ofr_price # bid_price else: logger.error("Unsupported direction") return False if not open_exec_price or not close_exec_price: logger.error("No order execution price") return False if self._slippage > 0.0: # @todo deferred to update return False else: # immediate execution of the order if trader_market.has_position: # close isolated position result = close_position(self, trader_market, position, close_exec_price, Order.ORDER_MARKET) else: # close position (could be using FIFO method) result = exec_margin_order(self, order, trader_market, open_exec_price, close_exec_price) else: result = False return result
def close_position(self, position_id, market=True, limit_price=None): if not self._activity: return False result = False self.lock() position = self._positions.get(position_id) if position and position.is_opened(): # market stop order order = Order(self, position.symbol) order.set_position_id(position_id) order.direction = position.close_direction() if market and limit_price: order.order_type = Order.ORDER_LIMIT order.price = limit_price else: order.order_type = Order.ORDER_MARKET order.quantity = position.quantity # fully close order.leverage = position.leverage # same as open order.close_only = True order.reduce_only = True self.unlock() # # price according to order type # market = self.market(order.symbol) bid_price = 0 ofr_price = 0 if order.order_type == Order.ORDER_LIMIT: bid_price = order.price ofr_price = order.price elif order.order_type == Order.ORDER_MARKET: bid_price = market.bid ofr_price = market.ofr # open long are executed on bid and short on ofr, close the inverse if order.direction == Position.LONG: open_exec_price = ofr_price # bid_price close_exec_price = bid_price # ofr_price elif order.direction == Position.SHORT: open_exec_price = bid_price # ofr_price close_exec_price = ofr_price # bid_price else: logger.error("Unsupported direction") return False if not open_exec_price or not close_exec_price: logger.error("No order execution price") return False if self._slippage > 0.0: # @todo return False else: # immediate execution of the order result = exec_margin_order(self, order, market, open_exec_price, close_exec_price) else: self.unlock() result = False return result