예제 #1
0
    def _execute_filters(self):
        for f in self.filters():
            try:
                passed = f()
            except TypeError:
                raise exceptions.InvalidStrategy(
                    "Invalid filter format. You need to pass filter methods WITHOUT calling them "
                    "(no parentheses must be present at the end)"
                    "\n\n"
                    u"\u274C " + "Incorrect Example:\n"
                    "return [\n"
                    "    self.filter_1()\n"
                    "]\n\n"
                    u"\u2705 " + "Correct Example:\n"
                    "return [\n"
                    "    self.filter_1\n"
                    "]\n"
                )

            if passed == False:
                logger.info(f.__name__)
                self._reset()
                return False
        
        return True
예제 #2
0
def _check_for_liquidations(candle: np.ndarray, exchange: str, symbol: str) -> None:
    p: Position = selectors.get_position(exchange, symbol)

    if not p:
        return

    # for now, we only support the isolated mode:
    if p.mode != 'isolated':
        return

    if candle_includes_price(candle, p.liquidation_price):
        closing_order_side = jh.closing_side(p.type)

        # create the market order that is used as the liquidation order
        order = Order({
            'id': jh.generate_unique_id(),
            'symbol': symbol,
            'exchange': exchange,
            'side': closing_order_side,
            'type': order_types.MARKET,
            'flag': order_flags.REDUCE_ONLY,
            'qty': jh.prepare_qty(p.qty, closing_order_side),
            'price': p.bankruptcy_price,
            'role': order_roles.CLOSE_POSITION
        })

        store.orders.add_order(order)

        store.app.total_liquidations += 1

        logger.info(f'{p.symbol} liquidated at {p.liquidation_price}')

        order.execute()
예제 #3
0
    def __init__(self, attributes: dict = None, **kwargs) -> None:
        Model.__init__(self, attributes=attributes, **kwargs)

        if attributes is None:
            attributes = {}

        for a, value in attributes.items():
            setattr(self, a, value)

        if self.created_at is None:
            self.created_at = jh.now_to_timestamp()

        if jh.is_live():
            from jesse.store import store
            self.session_id = store.app.session_id
            self.save(force_insert=True)

        if jh.is_live():
            self.notify_submission()
        if jh.is_debuggable('order_submission'):
            txt = f'{"QUEUED" if self.is_queued else "SUBMITTED"} order: {self.symbol}, {self.type}, {self.side}, {self.qty}'
            if self.price:
                txt += f', ${round(self.price, 2)}'
            logger.info(txt)

        # handle exchange balance for ordered asset
        e = selectors.get_exchange(self.exchange)
        e.on_order_submission(self)
예제 #4
0
    def execute(self):
        if self.is_canceled or self.is_executed:
            return

        self.executed_at = jh.now_to_timestamp()
        self.status = order_statuses.EXECUTED

        # log
        if jh.is_debuggable('order_execution'):
            logger.info('EXECUTED order: {}, {}, {}, {}, ${}'.format(
                self.symbol, self.type, self.side, self.qty,
                round(self.price, 2)))
        # notify
        if jh.is_live(
        ) and config['env']['notifications']['events']['executed_orders']:
            notify('EXECUTED order: {}, {}, {}, {}, {}'.format(
                self.symbol, self.type, self.side, self.qty,
                round(self.price, 2)))

        p = selectors.get_position(self.exchange, self.symbol)

        if p:
            p._on_executed_order(self)

        # handle exchange balance for ordered asset
        e = selectors.get_exchange(self.exchange)
        e.on_order_execution(self)
예제 #5
0
    def execute(self, silent=False) -> None:
        if self.is_canceled or self.is_executed:
            return

        self.executed_at = jh.now_to_timestamp()
        self.status = order_statuses.EXECUTED

        if jh.is_live():
            self.save()

        if not silent:
            txt = f'EXECUTED order: {self.symbol}, {self.type}, {self.side}, {self.qty}'
            if self.price:
                txt += f', ${round(self.price, 2)}'
            # log
            if jh.is_debuggable('order_execution'):
                logger.info(txt)
            # notify
            if jh.is_live():
                self.broadcast()
                if config['env']['notifications']['events']['executed_orders']:
                    notify(txt)

        p = selectors.get_position(self.exchange, self.symbol)

        if p:
            p._on_executed_order(self)

        # handle exchange balance for ordered asset
        e = selectors.get_exchange(self.exchange)
        e.on_order_execution(self)
