예제 #1
0
def exec_indmargin_order(trader, order, market, open_exec_price,
                         close_exec_price):
    """
    Execute the order for indivisible margin position.
    @todo update to support only indivisible margin order
    """
    current_position = None
    positions = []

    trader.lock()

    if order.symbol:
        # in that case position is identifier by its market
        current_position = trader._positions.get(order.symbol)

    if current_position and current_position.is_opened():
        # increase or reduce the current position
        org_quantity = current_position.quantity
        exec_price = 0.0

        #
        # and adjust the position quantity (no possible hedging)
        #

        # price difference depending of the direction
        delta_price = 0
        if current_position.direction == Position.LONG:
            delta_price = close_exec_price - current_position.entry_price
            # logger.debug("cl", delta_price, " use ", close_exec_price, " other ", open_exec_price, close_exec_price < open_exec_price)
        elif current_position.direction == Position.SHORT:
            delta_price = current_position.entry_price - close_exec_price
            # logger.debug("cs", delta_price, " use ", close_exec_price, " other ", open_exec_price, close_exec_price < open_exec_price)

        # keep for percent calculation
        prev_entry_price = current_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

        # logger.debug(order.symbol, bid_price, ofr_price, open_exec_price, close_exec_price, delta_price, current_position.entry_price, order.price)
        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

        if order.direction == current_position.direction:
            # first, same direction, increase the position
            # it's what we have really buy
            realized_position_cost = order.quantity * (lot_size * contract_size
                                                       )  # in base currency

            # check available margin
            margin_cost = realized_position_cost * margin_factor / base_exchange_rate

            if not trader.has_margin(margin_cost):
                # and then rejected order
                trader.unlock()

                trader.service.watcher_service.notify(
                    Signal.SIGNAL_ORDER_REJECTED, trader.name,
                    (order.symbol, order.ref_order_id))

                logger.error(
                    "Not enought free margin for %s need %s but have %s!" %
                    (order.symbol, margin_cost, trader.account.margin_balance))
                return False

            # still in long, position size increase and adjust the entry price
            entry_price = (
                (current_position.entry_price * current_position.quantity) +
                (open_exec_price * order.quantity)) / 2
            current_position.entry_price = entry_price
            current_position.quantity += order.quantity

            # directly executed quantity
            order.executed = order.quantity
            exec_price = open_exec_price

            # increase used margin
            trader.account.add_used_margin(margin_cost)
        else:
            # different direction
            if current_position.quantity > order.quantity:
                # first case the direction still the same, reduce the position and the margin
                # take the profit/loss from the difference by order.quantity and adjust the entry price and quantity
                position_gain_loss = effective_price * order.quantity

                # it's what we have really closed
                realized_position_cost = order.quantity * (
                    lot_size * contract_size)  # in base currency

                # and decrease used margin
                trader.account.add_used_margin(-realized_position_cost *
                                               margin_factor /
                                               base_exchange_rate)

                # entry price might not move...
                # current_position.entry_price = ((current_position.entry_price * current_position.quantity) - (close_exec_price * order.quantity)) / 2
                current_position.quantity -= order.quantity
                exec_price = close_exec_price

                # directly executed quantity
                order.executed = order.quantity

            elif current_position.quantity == order.quantity:
                # second case the position is closed, exact quantity in the opposite direction

                position_gain_loss = effective_price * current_position.quantity
                current_position.quantity = 0.0

                # it's what we have really closed
                realized_position_cost = order.quantity * (
                    lot_size * contract_size)  # in base currency

                # directly executed quantity
                order.executed = order.quantity
                exec_price = close_exec_price

                # and decrease used margin
                trader.account.add_used_margin(-realized_position_cost *
                                               margin_factor /
                                               base_exchange_rate)
            else:
                # third case the position is reversed
                # 1) get the profit loss
                position_gain_loss = effective_price * current_position.quantity

                # it's what we have really closed
                realized_position_cost = order.quantity * (
                    lot_size * contract_size)  # in base currency

                # first decrease of released margin
                trader.account.add_used_margin(-realized_position_cost *
                                               margin_factor /
                                               base_exchange_rate)

                # 2) adjust the position entry
                current_position.quantity = order.quantity - current_position.quantity
                current_position.entry_price = open_exec_price

                # 3) the direction is now at opposite
                current_position.direction = order.direction

                # directly executed quantity
                order.executed = order.quantity
                exec_price = open_exec_price

                # next increase margin of the new volume
                trader.account.add_used_margin(
                    (order.quantity * lot_size * contract_size * margin_factor)
                    / base_exchange_rate)

        # transaction time is current timestamp
        order.transact_time = trader.timestamp

        #order.set_position_id(current_position.position_id)

        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)
            current_position.profit_loss = position_gain_loss
            current_position.profit_loss_rate = gain_loss_rate

            # if taker close (market)
            current_position.profit_loss_market = position_gain_loss
            current_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')

            logger.debug(
                "Account balance %.2f / Margin balance %.2f" %
                (trader.account.balance, trader.account.margin_balance))
        else:
            gain_loss_rate = 0.0

        #
        # history
        #

        # and keep for history (backtesting reporting)
        history = PaperTraderHistoryEntry(
            order, trader.account.balance, trader.account.margin_balance,
            delta_price / one_pip_means, gain_loss_rate, position_gain_loss,
            position_gain_loss / base_exchange_rate)

        trader._history.add(history)

        # unlock before notify signals
        trader.unlock()

        result = True

        #
        # 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': exec_price,  # current_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,  # @todo
            '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
        #

        # signal as watcher service
        if current_position.quantity <= 0:
            # closed position
            position_data = {
                'id': current_position.position_id,
                'symbol': current_position.symbol,
                'direction': current_position.direction,
                'timestamp': order.transact_time,
                'quantity': 0,
                'exec-price': exec_price,
                'stop-loss': None,
                'take-profit': None
            }

            trader.service.watcher_service.notify(
                Signal.SIGNAL_POSITION_DELETED, trader.name,
                (order.symbol, position_data, order.ref_order_id))
        else:
            # updated position
            position_data = {
                'id': current_position.position_id,
                'symbol': current_position.symbol,
                'direction': current_position.direction,
                'timestamp': order.transact_time,
                'quantity': current_position.quantity,
                # 'avg-price': current_position.entry_price,
                'exec-price': exec_price,
                'stop-loss': current_position.stop_loss,
                'take-profit': current_position.take_profit,
                # 'profit-loss': @todo here
            }

            trader.service.watcher_service.notify(
                Signal.SIGNAL_POSITION_UPDATED, 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, ""))

        # if position is empty -> closed -> delete it
        if current_position.quantity <= 0.0:
            # take care this does not make an issue
            current_position.exit(None)

            # dont a next update
            # trader.lock()

            # if current_position.symbol in trader._positions:
            #     del trader._positions[current_position.symbol]

            # trader.unlock()
    else:
        # unique position per market
        position_id = market.market_id

        # it's what we have really buy
        realized_position_cost = order.quantity * (
            market.lot_size * market.contract_size)  # in base currency

        # check available margin
        margin_cost = realized_position_cost * market.margin_factor / market.base_exchange_rate

        if not trader.has_margin(margin_cost):
            # and then rejected order
            trader.unlock()

            trader.service.watcher_service.notify(
                Signal.SIGNAL_ORDER_REJECTED, trader.name,
                (order.symbol, order.ref_order_id))

            logger.error(
                "Not enought free margin for %s need %s but have %s!" %
                (order.symbol, margin_cost, trader.account.margin_balance))
            return False

        # create a new position at market
        position = Position(trader)
        position.symbol = order.symbol

        position.set_position_id(position_id)
        position.set_key(trader.service.gen_key())

        position.entry(order.direction, order.symbol, order.quantity)
        position.leverage = order.leverage

        position.created_time = trader.timestamp

        account_currency = trader.account.currency

        # long are open on ofr and short on bid
        exec_price = open_exec_price
        position.entry_price = exec_price
        # logger.debug("%s %f %f %f %i" % ("el" if position.direction>0 else "es", position.entry_price, market.bid, market.ofr, market.bid < market.ofr))

        # transaction time is creation position date time
        order.transact_time = position.created_time
        order.set_position_id(position_id)

        # directly executed quantity
        order.executed = order.quantity

        trader._positions[position_id] = position

        # increase used margin
        trader.account.add_used_margin(margin_cost)

        #
        # history
        #

        history = PaperTraderHistoryEntry(order, trader.account.balance,
                                          trader.account.margin_balance)
        trader._history.add(history)

        # unlock before notify signals
        trader.unlock()

        result = True

        #
        # order signal (SIGNAL_ORDER_OPENED+TRADED+DELETED, 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': position.entry_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,  # @todo
            'commission-asset': trader.account.currency
        }

        #logger.info("%s %s %s" % (position.entry_price, position.quantity, order.direction))
        trader.service.watcher_service.notify(
            Signal.SIGNAL_ORDER_TRADED, trader.name,
            (order.symbol, order_data, order.ref_order_id))

        #
        # position signal
        #

        position_data = {
            'id': position.position_id,
            'symbol': position.symbol,
            'direction': position.direction,
            'timestamp': order.transact_time,
            'quantity': position.quantity,
            'exec-price': position.entry_price,
            'stop-loss': position.stop_loss,
            'take-profit': position.take_profit
        }

        # signal as watcher service (position opened fully completed)
        trader.service.watcher_service.notify(
            Signal.SIGNAL_POSITION_OPENED, 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, ""))

    return result
