Exemple #1
0
def test_get_current_and_past_ticker():
    set_up()

    # add 4 tickers
    t1 = np.array([jh.now_to_timestamp(), 1, 2, 3, 4], dtype=np.float64)
    t2 = np.array([jh.now_to_timestamp() + 1000, 2, 2, 3, 4], dtype=np.float64)
    t3 = np.array([jh.now_to_timestamp() + 2000, 3, 2, 3, 4], dtype=np.float64)
    t4 = np.array([jh.now_to_timestamp() + 3000, 4, 2, 3, 4], dtype=np.float64)
    store.tickers.add_ticker(t1, 'Sandbox', 'BTC-USD')
    store.app.time += 1000
    store.tickers.add_ticker(t2, 'Sandbox', 'BTC-USD')
    store.app.time += 1000
    store.tickers.add_ticker(t3, 'Sandbox', 'BTC-USD')
    store.app.time += 1000
    store.tickers.add_ticker(t4, 'Sandbox', 'BTC-USD')
    np.testing.assert_equal(store.tickers.get_tickers('Sandbox', 'BTC-USD'),
                            np.array([t1, t2, t3, t4]))

    # get the previous one
    np.testing.assert_equal(
        store.tickers.get_past_ticker('Sandbox', 'BTC-USD', 1), t3)

    # get current
    np.testing.assert_equal(
        store.tickers.get_current_ticker('Sandbox', 'BTC-USD'), t4)
Exemple #2
0
def test_can_log_error_by_firing_event():
    set_up()

    # fire first error event
    logger.error('first error!!!!!')
    first_logged_error = {'time': jh.now_to_timestamp(), 'message': 'first error!!!!!'}

    assert store.logs.errors == [first_logged_error]

    # fire second error event
    logger.error('second error!!!!!')
    second_logged_error = {'time': jh.now_to_timestamp(), 'message': 'second error!!!!!'}

    assert store.logs.errors == [first_logged_error, second_logged_error]
Exemple #3
0
def info(msg: str) -> None:
    from jesse.store import store

    store.logs.info.append({'time': jh.now_to_timestamp(), 'message': msg})

    if (jh.is_backtesting() and jh.is_debugging()) or jh.is_collecting_data():
        print('[{}]: {}'.format(jh.timestamp_to_time(jh.now_to_timestamp()),
                                msg))

    if jh.is_live():
        msg = '[INFO | {}] '.format(
            jh.timestamp_to_time(jh.now_to_timestamp())[:19]) + str(msg)
        import logging
        logging.info(msg)
Exemple #4
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)
Exemple #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)
Exemple #6
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)
Exemple #7
0
def test_risk_percentage():
    set_up(zero_fee=True)

    trade = CompletedTrade({
        'type': 'long',
        'exchange': 'Sandbox',
        'entry_price': 10,
        'exit_price': 12,
        'take_profit_at': 20,
        'stop_loss_at': 5,
        'qty': 1,
        'orders': [],
        'symbol': 'BTC-USD',
        'opened_at': jh.now_to_timestamp(),
        'closed_at': jh.now_to_timestamp()
    })
Exemple #8
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)
Exemple #9
0
    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_margin_balance(size)

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

        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)
def test_trade_size():
    trade = CompletedTrade({
        'type': 'long',
        'exchange': 'Sandbox',
        'entry_price': 10,
        'exit_price': 20,
        'take_profit_at': 20,
        'stop_loss_at': 5,
        'qty': 1,
        'orders': [],
        'symbol': 'BTC-USD',
        'opened_at': jh.now_to_timestamp(),
        'closed_at': jh.now_to_timestamp()
    })

    assert trade.size == 10
Exemple #11
0
def store_orderbook_into_db(exchange: str, symbol: str, orderbook: np.ndarray):
    """

    :param exchange:
    :param symbol:
    :param orderbook:
    """
    d = {
        'id': jh.generate_unique_id(),
        'timestamp': jh.now_to_timestamp(),
        'data': orderbook.dumps(),
        'symbol': symbol,
        'exchange': exchange,
    }

    def async_save():
        Orderbook.insert(**d).on_conflict_ignore().execute()
        print(
            jh.color(
                'orderbook: {}-{}-{}: [{}, {}], [{}, {}]'.format(
                    jh.timestamp_to_time(d['timestamp']), exchange, symbol,
                    # best ask
                    orderbook[0][0][0], orderbook[0][0][1],
                    # best bid
                    orderbook[1][0][0], orderbook[1][0][1]
                ),
                'magenta'
            )
        )

    # async call
    threading.Thread(target=async_save).start()