예제 #6
0
파일: Position.py 프로젝트: ynzheng/jesse
    def _reduce(self, qty, price):
        if self.is_open is False:
            raise EmptyPosition('The position is closed.')

        # just to prevent confusion
        qty = abs(qty)

        estimated_profit = jh.estimate_PNL(qty, self.entry_price, price,
                                           self.type)

        if self.exchange:
            self.exchange.increase_balance(
                self, qty * self.entry_price + estimated_profit)

        if self.type == trade_types.LONG:
            self.qty -= qty
        elif self.type == trade_types.SHORT:
            self.qty += qty

        info_text = 'REDUCED position: {}, {}, {}, {}, ${}'.format(
            self.exchange_name, self.symbol, self.type, self.qty,
            round(self.entry_price, 2))

        if jh.is_debuggable('position_reduced'):
            logger.info(info_text)

        if jh.is_live(
        ) and config['env']['notifications']['events']['updated_position']:
            notifier.notify(info_text)
예제 #7
0
    def __init__(self, attributes=None, **kwargs)-> None:
        Model.__init__(self, attributes=attributes, **kwargs)

        if attributes is None:
            attributes = {}

        for a in attributes:
            setattr(self, a, attributes[a])

        if self.created_at is None:
            self.created_at = jh.now_to_timestamp()

        if jh.is_live() and config['env']['notifications']['events']['submitted_orders']:
            self.notify_submission()
        if jh.is_debuggable('order_submission'):
            logger.info(
                '{} order: {}, {}, {}, {}, ${}'.format(
                    'QUEUED' if self.is_queued else 'SUBMITTED',
                    self.symbol, self.type, self.side, self.qty,
                    round(self.price, 2)
                )
            )

        # handle exchange balance for ordered asset
        e = selectors.get_exchange(self.exchange)
        e.on_order_submission(self)
예제 #8
0
파일: Position.py 프로젝트: ynzheng/jesse
    def _increase(self, qty, price):
        if not self.is_open:
            raise OpenPositionError(
                'position must be already open in order to incrase its size')

        qty = abs(qty)
        size = qty * price

        if self.exchange:
            self.exchange.decrease_balance(self, size)

        self.entry_price = jh.estimate_average_price(qty, price, self.qty,
                                                     self.entry_price)

        if self.type == trade_types.LONG:
            self.qty += qty
        elif self.type == trade_types.SHORT:
            self.qty -= qty

        info_text = 'INCREASED position: {}, {}, {}, {}, ${}'.format(
            self.exchange_name, self.symbol, self.type, self.qty,
            round(self.entry_price, 2))

        if jh.is_debuggable('position_increased'):
            logger.info(info_text)

        if jh.is_live(
        ) and config['env']['notifications']['events']['updated_position']:
            notifier.notify(info_text)
예제 #9
0
파일: Position.py 프로젝트: ynzheng/jesse
    def _close(self, close_price):
        if self.is_open is False:
            raise EmptyPosition('The position is already closed.')

        # just to prevent confusion
        close_qty = abs(self.qty)

        estimated_profit = jh.estimate_PNL(close_qty, self.entry_price,
                                           close_price, self.type)
        entry = self.entry_price
        trade_type = self.type
        self.exit_price = close_price

        if self.exchange:
            self.exchange.increase_balance(
                self, close_qty * self.entry_price + estimated_profit)
        self.qty = 0
        self.entry_price = None
        self.closed_at = jh.now()

        info_text = 'CLOSED {} position: {}, {}. PNL: ${}, entry: {}, exit: {}'.format(
            trade_type, self.exchange_name, self.symbol,
            round(estimated_profit, 2), entry, close_price)

        if jh.is_debuggable('position_closed'):
            logger.info(info_text)

        if jh.is_live(
        ) and config['env']['notifications']['events']['updated_position']:
            notifier.notify(info_text)
예제 #10
0
파일: Order.py 프로젝트: vaiblast/jesse
    def cancel(self):
        """

        :return:
        """
        if self.is_canceled or self.is_executed:
            return

        self.canceled_at = jh.now()
        self.status = order_statuses.CANCELED

        if jh.is_debuggable('order_cancellation'):
            logger.info(
                'CANCELED order: {}, {}, {}, {}, ${}'.format(
                    self.symbol, self.type, self.side, self.qty, round(self.price, 2)
                )
            )

        # notify
        if jh.is_live() and config['env']['notifications']['events']['cancelled_orders']:
            notify(
                'CANCELED order: {}, {}, {}, {}, {}'.format(
                    self.symbol, self.type, self.side, self.qty, round(self.price, 2)
                )
            )

        p = selectors.get_position(self.exchange, self.symbol)
        if p:
            p._on_canceled_order(self)
