Exemple #1
0
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
Exemple #2
0
    def __update_orders(self):
        if not self.connected:
            return

        # filters only siis managed orders
        src_orders = self._watcher.connector.ws.open_orders("")  # "siis_")

        # first delete older orders
        order_rm_list = []
        for k, order in self._orders.items():
            found = False

            for src_order in src_orders:
                src_order_id = src_order['clOrdID'] or src_order['orderID']

                if order.order_id == src_order[
                        'clOrdID'] or order.order_id == src_order['orderID']:
                    found = True
                    break

            if not found:
                order_rm_list.append(order.order_id)

        for order_id in order_rm_list:
            del self._orders[order_id]

        # insert or update active orders
        for src_order in src_orders:
            found = False
            src_order_id = src_order['clOrdID'] or src_order['orderID']

            order = self._orders.get(src_order_id)

            if order is None:
                # insert
                order = Order(self, src_order['symbol'])
                order.set_order_id(src_order_id)

                self._orders[order.order_id] = order
            else:
                order = self._orders.get(src_order_id)

            # logger.info(src_order)

            # probably modifier or when leavesQty is update the ordStatus must change
            # if src_order['ordStatus'] != "New":
            #   continue

            # update
            order.direction = Position.LONG if src_order[
                'side'] == 'Buy' else Position.SHORT
            # 'orderQty' (ordered qty), 'cumQty' (cumulative done), 'leavesQty' (remaning)
            order.quantity = src_order.get('leavesQty',
                                           src_order.get('orderQty', 0))

            if src_order.get('transactTime'):
                order.transact_time = self._parse_datetime(
                    src_order.get('transactTime')).timestamp()

            if src_order['ordType'] == "Market":
                order.order_type = Order.ORDER_MARKET

            elif src_order['ordType'] == "Limit":
                order.order_type = Order.ORDER_LIMIT
                order.price = src_order.get('price')

            elif src_order['ordType'] == "Stop":
                order.order_type = Order.ORDER_STOP
                order.stop_price = src_order.get('stopPx')

            elif src_order['ordType'] == "StopLimit":
                order.order_type = Order.ORDER_STOP_LIMIT
                order.price = src_order.get('price')
                order.stop_price = src_order.get('stopPx')

            elif src_order['ordType'] == "MarketIfTouched":
                order.order_type = Order.ORDER_TAKE_PROFIT
                order.stop_price = src_order.get('stopPx')

            elif src_order['ordType'] == "LimitIfTouched":
                order.order_type = Order.ORDER_TAKE_PROFIT_LIMIT
                order.price = src_order.get('price')
                order.stop_price = src_order.get('stopPx')

            if src_order['timeInForce'] == 'GoodTillCancel':
                order.time_in_force = Order.TIME_IN_FORCE_GTC
            elif src_order['timeInForce'] == 'ImmediateOrCancel':
                order.time_in_force = Order.TIME_IN_FORCE_IOC
            elif src_order['timeInForce'] == 'FillOrKill':
                order.time_in_force = Order.TIME_IN_FORCE_FOK
            else:
                order.time_in_force = Order.TIME_IN_FORCE_GTC

            # triggered, ordRejReason, currency
            # @todo

            # execution options
            exec_inst = src_order['execInst'].split(',')

            # taker or maker fee
            if 'ParticipateDoNotInitiate' in exec_inst:
                order.post_only = True
            else:
                order.post_only = False

            # close reduce only
            if 'Close' in exec_inst:
                # close only order (must be used with reduce only, only reduce a position, and close opposites orders)
                order.close_only = True
            else:
                order.close_only = False

            # close reduce only
            if 'ReduceOnly' in exec_inst:
                # reduce only order (only reduce a position)
                order.reduce_only = True
            else:
                order.redeuce_only = False

            # execution price
            if 'LastPrice' in exec_inst:
                order.price_type = Order.PRICE_LAST
            elif 'IndexPrice' in exec_inst:
                order.price_type = Order.PRICE_MARK
            elif 'MarkPrice' in exec_inst:
                order.price_type = Order.PRICE_INDEX
Exemple #3
0
    def __fetch_orders(self, signals=False):
        """
        This is the synchronous REST fetching, but prefer the WS asynchronous and live one.
        Mainly used for initial fetching.
        """
        try:
            open_orders = self._watcher.connector.open_orders()
        except Exception as e:
            logger.error("__fetch_orders: %s" % repr(e))
            raise

        orders = {}

        for data in open_orders:
            market = self.market(data['symbol'])

            if data['status'] == 'NEW':  # might be...
                order = Order(self, data['symbol'])

                order.set_order_id(data['orderId'])
                order.quantity = data['origQty']
                order.executed = data['executedQty']

                order.direction = Order.LONG if data[
                    'side'] == 'BUY' else Order.SHORT

                if data['type'] == 'LIMIT':
                    order.order_type = Order.ORDER_LIMIT
                elif data['type'] == 'MARKET':
                    order.order_type = Order.ORDER_MARKET
                elif data['type'] == 'STOP_LOSS_LIMIT':
                    order.order_type = Order.ORDER_STOP_LIMIT
                    order.close_only = True
                elif data['type'] == 'TAKE_PROFIT_LIMIT':
                    order.order_type = Order.ORDER_LIMIT
                    order.close_only = True

                order.order_price = data['price']
                order.stop_loss = data['stopPrice']

                order.created_time = data['time']
                order.transact_time = data['updateTime']

                if data['timeInForce'] == 'GTC':
                    order.time_in_force = Order.TIME_IN_FORCE_GTC
                elif data['timeInForce'] == 'IOC':
                    order.time_in_force = Order.TIME_IN_FORCE_IOC
                elif data['timeInForce'] == 'FOK':
                    order.time_in_force = Order.TIME_IN_FORCE_FOK
                else:
                    order.time_in_force = Order.TIME_IN_FORCE_GTC

                # "icebergQty": "0.0"  # @todo a day when I'll be rich
                orders[order.order_id] = order

        if signals:
            # deleted (for signals if no WS)
            deleted_list = self._orders.keys() - orders.keys()
            # @todo

            # created (for signals if no WS)
            created_list = orders.keys() - self._orders.keys()
            # @todo

        self._orders = orders
Exemple #4
0
    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
Exemple #5
0
    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