Exemple #12
0
def info(msg: str, send_notification=False) -> None:
    if jh.app_mode() not in LOGGERS:
        _init_main_logger()

    msg = str(msg)
    from jesse.store import store

    log_id = jh.generate_unique_id()
    log_dict = {
        'id': log_id,
        'timestamp': jh.now_to_timestamp(),
        'message': msg
    }

    store.logs.info.append(log_dict)

    if jh.is_collecting_data() or jh.is_live():
        sync_publish('info_log', log_dict)

    if jh.is_live() or (jh.is_backtesting() and jh.is_debugging()):
        msg = f"[INFO | {jh.timestamp_to_time(jh.now_to_timestamp())[:19]}] {msg}"
        logger = LOGGERS[jh.app_mode()]
        logger.info(msg)

    if jh.is_live():
        from jesse.models.utils import store_log_into_db
        store_log_into_db(log_dict, 'info')

    if send_notification:
        notify(msg)
Exemple #13
0
def error(msg: str) -> None:
    if jh.app_mode() not in LOGGERS:
        _init_main_logger()

    # error logs should be logged as info logs as well
    info(msg)

    msg = str(msg)
    from jesse.store import store

    log_id = jh.generate_unique_id()
    log_dict = {
        'id': log_id,
        'timestamp': jh.now_to_timestamp(),
        'message': msg
    }

    if jh.is_live() and jh.get_config('env.notifications.events.errors', True):
        # notify_urgently(f"ERROR at \"{jh.get_config('env.identifier')}\" account:\n{msg}")
        notify_urgently(f"ERROR:\n{msg}")
        notify(f'ERROR:\n{msg}')
    if (jh.is_backtesting() and jh.is_debugging()) or jh.is_collecting_data() or jh.is_live():
        sync_publish('error_log', log_dict)

    store.logs.errors.append(log_dict)

    if jh.is_live() or jh.is_optimizing():
        msg = f"[ERROR | {jh.timestamp_to_time(jh.now_to_timestamp())[:19]}] {msg}"
        logger = LOGGERS[jh.app_mode()]
        logger.error(msg)

    if jh.is_live():
        from jesse.models.utils import store_log_into_db
        store_log_into_db(log_dict, 'error')
Exemple #14
0
    def __init__(self, iterations: int, population_size: int, solution_len: int,
                 charset: str = '()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvw',
                 fitness_goal: float = 1,
                 options: Dict[str, Union[bool, Any]] = None) -> None:
        self.started_index = 0
        self.start_time = jh.now_to_timestamp()
        self.population = []
        self.iterations = iterations
        self.population_size = population_size
        self.solution_len = solution_len
        self.charset = charset
        self.fitness_goal = fitness_goal
        self.cpu_cores = 0

        if options is None:
            self.options = {}
        else:
            self.options = options

        os.makedirs('./storage/temp/optimize', exist_ok=True)
        self.temp_path = f"./storage/temp/optimize/{self.options['strategy_name']}-{self.options['exchange']}-{self.options['symbol']}-{self.options['timeframe']}-{self.options['start_date']}-{self.options['finish_date']}.pickle"

        if fitness_goal > 1 or fitness_goal < 0:
            raise ValueError('fitness scores must be between 0 and 1')

        # if temp file exists, load data to resume previous session
        if jh.file_exists(self.temp_path):
            if click.confirm('Previous session detected. Do you want to resume?', default=True):
                self.load_progress()
Exemple #15
0
    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)
Exemple #16
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)
Exemple #17
0
def broadcast_error_without_logging(msg: str):
    msg = str(msg)

    sync_publish('error_log', {
        'id': jh.generate_unique_id(),
        'timestamp': jh.now_to_timestamp(),
        'message': msg
    })
Exemple #18
0
def test_can_log_info_by_firing_event():
    set_up()

    # fire first info event
    logger.info('first info!!!!!')
    first_logged_info = {'time': jh.now_to_timestamp(), 'message': 'first info!!!!!'}

    assert store.logs.info == [first_logged_info]

    # fire second info event
    logger.info('second info!!!!!')
    second_logged_info = {
        'time': jh.now_to_timestamp(),
        'message': 'second info!!!!!'
    }

    assert store.logs.info == [first_logged_info, second_logged_info]
Exemple #19
0
def test_risk_percentage():
    no_fee()

    trade = CompletedTrade({
        'type': 'long',
        'exchange': 'Sandbox',
        'entry_price': 10,
        'exit_price': 12,
        'take_profit_at': 20,
        'stop_loss_at': 5,
        'qty': 1,
        'orders': [],
        'symbol': 'BTC-USD',
        'opened_at': jh.now_to_timestamp(),
        'closed_at': jh.now_to_timestamp()
    })
    assert trade.risk_percentage == round((((10 - 5) / 1) * 10), 2)