예제 #11
0
파일: Position.py 프로젝트: ynzheng/jesse
    def _open(self, qty, price, change_balance=True):
        if self.is_open:
            raise OpenPositionError(
                'an already open position cannot be opened')

        if change_balance:
            size = abs(qty) * price
            if self.exchange:
                self.exchange.decrease_balance(self, size)

        self.entry_price = price
        self.exit_price = None
        self.qty = qty
        self.opened_at = jh.now()

        info_text = 'OPENED {} position: {}, {}, {}, ${}'.format(
            self.type, self.exchange_name, self.symbol, self.qty,
            round(self.entry_price, 2))

        if jh.is_debuggable('position_opened'):
            logger.info(info_text)

        if jh.is_live(
        ) and config['env']['notifications']['events']['updated_position']:
            notifier.notify(info_text)
예제 #12
0
 def charge_fee(self, amount):
     fee_amount = abs(amount) * self.fee_rate
     new_balance = self.assets[self.settlement_currency] - fee_amount
     logger.info(
         f'Charged {round(fee_amount, 2)} as fee. Balance for {self.settlement_currency} on {self.name} changed from {round(self.assets[self.settlement_currency], 2)} to {round(new_balance, 2)}'
     )
     self.assets[self.settlement_currency] = new_balance
예제 #13
0
    def on_order_cancellation(self, order: Order):
        base_asset = jh.base_asset(order.symbol)
        quote_asset = jh.quote_asset(order.symbol)

        # used for logging balance change
        temp_old_quote_available_asset = self.available_assets[quote_asset]
        temp_old_base_available_asset = self.available_assets[base_asset]

        if order.side == sides.BUY:
            self.available_assets[quote_asset] += (
                abs(order.qty) * order.price) * (1 + self.fee_rate)
        # sell order
        else:
            self.available_assets[base_asset] += abs(order.qty)

        temp_new_quote_available_asset = self.available_assets[quote_asset]
        if jh.is_debuggable(
                'balance_update'
        ) and temp_old_quote_available_asset != temp_new_quote_available_asset:
            logger.info(
                'Available balance for {} on {} changed from {} to {}'.format(
                    quote_asset, self.name,
                    round(temp_old_quote_available_asset, 2),
                    round(temp_new_quote_available_asset, 2)))
        temp_new_base_available_asset = self.available_assets[base_asset]
        if jh.is_debuggable(
                'balance_update'
        ) and temp_old_base_available_asset != temp_new_base_available_asset:
            logger.info(
                'Available balance for {} on {} changed from {} to {}'.format(
                    base_asset, self.name,
                    round(temp_old_base_available_asset, 2),
                    round(temp_new_base_available_asset, 2)))
예제 #14
0
파일: utils.py 프로젝트: zx9r/jesse
def save_daily_portfolio_balance() -> None:
    balances = []

    # add exchange balances
    for key, e in store.exchanges.storage.items():
        balances.append(e.assets[jh.app_currency()])

        # # store daily_balance of assets into database
        # if jh.is_livetrading():
        #     for asset_key, asset_value in e.assets.items():
        #         store_daily_balance_into_db({
        #             'id': jh.generate_unique_id(),
        #             'timestamp': jh.now(),
        #             'identifier': jh.get_config('env.identifier', 'main'),
        #             'exchange': e.name,
        #             'asset': asset_key,
        #             'balance': asset_value,
        #         })

    # add open position values
    for key, pos in store.positions.storage.items():
        if pos.is_open:
            balances.append(pos.pnl)

    total = sum(balances)
    store.app.daily_balance.append(total)

    # TEMP: disable storing in database for now
    if not jh.is_livetrading():
        logger.info(f'Saved daily portfolio balance: {round(total, 2)}')
예제 #15
0
 def cancel_order(self, exchange: str, symbol: str, order_id: str) -> bool:
     if exchange not in self.drivers:
         logger.info(
             f'Exchange "{exchange}" driver not initiated yet. Trying again in the next candle'
         )
         return False
     return self.drivers[exchange].cancel_order(symbol, order_id)