예제 #2
0
    def __fetch_positions_and_orders(self):
        #
        # get opened position and once a position occurs with our order_id create the position and make the link
        #

        # https://1broker.com/api/v2/position/open.php?token=YOUR_API_TOKEN&pretty=true
        var = {
            'pretty': 'false',
            'token': self._connector.api_key
        }

        url = self._base_url + 'position/open.php?' + urllib.parse.urlencode(var)
        self._connector._conn.request("GET", url)

        response = self._connector._conn.getresponse()
        data = response.read()

        if response.status != 200:
            Terminal.inst().error("Http error (%s) list positions on %s !" % (response.status, self.name,))
            return None

        data = json.loads(data)

        if data['error'] or data['warning']:
            Terminal.inst().error("API error list positions on %s !" % (self.name,))
            return None

        p = data['response']

        for pos in p:
            # if order_id is one of non linked order and not found in positions list
            order = self._orders.get(pos['order_id'])
            position = self._positions.get(pos['position_id'])

            copy_of = pos['copy_of']

            if order is not None and position is None:
                # an order open to a position

                # insert the new position
                position = Position(self)

                position.set_position_id(pos['position_id'])
                position.set_key(self.service.gen_key())

                # create the position
                position.set_copied_position_id(order.copied_position_id)
                position.entry(
                    Position.LONG if pos['direction'] == 'long' else Position.SHORT,
                    pos['symbol'],
                    pos['margin'],
                    float(pos['take_profit']) if pos['take_profit'] else None,
                    float(pos['stop_loss']) if pos['stop_loss'] else None,
                    float(pos['leverage']),
                    pos['trailing_stop_loss']
                )

                position.author = order.author
                position.entry_price = pos['entry_price']
                position.quantity = float(pos['value'])
                position.shared = pos['shared']
                position.profit_loss = float(pos['profit_loss'])
                position.profit_loss_rate = float(pos['profit_loss_percent']) * 0.01
                position.market_close = pos['market_close']
                position.created_time = datetime.strptime(pos['date_created'], "%Y-%m-%dT%H:%M:%SZ").timestamp() # .%fZ")

                # delete the order
                del self._orders[pos['order_id']]

                # insert the position
                self._positions[position.position_id] = position

            elif position is not None and order is None:
                # only have to update

                # update the position
                position.profit_loss = float(pos['profit_loss'])
                position.profit_loss_rate = float(pos['profit_loss_percent']) * 0.01
                position.market_close = pos['market_close']

                # can be a full update
                if not position.created_time:
                    position.entry(
                        Position.LONG if pos['direction'] == 'long' else Position.SHORT,
                        pos['symbol'],
                        pos['margin'],
                        float(pos['take_profit']) if pos['take_profit'] else None,
                        float(pos['stop_loss']) if pos['stop_loss'] else None,
                        float(pos['leverage']),
                        pos['trailing_stop_loss']
                    )

                    position.entry_price = pos['entry_price']
                    position.quantity = float(pos['value'])
                    position.shared = pos['shared']
                    position.created_time = datetime.strptime(pos['date_created'], "%Y-%m-%dT%H:%M:%SZ").timestamp() # .%fZ")

            elif order is None and position is None:
                # externaly created position or with auto copy of plateforme

                # create from copy of position (lookup for position)
                position = Position(self)

                position.set_position_id(pos['position_id'])
                position.set_key(self.service.gen_key())
                # position.set_order_id(pos['order_id'])

                # create the position
                position.set_copied_position_id(copy_of)
                position.entry(
                    Position.LONG if pos['direction'] == 'long' else Position.SHORT,
                    pos['symbol'],
                    pos['margin'],
                    pos['take_profit'],
                    pos['stop_loss'],
                    float(pos['leverage']),
                    pos['trailing_stop_loss']
                )

                # retrieve the order, the author...
                # position.author = order.author
                position.entry_price = pos['entry_price']
                position.quantity = float(pos['value'])
                position.shared = pos['shared']
                position.profit_loss = float(pos['profit_loss'])
                position.profit_loss_rate = float(pos['profit_loss_percent']) * 0.01
                position.market_close = pos['market_close']
                position.created_time = datetime.strptime(pos['date_created'], "%Y-%m-%dT%H:%M:%SZ").timestamp() # .%fZ")

                # insert the position
                self._positions[position.position_id] = position

                # retrieve the copied position id from database @todo
                logger.info("Retrieve pos %s copied from position %s" % (position.position_id, position.copied_position_id))

        #
        # check for cleared position
        #
        remove_list = []

        for k, position in self._positions.items():
            found = False

            for pos in p:
                if pos['position_id'] == position.position_id:
                    found = True
                    break

            if not found:
                # cleared but a copied trader or himself externaly
                remove_list.append(k)

        for k in remove_list:
             del self._positions[k]
