Exemplo n.º 1
0
    def _convert_to_numpy_array(self, arr, name) -> np.ndarray:
        if type(arr) is np.ndarray:
            return arr

        try:
            # create numpy array from list
            arr = np.array(arr, dtype=float)

            if jh.is_live():
                # in livetrade mode, we'll need them rounded
                price = arr[0][1]

                price_precision = selectors.get_exchange(
                    self.exchange).vars['precisions'][
                        self.symbol]['price_precision']
                qty_precision = selectors.get_exchange(
                    self.exchange).vars['precisions'][
                        self.symbol]['qty_precision']

                prices = jh.round_price_for_live_mode(price, arr[:, 1],
                                                      price_precision)
                qtys = jh.round_qty_for_live_mode(price, arr[:, 0],
                                                  qty_precision)

                arr[:, 0] = qtys
                arr[:, 1] = prices

            return arr
        except ValueError:
            raise exceptions.InvalidShape(
                f'The format of {name} is invalid. \n'
                f'It must be (qty, price) or [(qty, price), (qty, price)] for multiple points; but {arr} was given'
            )
Exemplo n.º 2
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)
Exemplo n.º 3
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)
Exemplo n.º 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)
Exemplo n.º 5
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)
Exemplo n.º 6
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)
Exemplo n.º 7
0
def set_up_without_fee(is_margin_trading=False):
    reset_config()
    config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'margin'
    config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0
    config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [
        {
            'asset': 'USDT',
            'balance': 1000
        },
        {
            'asset': 'BTC',
            'balance': 0
        },
    ]
    if is_margin_trading:
        # used only in margin trading
        config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'margin'
        config['env']['exchanges'][
            exchanges.SANDBOX]['settlement_currency'] = 'USDT'
    config['app']['trading_mode'] = 'backtest'
    config['app']['considering_exchanges'] = ['Sandbox']
    router.set_routes([(exchanges.SANDBOX, 'BTC-USDT', '5m', 'Test19')])
    store.reset(True)

    global position
    global exchange
    position = selectors.get_position(exchanges.SANDBOX, 'BTC-USDT')
    position.current_price = 50
    exchange = selectors.get_exchange(exchanges.SANDBOX)
Exemplo n.º 8
0
    def paper(debug, dev, fee):
        """
        trades in real-time on exchange with PAPER money
        """
        validate_cwd()

        # set trading mode
        from jesse.config import config
        config['app']['trading_mode'] = 'papertrade'

        register_custom_exception_handler()

        # debug flag
        config['app']['debug_mode'] = debug

        from plugins.live import init
        from jesse.services.selectors import get_exchange

        # fee flag
        if not fee:
            for e in config['app']['trading_exchanges']:
                config['env']['exchanges'][e]['fee'] = 0
                get_exchange(e).fee = 0

        # inject live config
        init(config)

        # execute live session
        from plugins.live.live_mode import run
        run(dev)
Exemplo n.º 9
0
def test_decrease_balance():
    set_up()

    e = selectors.get_exchange(exchanges.SANDBOX)
    assert e.balance == 2000
    e.decrease_balance(None, 100)
    assert e.balance == 1900
Exemplo n.º 10
0
def backtest(start_date: str, finish_date: str, debug: bool, csv: bool,
             json: bool, fee: bool, chart: bool, tradingview: bool,
             full_reports: bool) -> None:
    """
    backtest mode. Enter in "YYYY-MM-DD" "YYYY-MM-DD"
    """
    validate_cwd()

    from jesse.config import config
    config['app']['trading_mode'] = 'backtest'

    register_custom_exception_handler()

    from jesse.services import db
    from jesse.modes import backtest_mode
    from jesse.services.selectors import get_exchange

    # debug flag
    config['app']['debug_mode'] = debug

    # fee flag
    if not fee:
        for e in config['app']['trading_exchanges']:
            config['env']['exchanges'][e]['fee'] = 0
            get_exchange(e).fee = 0

    backtest_mode.run(start_date,
                      finish_date,
                      chart=chart,
                      tradingview=tradingview,
                      csv=csv,
                      json=json,
                      full_reports=full_reports)

    db.close_connection()