예제 #16
0
    def _on_executed_order(self, order: Order):
        qty = order.qty
        price = order.price

        # TODO: detect reduce_only order, and if so, see if you need to adjust qty and price (above variables)

        self.exchange.charge_fee(qty * price)

        # order opens position
        if self.qty == 0:
            change_balance = order.type == order_types.MARKET
            self._open(qty, price, change_balance)
        # order closes position
        elif (sum_floats(self.qty, qty)) == 0:
            self._close(price)
        # order increases the size of the position
        elif self.qty * qty > 0:
            self._increase(qty, price)
        # order reduces the size of the position
        elif self.qty * qty < 0:
            # if size of the order is big enough to both close the
            # position AND open it on the opposite side
            if abs(qty) > abs(self.qty):
                logger.info(
                    'Executed order is big enough to not close, but flip the position type. Order QTY: {}, Position QTY: {}'
                    .format(qty, self.qty))
                diff_qty = sum_floats(self.qty, qty)
                self._close(price)
                self._open(diff_qty, price)
            else:
                self._reduce(qty, price)

        if self.strategy:
            self.strategy._on_updated_position(order)
예제 #17
0
    def cancel(self, silent=False) -> None:
        if self.is_canceled or self.is_executed:
            return

        self.canceled_at = jh.now_to_timestamp()
        self.status = order_statuses.CANCELED

        if jh.is_live():
            self.save()

        if not silent:
            txt = f'CANCELED order: {self.symbol}, {self.type}, {self.side}, {self.qty}'
            if self.price:
                txt += f', ${round(self.price, 2)}'
            if jh.is_debuggable('order_cancellation'):
                logger.info(txt)
            if jh.is_live():
                self.broadcast()
                if config['env']['notifications']['events'][
                        'cancelled_orders']:
                    notify(txt)

        # handle exchange balance
        e = selectors.get_exchange(self.exchange)
        e.on_order_cancellation(self)
예제 #18
0
파일: Position.py 프로젝트: wcy/jesse
    def _reduce(self, qty: float, price: float) -> None:
        if self.is_open is False:
            raise EmptyPosition('The position is closed.')

        # just to prevent confusion
        qty = abs(qty)

        estimated_profit = jh.estimate_PNL(qty, self.entry_price, price,
                                           self.type)

        if self.exchange:
            # self.exchange.increase_futures_balance(qty * self.entry_price + estimated_profit)
            self.exchange.add_realized_pnl(estimated_profit)
            self.exchange.temp_reduced_amount[jh.base_asset(
                self.symbol)] += abs(qty * price)

        if self.type == trade_types.LONG:
            self.qty = subtract_floats(self.qty, qty)
        elif self.type == trade_types.SHORT:
            self.qty = sum_floats(self.qty, qty)

        info_text = 'REDUCED position: {}, {}, {}, {}, ${}'.format(
            self.exchange_name, self.symbol, self.type, self.qty,
            round(self.entry_price, 2))

        if jh.is_debuggable('position_reduced'):
            logger.info(info_text)

        if jh.is_live(
        ) and config['env']['notifications']['events']['updated_position']:
            notifier.notify(info_text)
예제 #19
0
파일: Position.py 프로젝트: wcy/jesse
    def _close(self, close_price: float) -> None:
        if self.is_open is False:
            raise EmptyPosition('The position is already closed.')

        # just to prevent confusion
        close_qty = abs(self.qty)

        estimated_profit = jh.estimate_PNL(close_qty, self.entry_price,
                                           close_price, self.type)
        entry = self.entry_price
        trade_type = self.type
        self.exit_price = close_price

        if self.exchange:
            self.exchange.add_realized_pnl(estimated_profit)
            self.exchange.temp_reduced_amount[jh.base_asset(
                self.symbol)] += abs(close_qty * close_price)
        self.qty = 0
        self.entry_price = None
        self.closed_at = jh.now_to_timestamp()

        if not jh.is_unit_testing():
            info_text = 'CLOSED {} position: {}, {}, {}. PNL: ${}, Balance: ${}, entry: {}, exit: {}'.format(
                trade_type, self.exchange_name, self.symbol,
                self.strategy.name, round(estimated_profit, 2),
                jh.format_currency(
                    round(self.exchange.wallet_balance(self.symbol),
                          2)), entry, close_price)

            if jh.is_debuggable('position_closed'):
                logger.info(info_text)

            if jh.is_live(
            ) and config['env']['notifications']['events']['updated_position']:
                notifier.notify(info_text)
예제 #20
0
    def _increase(self, qty: float, price: float) -> None:
        if not self.is_open:
            raise OpenPositionError('position must be already open in order to increase its size')

        qty = abs(qty)
        # size = qty * price

        # if self.exchange:
        #     self.exchange.decrease_futures_balance(size)

        self.entry_price = jh.estimate_average_price(qty, price, self.qty,
                                                     self.entry_price)

        if self.type == trade_types.LONG:
            self.qty = sum_floats(self.qty, qty)
        elif self.type == trade_types.SHORT:
            self.qty = subtract_floats(self.qty, qty)

        info_text = f'INCREASED position: {self.exchange_name}, {self.symbol}, {self.type}, {self.qty}, ${round(self.entry_price, 2)}'

        if jh.is_debuggable('position_increased'):
            logger.info(info_text)

        if jh.is_live() and config['env']['notifications']['events']['updated_position']:
            notifier.notify(info_text)