Exemple #20
0
def test_R():
    no_fee()

    trade = CompletedTrade({
        'type': 'long',
        'exchange': 'Sandbox',
        'entry_price': 10,
        'exit_price': 12,
        'take_profit_at': 20,
        'stop_loss_at': 5,
        'qty': 1,
        'orders': [],
        'symbol': 'BTC-USD',
        'opened_at': jh.now_to_timestamp(),
        'closed_at': jh.now_to_timestamp()
    })
    assert trade.risk_reward_ratio == 2
Exemple #21
0
    def add_candle(self,
                   candle: np.ndarray,
                   exchange: str,
                   symbol: str,
                   timeframe: str,
                   with_execution: bool = True,
                   with_generation: bool = True) -> None:
        if jh.is_collecting_data():
            # make sure it's a complete (and not a forming) candle
            if jh.now_to_timestamp() >= (candle[0] + 60000):
                store_candle_into_db(exchange, symbol, candle)
            return

        arr: DynamicNumpyArray = self.get_storage(exchange, symbol, timeframe)

        if jh.is_live():
            self.update_position(exchange, symbol, candle)

            # ignore new candle at the time of execution because it messes
            # the count of candles without actually having an impact
            if candle[0] >= jh.now():
                return

        # initial
        if len(arr) == 0:
            arr.append(candle)

        # if it's new, add
        elif candle[0] > arr[-1][0]:
            # in paper mode, check to see if the new candle causes any active orders to be executed
            if with_execution and jh.is_paper_trading():
                self.simulate_order_execution(exchange, symbol, timeframe,
                                              candle)

            arr.append(candle)

            # generate other timeframes
            if with_generation and timeframe == '1m':
                self.generate_bigger_timeframes(candle, exchange, symbol,
                                                with_execution)

        # if it's the last candle again, update
        elif candle[0] == arr[-1][0]:
            # in paper mode, check to see if the new candle causes any active orders to get executed
            if with_execution and jh.is_paper_trading():
                self.simulate_order_execution(exchange, symbol, timeframe,
                                              candle)

            arr[-1] = candle

            # regenerate other timeframes
            if with_generation and timeframe == '1m':
                self.generate_bigger_timeframes(candle, exchange, symbol,
                                                with_execution)

        # past candles will be ignored (dropped)
        elif candle[0] < arr[-1][0]:
            return
Exemple #22
0
    def add_orderbook(self, exchange: str, symbol: str, asks: list, bids: list) -> None:
        key = jh.key(exchange, symbol)
        self.temp_storage[key]['asks'] = asks
        self.temp_storage[key]['bids'] = bids

        # generate new numpy formatted orderbook if it is
        # either the first time, or that it has passed
        # 1000 milliseconds since the last time
        if self.temp_storage[key]['last_updated_timestamp'] is None or jh.now_to_timestamp() - self.temp_storage[key][
            'last_updated_timestamp'] >= 1000:
            self.temp_storage[key]['last_updated_timestamp'] = jh.now_to_timestamp()

            formatted_orderbook = self.format_orderbook(exchange, symbol)

            if jh.is_collecting_data():
                store_orderbook_into_db(exchange, symbol, formatted_orderbook)
            else:
                self.storage[key].append(formatted_orderbook)
Exemple #23
0
def error(msg):
    from jesse.store import store

    if jh.is_live() and jh.get_config('env.notifications.events.errors', True):
        notify('ERROR:\n{}'.format(msg))
    if (jh.is_backtesting() and jh.is_debugging()) or jh.is_collecting_data():
        print(
            jh.color(
                '[{}]: {}'.format(jh.timestamp_to_time(jh.now_to_timestamp()),
                                  msg), 'red'))

    store.logs.errors.append({'time': jh.now_to_timestamp(), 'message': msg})

    if jh.is_live() or jh.is_optimizing():
        msg = '[ERROR | {}] '.format(
            jh.timestamp_to_time(jh.now_to_timestamp())[:19]) + str(msg)
        import logging
        logging.error(msg)
Exemple #24
0
    def add_ticker(self, ticker: np.ndarray, exchange: str, symbol: str) -> None:
        key = jh.key(exchange, symbol)

        # only process once per second
        if len(self.storage[key][:]) == 0 or jh.now_to_timestamp() - self.storage[key][-1][0] >= 1000:
            self.storage[key].append(ticker)

            if jh.is_collecting_data():
                store_ticker_into_db(exchange, symbol, ticker)
                return
def test_pnl_with_fee():
    # set fee (0.20%)
    config['env']['exchanges']['Sandbox']['fee'] = 0.002

    trade = CompletedTrade({
        'type': 'long',
        'exchange': 'Sandbox',
        'entry_price': 10,
        'exit_price': 20,
        'take_profit_at': 20,
        'stop_loss_at': 5,
        'qty': 1,
        'orders': [],
        'symbol': 'BTC-USD',
        'opened_at': jh.now_to_timestamp(),
        'closed_at': jh.now_to_timestamp()
    })

    assert trade.fee == 0.06
    assert trade.pnl == 9.94