Exemplo n.º 11
0
    def __init__(self,
                 exchange_name: str,
                 symbol: str,
                 attributes=None) -> None:
        self.id = jh.generate_unique_id()
        self.entry_price = None
        self.exit_price = None
        self.current_price = None
        self.qty = 0
        self.opened_at = None
        self.closed_at = None

        # TODO: self._mark_price = None

        if attributes is None:
            attributes = {}

        self.exchange_name = exchange_name
        self.exchange: Exchange = selectors.get_exchange(self.exchange_name)

        self.symbol = symbol
        self.strategy = None

        for a in attributes:
            setattr(self, a, attributes[a])
Exemplo n.º 12
0
def test_negative_balance_validation():
    with pytest.raises(NegativeBalance):
        set_up()

        e = selectors.get_exchange(exchanges.SANDBOX)
        assert e.balance == 2000
        e.decrease_balance(None, 3000)
Exemplo n.º 13
0
    def live(testdrive: bool, debug: bool, dev: bool, fee: bool) -> None:
        """
        trades in real-time on exchange with REAL money
        """
        validate_cwd()

        # set trading mode
        from jesse.config import config
        config['app']['trading_mode'] = 'livetrade'
        config['app']['is_test_driving'] = testdrive

        register_custom_exception_handler()

        # debug flag
        config['app']['debug_mode'] = debug

        from plugins.live import init
        from jesse.services.selectors import get_exchange

        # fee flag
        if not fee:
            for e in config['app']['trading_exchanges']:
                config['env']['exchanges'][e]['fee'] = 0
                get_exchange(e).fee = 0

        # inject live config
        init(config)

        # execute live session
        from plugins.live.live_mode import run
        run(dev)
Exemplo n.º 14
0
def set_up_with_fee(is_futures_trading=False):
    reset_config()
    config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0.002
    config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [
        {
            'asset': 'USDT',
            'balance': 1000
        },
        {
            'asset': 'BTC',
            'balance': 0
        },
    ]
    if is_futures_trading:
        # used only in futures trading
        config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'futures'
    else:
        config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'spot'
    config['env']['exchanges'][
        exchanges.SANDBOX]['settlement_currency'] = 'USDT'
    config['app']['trading_mode'] = 'backtest'
    config['app']['considering_exchanges'] = ['Sandbox']
    router.set_routes([(exchanges.SANDBOX, 'BTC-USDT', '5m', 'Test19')])
    store.reset(True)

    global position
    global exchange
    global broker
    position = selectors.get_position(exchanges.SANDBOX, 'BTC-USDT')
    position.current_price = 50
    exchange = selectors.get_exchange(exchanges.SANDBOX)
    broker = Broker(position, exchanges.SANDBOX, 'BTC-USDT',
                    timeframes.MINUTE_5)
Exemplo n.º 15
0
def generate_signal(start_date, finish_date, reports_folder, debug, csv, json,
                    fee, chart, tradingview):
    """
    backtest mode. Enter in "YYYY-MM-DD" "YYYY-MM-DD"
    """
    validate_cwd()

    from jesse.config import config
    config['app']['trading_mode'] = 'backtest'

    register_custom_exception_handler()

    from jesse.services import db
    from jesse.modes import backtest_mode
    from jesse.services.selectors import get_exchange

    # debug flag
    config['app']['debug_mode'] = debug

    # fee flag
    if not fee:
        for e in config['app']['trading_exchanges']:
            config['env']['exchanges'][e]['fee'] = 0
            get_exchange(e).fee = 0

    print(f"Generating report for {finish_date}")

    todays_signal_report = backtest_mode.generate_signal(
        start_date,
        finish_date,
        chart=chart,
        tradingview=tradingview,
        csv=csv,
        json=json)

    previous_report_file = Path(
        reports_folder) / f"{jh.get_previous_reports_file_name()}.json"
    print(f"Loading {previous_report_file} for comparison")
    previous_signal_report = jh.load_report(
        previous_report_file) if os.path.exists(previous_report_file) else None

    is_report_different = jh.are_reports_different(previous_signal_report,
                                                   todays_signal_report)

    if is_report_different:
        print("Posting to slack")
        slack_report = jh.construct_slack_report(previous_signal_report,
                                                 todays_signal_report)
        print(slack_report)
        jh.post_slack_notification(slack_report)

    current_report_file_name = Path(
        reports_folder) / f"{jh.get_current_report_file_name()}.json"
    print(f"Dumping to {current_report_file_name}")
    jh.dump_report(todays_signal_report, current_report_file_name)

    db.close_connection()