예제 #3
0
    def __fetch_positions(self):
        """
        This is the synchronous REST fetching, but prefer the WS asynchronous and live one.
        Mainly used for initial fetching.
        """
        try:
            positions = self._watcher.connector.positions()
        except Exception as e:
            logger.error("fetch_market_by_epic: %s" % repr(e))
            raise

        # too this can be done by signals
        for pos_data in positions:
            pos = pos_data.get('position')
            market_data = pos_data.get('market')
            if not pos or not market_data:
                continue

            epic = market_data.get('epic', '')
            if not epic:
                continue

            #
            # update the market data first
            #

            deal_id = pos.get('dealId')

            market_update_time = datetime.strptime(market_data['updateTime'], '%H:%M:%S').timestamp() if market_data.get('updateTime') else None
            market_status = (market_data['marketStatus'] == 'TRADEABLE')
            self.on_update_market(epic, market_status, market_update_time, market_data['bid'], market_data['offer'], base_exchange_rate=None)
 
            market = self.market(epic)
            if market is None:
                continue

            #
            # related order
            #

            # retrieve the related order and remove that order or do it at order deleted signals...
            deal_ref = pos.get('dealReference')
            original_order = self._orders.get(deal_ref)

            if original_order:
                # @todo might be not necessary as a rejected order emit a signal but return rejection error at creation
                # and deal ref are not into the dict (only accepted order id)
                logger.debug("Todo original order retrieved")
                del self._orders[deal_ref]

            #
            # create or update the local position
            #

            direction = Position.LONG if pos.get('direction') == 'BUY' else Position.SHORT
            quantity = pos.get('dealSize', 0.0)

            position = self._positions.get(deal_id)
            if position is None:
                position = Position(self)

                position.set_position_id(deal_id)
                position.set_key(self.service.gen_key())
                position.leverage = 1.0 / market.margin_factor

                position.entry(direction, epic, quantity, leverage=position.leverage)

                # position are merged by epic but might be independents
                self._positions[deal_id] = position

            # account local tz, but createdDateUTC exists in API v2 (look at HTTP header v=2)
            position.created_time = datetime.strptime(pos.get('createdDate', '1970/01/01 00:00:00:000'), "%Y/%m/%d %H:%M:%S:%f").timestamp()

            position.entry_price = pos.get('openLevel', 0.0)
            position.stop_loss = pos.get('stopLevel', 0.0)
            position.take_profit = pos.get('limitLevel', 0.0)

            # garanteed stop
            if pos.get('controlledRisk', False):
                pass  # @todo

            # @todo stop type (garantee, market, trailing)
            position.trailing_stop = pos.get('trailingStep', 0.0)
            position.trailing_stop_dst = pos.get('trailingStopDistance', 0.0)

            if market:
                position.update_profit_loss(market)

        # remove empty positions, but this can be too done by DELETED signal
        rm_list = []

        for k, position in self._positions.items():
            found = False

            for pos_data in positions:
                pos = pos_data.get('position')

                if position.position_id == pos.get('dealId'):
                    found = True
                    break

            if not found:
                rm_list.append(k)

        for rm_pos in rm_list:
            del self._positions[rm_pos]