Exemple #26
0
def info(msg: str) -> None:
    from jesse.store import store

    store.logs.info.append({'time': jh.now_to_timestamp(), 'message': msg})

    if (jh.is_backtesting() and jh.is_debugging()) or jh.is_collecting_data():
        print(f'[{jh.timestamp_to_time(jh.now_to_timestamp())}]: {msg}')

    if jh.is_live():
        msg = f"[INFO | {jh.timestamp_to_time(jh.now_to_timestamp())[:19]}] {str(msg)}"
        logging.info(msg)
Exemple #27
0
def test_can_add_new_ticker():
    set_up()

    np.testing.assert_equal(store.tickers.get_tickers('Sandbox', 'BTC-USD'),
                            np.zeros((0, 5)))

    # add first ticker
    t1 = np.array([jh.now_to_timestamp(), 1, 2, 3, 4], dtype=np.float64)
    store.tickers.add_ticker(t1, 'Sandbox', 'BTC-USD')
    np.testing.assert_equal(
        store.tickers.get_tickers('Sandbox', 'BTC-USD')[0], t1)

    # fake 1 second
    store.app.time += 1000

    # add second ticker
    t2 = np.array([jh.now_to_timestamp() + 1, 11, 22, 33, 44],
                  dtype=np.float64)
    store.tickers.add_ticker(t2, 'Sandbox', 'BTC-USD')
    np.testing.assert_equal(store.tickers.get_tickers('Sandbox', 'BTC-USD'),
                            np.array([t1, t2]))
Exemple #28
0
def test_cancel_order():
    set_up()

    order = Order({
        'id': jh.generate_unique_id(),
        'exchange': 'Sandbox',
        'symbol': 'BTC-USDT',
        'type': order_types.LIMIT,
        'price': 129.33,
        'qty': 10.2041,
        'side': sides.BUY,
        'status': order_statuses.ACTIVE,
        'created_at': jh.now_to_timestamp(),
    })

    assert order.is_canceled is False

    order.cancel()

    assert order.is_canceled is True
    assert order.canceled_at == jh.now_to_timestamp()
Exemple #29
0
def test_execute_order():
    set_up_without_fee()

    order = Order({
        'id': jh.generate_unique_id(),
        'symbol': 'BTC-USDT',
        'exchange': exchange.name,
        'type': order_types.LIMIT,
        'price': 129.33,
        'qty': 10.2041,
        'side': sides.BUY,
        'status': order_statuses.ACTIVE,
        'created_at': jh.now_to_timestamp(),
    })

    assert order.is_executed is False
    assert order.executed_at is None

    order.execute()

    assert order.is_executed is True
    assert order.executed_at == jh.now_to_timestamp()
Exemple #30
0
def livetrade():
    # TODO: for now, we assume that we trade on one exchange only. Later, we need to support for more than one exchange at a time
    # sum up balance of all trading exchanges
    starting_balance = 0
    current_balance = 0
    for e in store.exchanges.storage:
        starting_balance += store.exchanges.storage[e].starting_assets[
            jh.app_currency()]
        current_balance += store.exchanges.storage[e].assets[jh.app_currency()]
    starting_balance = round(starting_balance, 2)
    current_balance = round(current_balance, 2)

    # short trades summary
    if len(store.completed_trades.trades):
        df = pd.DataFrame.from_records(
            [t.to_dict() for t in store.completed_trades.trades])
        total = len(df)
        winning_trades = len(df.loc[df['PNL'] > 0])
        losing_trades = len(df.loc[df['PNL'] < 0])
        pnl = round(df['PNL'].sum(), 2)
        pnl_perc = round((pnl / starting_balance) * 100, 2)
    else:
        pnl, pnl_perc, total, winning_trades, losing_trades = 0, 0, 0, 0, 0

    routes = [{
        'exchange': r.exchange,
        'symbol': r.symbol,
        'timeframe': r.timeframe,
        'strategy': r.strategy_name
    } for r in router.routes]

    return {
        'session_id': store.app.session_id,
        'started_at': str(store.app.starting_time),
        'current_time': str(jh.now_to_timestamp()),
        'started_balance': str(starting_balance),
        'current_balance': str(current_balance),
        'debug_mode': str(config['app']['debug_mode']),
        'paper_mode': str(jh.is_paper_trading()),
        'count_error_logs': str(len(store.logs.errors)),
        'count_info_logs': str(len(store.logs.info)),
        'count_active_orders': str(store.orders.count_all_active_orders()),
        'open_positions': str(store.positions.count_open_positions()),
        'pnl': str(pnl),
        'pnl_perc': str(pnl_perc),
        'count_trades': str(total),
        'count_winning_trades': str(winning_trades),
        'count_losing_trades': str(losing_trades),
        'routes': routes
    }