Exemplo n.º 16
0
    def _init_objects(self) -> None:
        """
        This method gets called after right creating the Strategy object. It
        is just a workaround as a part of not being able to set them inside
        self.__init__() for the purpose of removing __init__() methods from strategies.
        """
        self.position = selectors.get_position(self.exchange, self.symbol)
        self.broker = Broker(self.position, self.exchange, self.symbol,
                             self.timeframe)

        if jh.is_live():
            self.price_precision = selectors.get_exchange(
                self.exchange).vars['precisions'][
                    self.symbol]['price_precision']
            self.qty_precision = selectors.get_exchange(
                self.exchange).vars['precisions'][self.symbol]['qty_precision']

        if self.hp is None:
            if len(self.hyperparameters()) > 0:
                self.hp = {}
                for dna in self.hyperparameters():
                    self.hp[dna['name']] = dna['default']
Exemplo n.º 17
0
def test_reduce_a_short_position():
    set_up()

    p = Position(exchanges.SANDBOX, 'BTCUSD', {
        'entry_price': 50,
        'current_price': 50,
        'qty': -2,
    })
    e = selectors.get_exchange('Sandbox')

    p._reduce(1, 50)

    assert p.qty == -1
    assert e.balance == 1050
Exemplo n.º 18
0
    def update_position(exchange: str, symbol: str, candle: np.ndarray) -> None:
        # get position object
        p = selectors.get_position(exchange, symbol)

        # for extra_route candles, p == None, hence no further action is required
        if p is None:
            return

        price_precision = 0
        if jh.is_live():
          price_precision = selectors.get_exchange(exchange).vars['precisions'][symbol]['price_precision']

        # update position.current_price
        p.current_price = jh.round_price_for_live_mode(candle[2], candle[2], price_precision)
Exemplo n.º 19
0
def test_is_able_to_close_via_reduce_postion_too():
    set_up()

    p = Position(exchanges.SANDBOX, 'BTCUSD', {
        'entry_price': 50,
        'current_price': 50,
        'qty': 2,
    })
    e = selectors.get_exchange('Sandbox')

    p._reduce(2, 50)

    assert p.qty == 0
    assert e.balance == 1100
Exemplo n.º 20
0
    def __init__(self, attributes=None):
        # id generated by Jesse for database usage
        self.id = ''

        # id generated by market, used in live-trade mode
        self.exchange_id = ''
        # some exchanges might require even further info
        self.vars = {}

        self.symbol = ''
        self.exchange = ''
        self.side = ''
        self.type = ''
        self.flag = ''
        self.qty = 0
        self.price = 0
        self.status = order_statuses.ACTIVE
        self.created_at = None
        self.executed_at = None
        self.canceled_at = None
        self.role = None

        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()

        p = selectors.get_position(self.exchange, self.symbol)
        if p:
            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)
                    )
                )

            p._on_opened_order(self)

        # handle exchange balance for ordered asset
        e = selectors.get_exchange(self.exchange)
        e.on_order_submission(self)
Exemplo n.º 21
0
def test_increase_a_short_position():
    set_up()

    p = Position(exchanges.SANDBOX, 'BTCUSD', {
        'entry_price': 50,
        'current_price': 50,
        'qty': -2,
    })
    e = selectors.get_exchange('Sandbox')

    p._increase(2, 40)

    assert p.qty == -4
    assert p.entry_price == 45
    assert e.balance == 920
Exemplo n.º 22
0
def set_up_with_fee():
    reset_config()
    config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0.002
    config['env']['exchanges'][exchanges.SANDBOX]['starting_balance'] = 1000
    config['app']['trading_mode'] = 'backtest'
    config['app']['considering_exchanges'] = ['Sandbox']
    router.set_routes([(exchanges.SANDBOX, 'BTCUSD', '5m', 'Test19')])
    store.reset(True)

    global position
    global exchange
    global broker
    position = selectors.get_position(exchanges.SANDBOX, 'BTCUSD')
    position.current_price = 50
    exchange = selectors.get_exchange(exchanges.SANDBOX)
    broker = Broker(position, exchanges.SANDBOX, 'BTCUSD', timeframes.MINUTE_5)
Exemplo n.º 23
0
def test_close_position():
    set_up()

    p = Position(exchanges.SANDBOX, 'BTCUSD', {
        'entry_price': 50,
        'current_price': 50,
        'qty': 2,
    })
    e = selectors.get_exchange('Sandbox')
    assert p.exit_price is None

    p._close(50)

    assert p.qty == 0
    assert e.balance == 1100
    assert p.entry_price is None
    assert p.exit_price == 50