예제 #4
0
def open_position(trader, order, market, open_exec_price):
    """
    Execute the order for margin position.
    """
    current_position = None
    positions = []

    trader.lock()

    # get a new distinct position id
    position_id = "siis_" + base64.b64encode(
        uuid.uuid4().bytes).decode('utf8').rstrip('=\n')

    realized_position_cost = market.effective_cost(order.quantity,
                                                   open_exec_price)
    margin_cost = market.margin_cost(order.quantity, open_exec_price)

    if not trader._unlimited and trader.account.margin_balance < margin_cost:
        # and then rejected order
        trader.unlock()

        logger.error(
            "Not enought free margin for %s need %s but have %s!" %
            (order.symbol, margin_cost, trader.account.margin_balance))
        trader.service.watcher_service.notify(
            Signal.SIGNAL_ORDER_REJECTED, trader.name,
            (order.symbol, order.ref_order_id))

        return False

    # create a new position at market
    position = Position(trader)
    position.symbol = order.symbol

    position.set_position_id(position_id)
    position.set_key(trader.service.gen_key())

    position.entry(order.direction, order.symbol, order.quantity)
    position.leverage = order.leverage

    position.created_time = trader.timestamp

    account_currency = trader.account.currency

    # long are open on ofr and short on bid
    position.entry_price = market.open_exec_price(order.direction)

    # transaction time is creation position date time
    order.transact_time = position.created_time
    order.set_position_id(position_id)

    # directly executed quantity
    order.executed = order.quantity

    trader._positions[position_id] = position

    # increase used margin
    trader.account.use_margin(margin_cost)

    # unlock before notify signals
    trader.unlock()

    #
    # order signal (SIGNAL_ORDER_OPENED+TRADED+DELETED, 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': position.entry_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,  # @todo
        '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
    #

    position_data = {
        'id': position.position_id,
        'symbol': position.symbol,
        'direction': position.direction,
        'timestamp': order.transact_time,
        'quantity': position.quantity,
        'exec-price': position.entry_price,
        'stop-loss': position.stop_loss,
        'take-profit': position.take_profit,
        'avg-entry-price': position.entry_price,
        'profit-loss': 0.0,
        'profit-loss-currency': market.quote
    }

    # signal as watcher service (position opened fully completed)
    trader.service.watcher_service.notify(
        Signal.SIGNAL_POSITION_OPENED, 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, ""))

    return True