예제 #21
0
파일: utils.py 프로젝트: youngqqcn/jesse
def save_daily_portfolio_balance() -> None:
    balances = []

    # add exchange balances
    for key, e in store.exchanges.storage.items():
        balances.append(e.assets[jh.app_currency()])

        # store daily_balance of assets into database
        if jh.is_livetrading():
            for asset_key, asset_value in e.assets.items():
                store_daily_balance_into_db({
                    'id':
                    jh.generate_unique_id(),
                    'timestamp':
                    jh.now(),
                    'identifier':
                    jh.get_config('env.identifier', 'main'),
                    'exchange':
                    e.name,
                    'asset':
                    asset_key,
                    'balance':
                    asset_value,
                })

    # add open position values
    for key, pos in store.positions.storage.items():
        if pos.is_open:
            balances.append(pos.pnl)

    total = sum(balances)
    store.app.daily_balance.append(total)
    logger.info('Saved daily portfolio balance: {}'.format(round(total, 2)))
예제 #22
0
 def add_realized_pnl(self, realized_pnl: float) -> None:
     new_balance = self.assets[self.settlement_currency] + realized_pnl
     logger.info('Added realized PNL of {}. Balance for {} on {} changed from {} to {}'.format(
         round(realized_pnl, 2),
         self.settlement_currency, self.name,
         round(self.assets[self.settlement_currency], 2),
         round(new_balance, 2),
     ))
     self.assets[self.settlement_currency] = new_balance
예제 #23
0
파일: Strategy.py 프로젝트: youngqqcn/jesse
    def _on_take_profit(self, order: Order) -> None:
        if not jh.should_execute_silently() or jh.is_debugging():
            logger.info("Take-profit order has been executed.")

        self._broadcast('route-take-profit')
        self._execute_cancel()
        self.on_take_profit(order)

        self._detect_and_handle_entry_and_exit_modifications()
예제 #24
0
파일: Strategy.py 프로젝트: youngqqcn/jesse
    def _on_stop_loss(self, order: Order) -> None:
        if not jh.should_execute_silently() or jh.is_debugging():
            logger.info('Stop-loss has been executed.')

        self._broadcast('route-stop-loss')
        self._execute_cancel()
        self.on_stop_loss(order)

        self._detect_and_handle_entry_and_exit_modifications()
예제 #25
0
    def log(self, msg, log_type='info'):
        msg = str(msg)

        if log_type == 'info':
            logger.info(msg)
        elif log_type == 'error':
            logger.error(msg)
        else:
            raise ValueError(f'log_type should be either "info" or "error". You passed {log_type}')
예제 #26
0
 def stop_order(self, exchange: str, symbol: str, qty: float, price: float,
                side: str, role: str, flags: list) -> Union[Order, None]:
     if exchange not in self.drivers:
         logger.info(
             f'Exchange "{exchange}" driver not initiated yet. Trying again in the next candle'
         )
         return None
     return self.drivers[exchange].stop_order(symbol, qty, price, side,
                                              role, flags)
예제 #27
0
    def _on_increased_position(self):
        if not jh.should_execute_silently() or jh.is_debugging():
            logger.info("Position size increased.")

        self._broadcast('route-increased-position')

        self.on_increased_position()

        self._detect_and_handle_entry_and_exit_modifications()
예제 #28
0
파일: Strategy.py 프로젝트: jesse-ai/jesse
    def _on_close_position(self, order: Order):
        if not jh.should_execute_silently() or jh.is_debugging():
            logger.info("A closing order has been executed")

        self._broadcast('route-close-position')
        self._execute_cancel()
        self.on_close_position(order)

        self._detect_and_handle_entry_and_exit_modifications()
예제 #29
0
    def decrease_balance(self, position, balance):
        old_balance = self.balance

        self.balance -= abs(balance) * (1 + self.fee)

        new_balance = self.balance

        if jh.is_debuggable('balance_update'):
            logger.info('balance changed from {} to {}'.format(
                old_balance, new_balance))
예제 #30
0
    def terminate(self):
        # log, so we can check this block was executed in the first place
        logger.info('executed terminate successfully')

        # assert open position
        assert self.position.is_open
        assert self.position.pnl == 97

        # close it manually
        self.liquidate()