Exemplo n.º 24
0
def test_open_position():
    set_up()

    p = Position(exchanges.SANDBOX, 'BTCUSD')
    e = selectors.get_exchange('Sandbox')

    assert p.qty == 0
    assert p.entry_price is None
    assert p.exit_price is None
    assert p.current_price is None
    assert e.balance == 1000

    p._open(1, 50)

    assert p.qty == 1
    assert p.entry_price == 50
    assert p.exit_price is None
    assert e.balance == 950
Exemplo n.º 25
0
    def cancel(self):
        if self.is_canceled or self.is_executed:
            return

        self.canceled_at = jh.now_to_timestamp()
        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)))
        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)))

        # handle exchange balance
        e = selectors.get_exchange(self.exchange)
        e.on_order_cancellation(self)
Exemplo n.º 26
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(
        ) and config['env']['notifications']['events']['submitted_orders']:
            self.notify_submission()
        if jh.is_debuggable('order_submission'):
            logger.info(
                f'{"QUEUED" if self.is_queued else "SUBMITTED"} order: {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)
Exemplo n.º 27
0
def livetrade() -> List[Union[List[Union[str, Any]], List[str], List[Union[
    str, int]], List[Union[str, Dict[str, Union[str, int]], Dict[
        str, str], Dict[str, bool], Dict[str, Union[Dict[str, Union[
            int, str, List[Dict[str, Union[str, int]]]]], Dict[str, Union[
                float, str, int, List[Dict[str, Union[str, int]]]]]]], Dict[
                    str, int]]]]]:
    # 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)

    arr = [[
        'started at',
        jh.timestamp_to_arrow(store.app.starting_time).humanize()
    ], ['current time',
        jh.timestamp_to_time(jh.now_to_timestamp())[:19]],
           ['errors/info', f'{len(store.logs.errors)}/{len(store.logs.info)}'],
           ['active orders',
            store.orders.count_all_active_orders()],
           ['open positions',
            store.positions.count_open_positions()]]

    # 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
    first_exchange = selectors.get_exchange(router.routes[0].exchange)

    if first_exchange.type == 'futures':
        arr.append([
            'started/current balance', f'{starting_balance}/{current_balance}'
        ])
    else:
        # loop all trading exchanges
        for exchange in selectors.get_all_exchanges():
            # loop all assets
            for asset_name, asset_balance in exchange.assets.items():
                if asset_name == jh.base_asset(router.routes[0].symbol):
                    current_price = selectors.get_current_price(
                        router.routes[0].exchange, router.routes[0].symbol)
                    arr.append([
                        f'{asset_name}',
                        f'{round(exchange.available_assets[asset_name], 5)}/{round(asset_balance, 5)} ({jh.format_currency(round(asset_balance * current_price, 2))} { jh.quote_asset(router.routes[0].symbol)})'
                    ])
                else:
                    arr.append([
                        f'{asset_name}',
                        f'{round(exchange.available_assets[asset_name], 5)}/{round(asset_balance, 5)}'
                    ])

    # 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 = df.loc[df['PNL'] > 0]
        losing_trades = df.loc[df['PNL'] < 0]
        pnl = round(df['PNL'].sum(), 2)
        pnl_percentage = round((pnl / starting_balance) * 100, 2)

        arr.append([
            'total/winning/losing trades',
            f'{total}/{len(winning_trades)}/{len(losing_trades)}'
        ])
        arr.append(['PNL (%)', f'${pnl} ({pnl_percentage}%)'])

    if config['app']['debug_mode']:
        arr.append(['debug mode', config['app']['debug_mode']])

    if config['app']['is_test_driving']:
        arr.append(['Test Drive', config['app']['is_test_driving']])
    return arr
Exemplo n.º 28
0
 def fee_rate(self) -> float:
     return selectors.get_exchange(self.exchange).fee_rate
Exemplo n.º 29
0
 def capital(self):
     """the current capital in the trading exchange"""
     return selectors.get_exchange(self.exchange).balance
Exemplo n.º 30
0
 def capital(self):
     """the current capital in the trading exchange"""
     e = selectors.get_exchange(self.exchange)
     return e.tradable_balance(self.symbol)