예제 #5
0
파일: trader.py 프로젝트: cal97g/siis
    def __update_positions(self, symbol, market):
        if not self.connected:
            return

        # position for each configured market
        for symbol, market in self._markets.items():
            pos = self._watcher.connector.ws.position(symbol)
            position = None

            if self._positions.get(symbol):
                position = self._positions.get(symbol)

            elif pos['isOpen']:
                # insert the new position
                position = Position(self)
                position.set_position_id(symbol)
                position.set_key(self.service.gen_key())

                quantity = abs(float(pos['currentQty']))
                direction = Position.SHORT if pos[
                    'currentQty'] < 0 else Position.LONG

                position.entry(direction, symbol, quantity)

                position.leverage = pos['leverage']

                position.entry_price = pos['avgEntryPrice']
                position.created_time = datetime.strptime(
                    pos['openingTimestamp'],
                    "%Y-%m-%dT%H:%M:%S.%fZ").timestamp()  # .%fZ")

                # id is symbol
                self._positions[symbol] = position

            elif (not pos['isOpen']
                  or pos['currentQty'] == 0) and self._positions.get(symbol):
                # no more position
                del self._positions[symbol]

            if position:

                # absolute value because we work with positive quantity + direction information
                position.quantity = abs(float(pos['currentQty']))
                position.direction = Position.SHORT if pos[
                    'currentQty'] < 0 else Position.LONG

                position.leverage = pos['leverage']

                # position.market_close = pos['market_close']
                position.entry_price = pos['avgEntryPrice']
                position.created_time = datetime.strptime(
                    pos['openingTimestamp'],
                    "%Y-%m-%dT%H:%M:%S.%fZ").timestamp()  # .%fZ")
예제 #6
0
def exec_margin_order(trader, order, market, open_exec_price, close_exec_price):
    """
    Execute the order for margin position.
    @todo support of hedging else reduce first the opposite direction positions (FIFO method)
    """
    current_position = None
    positions = []

    trader.lock()

    if order.position_id:
        current_position = trader._positions.get(order.position_id)
    else:
        # @todo
        pass
        # # position of the same market on any directions
        # for k, pos in trader._positions.items():
        #     if pos.symbol == order.symbol:
        #         positions.append(pos)

        # if order.hedging and market.hedging:
        #     pass
        # else:
        #     current_position = positions[-1] if positions else None

    if current_position and current_position.is_opened():
        # increase or reduce the current position
        org_quantity = current_position.quantity
        exec_price = 0.0

        #
        # and adjust the position quantity (no hedging)
        #

        # price difference depending of the direction
        delta_price = 0
        if current_position.direction == Position.LONG:
            delta_price = close_exec_price - current_position.entry_price
            # logger.debug("cl", delta_price, " use ", close_exec_price, " other ", open_exec_price, close_exec_price < open_exec_price)
        elif current_position.direction == Position.SHORT:
            delta_price = current_position.entry_price - close_exec_price
            # logger.debug("cs", delta_price, " use ", close_exec_price, " other ", open_exec_price, close_exec_price < open_exec_price)

        # keep for percent calculation
        prev_entry_price = current_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

        # logger.debug(order.symbol, bid_price, ofr_price, open_exec_price, close_exec_price, delta_price, current_position.entry_price, order.price)
        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

        if order.direction == current_position.direction:
            # first, same direction, increase the position
            realized_position_cost = market.effective_cost(order.quantity, open_exec_price)
            margin_cost = market.margin_cost(order.quantity, open_exec_price)

            if not trader._unlimited and trader.account.margin_balance < margin_cost:
                # and then rejected order
                trader.unlock()

                trader.service.watcher_service.notify(Signal.SIGNAL_ORDER_REJECTED, trader.name, (order.symbol, order.ref_order_id))

                logger.error("Not enought free margin for %s need %s but have %s!" % (order.symbol, margin_cost, trader.account.margin_balance))
                return False

            # still in long, position size increase and adjust the entry price
            entry_price = ((current_position.entry_price * current_position.quantity) + (open_exec_price * order.quantity)) / 2
            current_position.entry_price = entry_price
            current_position.quantity += order.quantity

            # directly executed quantity
            order.executed = order.quantity
            exec_price = open_exec_price

            # increase used margin
            trader.account.use_margin(margin_cost)
        else:
            # different direction
            if current_position.quantity > order.quantity:
                # first case the direction still the same, reduce the position and the margin
                # take the profit/loss from the difference by order.quantity and adjust the entry price and quantity
                position_gain_loss = effective_price * order.quantity

                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)

                # entry price might not move...
                # current_position.entry_price = ((current_position.entry_price * current_position.quantity) - (close_exec_price * order.quantity)) / 2
                current_position.quantity -= order.quantity
                current_position.exit_price = close_exec_price

                exec_price = close_exec_price

                # directly executed quantity
                order.executed = order.quantity

            elif current_position.quantity == order.quantity:
                # second case the position is closed, exact quantity in the opposite direction
                position_gain_loss = effective_price * current_position.quantity

                current_position.quantity = 0.0
                current_position.exit_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)

                # directly executed quantity
                order.executed = order.quantity
                exec_price = close_exec_price

                # and decrease used margin
                trader.account.free_margin(margin_cost)
            else:
                # third case the position is reversed
                # 1) get the profit loss
                position_gain_loss = effective_price * current_position.quantity

                realized_position_cost = market.effective_cost(order.quantity, close_exec_price)
                margin_cost = market.margin_cost(order.quantity, close_exec_price)

                # first decrease of released margin
                trader.account.free_margin(margin_cost)

                # 2) adjust the position entry
                current_position.quantity = order.quantity - current_position.quantity
                current_position.entry_price = open_exec_price

                # 3) the direction is now at opposite
                current_position.direction = order.direction

                # directly executed quantity
                order.executed = order.quantity
                exec_price = open_exec_price

                margin_cost = market.margin_cost(order.quantity-current_position.quantity, open_exec_price)

                # next increase margin of the new volume
                trader.account.use_margin(margin_cost)

        # transaction time is current timestamp
        order.transact_time = trader.timestamp

        #order.set_position_id(current_position.position_id)

        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)
            current_position.profit_loss = position_gain_loss
            current_position.profit_loss_rate = gain_loss_rate

            # if taker close (market)
            current_position.profit_loss_market = position_gain_loss
            current_position.profit_loss_market_rate = gain_loss_rate

            trader.account.add_realized_profit_loss(position_gain_loss / base_exchange_rate)
        else:
            gain_loss_rate = 0.0

        # unlock before notify signals
        trader.unlock()

        result = True

        #
        # 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': current_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,  # @todo
            '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
        #

        # signal as watcher service
        if current_position.quantity <= 0:
            # closed position
            position_data = {
                'id': current_position.position_id,
                'symbol': current_position.symbol,
                'direction': current_position.direction,
                'timestamp': order.transact_time,
                'quantity': 0,
                'avg-entry-price': current_position.entry_price,
                'avg-exit-price': current_position.exit_price,
                'exec-price': exec_price,
                'stop-loss': None,
                'take-profit': None,
                'profit-loss': current_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))
        else:
            # updated position
            position_data = {
                'id': current_position.position_id,
                'symbol': current_position.symbol,
                'direction': current_position.direction,
                'timestamp': order.transact_time,
                'quantity': current_position.quantity,
                'avg-entry-price': current_position.entry_price,
                'avg-exit-price': current_position.exit_price,
                'exec-price': exec_price,
                'stop-loss': current_position.stop_loss,
                'take-profit': current_position.take_profit,
                'profit-loss': current_position.profit_loss,
                'profit-loss-currency': market.quote
            }

            trader.service.watcher_service.notify(Signal.SIGNAL_POSITION_UPDATED, 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, ""))

        # if position is empty -> closed -> delete it
        if current_position.quantity <= 0.0:
            current_position.exit(None)

            # done during next update
            # trader.lock()

            # if current_position.position_id in trader._positions:
            #     del trader._positions[current_position.position_id]

            # trader.unlock()
    else:
        # get a new distinct position id
        position_id = "siis_" + base64.b64encode(uuid.uuid4().bytes).decode('utf8').rstrip('=\n')

        realized_position_cost = market.effective_cost(order.quantity, open_exec_price)
        margin_cost = market.margin_cost(order.quantity, open_exec_price)

        if not trader._unlimited and trader.account.margin_balance < margin_cost:
            # and then rejected order
            trader.unlock()

            trader.service.watcher_service.notify(Signal.SIGNAL_ORDER_REJECTED, trader.name, (order.symbol, order.ref_order_id))

            logger.error("Not enought free margin for %s need %s but have %s!" % (order.symbol, margin_cost, trader.account.margin_balance))
            return False

        # create a new position at market
        position = Position(trader)
        position.symbol = order.symbol

        position.set_position_id(position_id)
        position.set_key(trader.service.gen_key())

        position.entry(order.direction, order.symbol, order.quantity)
        position.leverage = order.leverage

        position.created_time = trader.timestamp

        account_currency = trader.account.currency

        # long are open on ofr and short on bid
        position.entry_price = market.open_exec_price(order.direction)
        # logger.debug("%s %f %f %f %i" % ("el" if position.direction>0 else "es", position.entry_price, market.bid, market.ofr, market.bid < market.ofr))

        # transaction time is creation position date time
        order.transact_time = position.created_time
        order.set_position_id(position_id)

        # directly executed quantity
        order.executed = order.quantity

        trader._positions[position_id] = position

        # increase used margin
        trader.account.use_margin(margin_cost)

        # unlock before notify signals
        trader.unlock()

        result = True

        #
        # order signal (SIGNAL_ORDER_OPENED+TRADED+DELETED, 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': position.entry_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,  # @todo
            'commission-asset': trader.account.currency
        }

        #logger.info("%s %s %s" % (position.entry_price, position.quantity, order.direction))
        trader.service.watcher_service.notify(Signal.SIGNAL_ORDER_TRADED, trader.name, (order.symbol, order_data, order.ref_order_id))

        #
        # position signal
        #

        position_data = {
            'id': position.position_id,
            'symbol': position.symbol,
            'direction': position.direction,
            'timestamp': order.transact_time,
            'quantity': position.quantity,
            'exec-price': position.entry_price,
            'stop-loss': position.stop_loss,
            'take-profit': position.take_profit,
            'avg-entry-price': position.entry_price,
            'profit-loss': 0.0,
            'profit-loss-currency': market.quote
        }

        # signal as watcher service (position opened fully completed)
        trader.service.watcher_service.notify(Signal.SIGNAL_POSITION_OPENED, 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, ""))

    return result
예제 #7
0
    def __update_positions(self, symbol, market):
        # position for each configured market
        for symbol, market in self._markets.items():
            pos = self._watcher.connector.ws.position(symbol)
            position = None

            if self._positions.get(symbol):
                position = self._positions.get(symbol)

            elif pos['isOpen']:
                # insert the new position
                position = Position(self)
                position.set_position_id(symbol)
                position.set_key(self.service.gen_key())

                quantity = abs(float(pos['currentQty']))
                direction = Position.SHORT if pos['currentQty'] < 0 else Position.LONG

                position.entry(direction, symbol, quantity)

                position.leverage = pos['leverage']

                position.entry_price = pos['avgEntryPrice']
                position.created_time = datetime.strptime(pos['openingTimestamp'], "%Y-%m-%dT%H:%M:%S.%fZ").timestamp()  # .%fZ")

                # id is symbol
                self._positions[symbol] = position

            elif (not pos['isOpen'] or pos['currentQty'] == 0) and self._positions.get(symbol):
                # no more position
                del self._positions[symbol]

            if position:

                # absolute value because we work with positive quantity + direction information
                position.quantity = abs(float(pos['currentQty']))
                position.direction = Position.SHORT if pos['currentQty'] < 0 else Position.LONG

                position.leverage = pos['leverage']

                # position.market_close = pos['market_close']
                position.entry_price = pos['avgEntryPrice']
                position.created_time = datetime.strptime(pos['openingTimestamp'], "%Y-%m-%dT%H:%M:%S.%fZ").timestamp()  # .%fZ")

                # XBt to XBT
                # ratio = 1.0
                # if pos['currency'] == 'XBt':
                #   ratio = 1.0 / 100000000.0

                # don't want them because they are in XBt or XBT
                # position.profit_loss = (float(pos['unrealisedPnl']) * ratio)
                # position.profit_loss_rate = float(pos['unrealisedPnlPcnt'])

                # # must be updated using the market taker fee
                # position.profit_loss_market = (float(pos['unrealisedPnl']) * ratio)
                # position.profit_loss_market_rate = float(pos['unrealisedPnlPcnt'])

                # compute profit loss in base currency
                position.update_profit_loss(market)
예제 #8
0
    def __update_positions(self, symbol, market):
        if not self.connected:
            return

        # position for each configured market
        for symbol, market in self._markets.items():
            pos = self._watcher.connector.ws.position(symbol)
            position = None

            if self._positions.get(symbol):
                position = self._positions.get(symbol)

            elif pos['isOpen']:
                # insert the new position
                position = Position(self)
                position.set_position_id(symbol)
                position.set_key(self.service.gen_key())

                quantity = abs(float(pos['currentQty']))
                direction = Position.SHORT if pos['currentQty'] < 0 else Position.LONG

                position.entry(direction, symbol, quantity)

                position.leverage = pos['leverage']

                position.entry_price = pos['avgEntryPrice']
                position.created_time = datetime.strptime(pos['openingTimestamp'], "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=UTC()).timestamp()

                # id is symbol
                self._positions[symbol] = position

            elif (not pos['isOpen'] or pos['currentQty'] == 0) and self._positions.get(symbol):
                # no more position
                del self._positions[symbol]

            if position:
                # absolute value because we work with positive quantity + direction information
                position.quantity = abs(float(pos['currentQty']))
                position.direction = Position.SHORT if pos['currentQty'] < 0 else Position.LONG

                position.leverage = pos['leverage']

                # position.market_close = pos['market_close']
                position.entry_price = pos['avgEntryPrice']
                position.created_time = datetime.strptime(pos['openingTimestamp'], "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=UTC()).timestamp()

                # XBt to XBT
                ratio = 1.0
                if pos['currency'] == 'XBt':
                    ratio = 1.0 / 100000000.0

                # @todo minus taker-fee
                position.profit_loss = (float(pos['unrealisedPnl']) * ratio)
                position.profit_loss_rate = float(pos['unrealisedPnlPcnt'])

                # @todo minus maker-fee
                position.profit_loss_market = (float(pos['unrealisedPnl']) * ratio)
                position.profit_loss_market_rate = float(pos['unrealisedPnlPcnt'])