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)
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)
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)
def _terminate(self) -> None: """ Optional for executing code after completion of a backTest. This block will not execute in live use as a live Jesse is never ending. """ if not jh.should_execute_silently() or jh.is_debugging(): logger.info("Terminating strategy...") self.terminate() self._detect_and_handle_entry_and_exit_modifications() # fake execution of market orders in backtest simulation if not jh.is_live(): store.orders.execute_pending_market_orders() if jh.is_live(): return if self.position.is_open: store.app.total_open_trades += 1 store.app.total_open_pl += self.position.pnl logger.info( f"Closed open {self.exchange}-{self.symbol} position at {self.position.current_price} with PNL: {round(self.position.pnl, 4)}({round(self.position.pnl_percentage, 2)}%) because we reached the end of the backtest session." ) # fake a closing (market) order so that the calculations would be correct self.broker.reduce_position_at(self.position.qty, self.position.current_price, order_roles.CLOSE_POSITION) return if self._open_position_orders: self._execute_cancel() logger.info('Canceled open-position orders because we reached the end of the backtest session.')
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')
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)
def handle_thread_exception(args): """ :param args: :return: """ if args.exc_type == SystemExit: return # handle Breaking exceptions if args.exc_type in [ exceptions.ConfigException, exceptions.RouteNotFound, exceptions.InvalidRoutes, exceptions.CandleNotFoundInDatabase ]: click.clear() print('=' * 30 + ' EXCEPTION TRACEBACK:') traceback.print_tb(args.exc_traceback, file=sys.stdout) print("=" * 73) print( '\n', jh.color('Uncaught Exception:', 'red'), jh.color( '{}: {}'.format(args.exc_type.__name__, args.exc_value), 'yellow')) return # send notifications if it's a live session if jh.is_live(): jesse_logger.error('{}: {}'.format(args.exc_type.__name__, args.exc_value)) if jh.is_live() or jh.is_collecting_data(): logging.error("Uncaught Exception:", exc_info=(args.exc_type, args.exc_value, args.exc_traceback)) else: print('=' * 30 + ' EXCEPTION TRACEBACK:') traceback.print_tb(args.exc_traceback, file=sys.stdout) print("=" * 73) print( '\n', jh.color('Uncaught Exception:', 'red'), jh.color( '{}: {}'.format(args.exc_type.__name__, args.exc_value), 'yellow')) if jh.is_paper_trading(): print( jh.color( 'An uncaught exception was raised. Check the log file at:\n{}' .format('storage/logs/paper-trade.txt'), 'red')) elif jh.is_livetrading(): print( jh.color( 'An uncaught exception was raised. Check the log file at:\n{}' .format('storage/logs/live-trade.txt'), 'red')) elif jh.is_collecting_data(): print( jh.color( 'An uncaught exception was raised. Check the log file at:\n{}' .format('storage/logs/collect.txt'), 'red'))
def handle_exception(exc_type, exc_value, exc_traceback) -> None: if issubclass(exc_type, KeyboardInterrupt): sys.excepthook(exc_type, exc_value, exc_traceback) return # handle Breaking exceptions if exc_type in [ exceptions.InvalidConfig, exceptions.RouteNotFound, exceptions.InvalidRoutes, exceptions.CandleNotFoundInDatabase ]: click.clear() print(f"{'=' * 30} EXCEPTION TRACEBACK:") traceback.print_tb(exc_traceback, file=sys.stdout) print("=" * 73) print( '\n', jh.color('Uncaught Exception:', 'red'), jh.color(f'{exc_type.__name__}: {exc_value}', 'yellow') ) return # send notifications if it's a live session if jh.is_live(): jesse_logger.error( f'{exc_type.__name__}: {exc_value}' ) if jh.is_live() or jh.is_collecting_data(): logging.error("Uncaught Exception:", exc_info=(exc_type, exc_value, exc_traceback)) else: print(f"{'=' * 30} EXCEPTION TRACEBACK:") traceback.print_tb(exc_traceback, file=sys.stdout) print("=" * 73) print( '\n', jh.color('Uncaught Exception:', 'red'), jh.color(f'{exc_type.__name__}: {exc_value}', 'yellow') ) if jh.is_paper_trading(): print( jh.color( 'An uncaught exception was raised. Check the log file at:\nstorage/logs/paper-trade.txt', 'red' ) ) elif jh.is_livetrading(): print( jh.color( 'An uncaught exception was raised. Check the log file at:\nstorage/logs/live-trade.txt', 'red' ) ) elif jh.is_collecting_data(): print( jh.color( 'An uncaught exception was raised. Check the log file at:\nstorage/logs/collect.txt', 'red' ) )
def _check(self) -> None: """Based on the newly updated info, check if we should take action or not""" if not self._is_initiated: self._is_initiated = True if jh.is_live() and jh.is_debugging(): logger.info( f'Executing {self.name}-{self.exchange}-{self.symbol}-{self.timeframe}' ) # for caution to make sure testing on livetrade won't bleed your account if jh.is_test_driving() and store.completed_trades.count >= 2: logger.info('Maximum allowed trades in test-drive mode is reached') return if self._open_position_orders != [] and self.is_close and self.should_cancel( ): self._execute_cancel() # make sure order cancellation response is received via WS if jh.is_live(): # sleep a little until cancel is received via WS sleep(0.1) # just in case, sleep some more if necessary for _ in range(20): if store.orders.count_active_orders( self.exchange, self.symbol) == 0: break logger.info('sleeping 0.2 more seconds...') sleep(0.2) # If it's still not cancelled, something is wrong. Handle cancellation failure if store.orders.count_active_orders(self.exchange, self.symbol) != 0: raise exceptions.ExchangeNotResponding( 'The exchange did not respond as expected') if self.position.is_open: self._update_position() if jh.is_backtesting() or jh.is_unit_testing(): store.orders.execute_pending_market_orders() if self.position.is_close and self._open_position_orders == []: should_short = self.should_short() should_long = self.should_long() # validation if should_short and should_long: raise exceptions.ConflictingRules( 'should_short and should_long should not be true at the same time.' ) if should_long: self._execute_long() elif should_short: self._execute_short()
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()), msg), 'red')) store.logs.errors.append({'time': jh.now(), 'message': msg}) if jh.is_live(): msg = '[ERROR | {}] '.format(jh.timestamp_to_time(jh.now())[:19]) + str(msg) import logging logging.error(msg)
def error(msg: str) -> None: msg = str(msg) from jesse.store import store 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(f'ERROR:\n{msg}') if (jh.is_backtesting() and jh.is_debugging()) or jh.is_collecting_data(): print(jh.color(f'[{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 = f"[ERROR | {jh.timestamp_to_time(jh.now_to_timestamp())[:19]}] {msg}" logging.error(msg)
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)
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)
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)
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)
def generate_bigger_timeframes(self, candle: np.ndarray, exchange: str, symbol: str, with_execution: bool) -> None: if not jh.is_live(): return for timeframe in config['app']['considering_timeframes']: # skip '1m' if timeframe == '1m': continue last_candle = self.get_current_candle(exchange, symbol, timeframe) generate_from_count = int((candle[0] - last_candle[0]) / 60_000) short_candles = self.get_candles(exchange, symbol, '1m')[-1 - generate_from_count:] # update latest candle generated_candle = generate_candle_from_one_minutes( timeframe, short_candles, accept_forming_candles=True) self.add_candle(generated_candle, exchange, symbol, timeframe, with_execution, with_generation=False)
def _convert_to_numpy_array(arr, name): 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] prices = jh.round_price_for_live_mode(price, arr[:, 1]) qtys = jh.round_qty_for_live_mode(price, arr[:, 0]) arr[:, 0] = qtys arr[:, 1] = prices return arr except ValueError: raise exceptions.InvalidShape( 'The format of {} is invalid. \n' 'It must be (qty, price) or [(qty, price), (qty, price)] for multiple points; but {} was given'.format( name, arr ) )
def log(msg: str, log_type: str = 'info') -> None: msg = str(msg) if log_type == 'info': logger.info(msg) if jh.is_live(): notifier.notify(msg) elif log_type == 'error': logger.error(msg) if jh.is_live(): notifier.notify(msg) notifier.notify_urgently(msg) else: raise ValueError(f'log_type should be either "info" or "error". You passed {log_type}')
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)
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)
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)
def initiate_drivers(self) -> None: considering_exchanges = jh.get_config('app.considering_exchanges') # A helpful assertion if not len(considering_exchanges): raise Exception( 'No exchange is available for initiating in the API class') for e in considering_exchanges: if jh.is_live(): def initiate_ws(exchange_name: str) -> None: from jesse_live.info import SUPPORTED_EXCHANGES, SUPPORTED_EXCHANGES_NAMES exchange_class = jh.get_config( f'app.live_drivers.{exchange_name}') if exchange_name not in SUPPORTED_EXCHANGES_NAMES: exchange_names = '' for se in SUPPORTED_EXCHANGES: exchange_names += f'\n "{se["name"]}"' error_msg = f'Driver for "{exchange_name}" is not supported yet. Supported exchanges are: {exchange_names}' jh.error(error_msg, force_print=True) jh.terminate_app() self.drivers[exchange_name] = exchange_class() threading.Thread(target=initiate_ws, args=[e]).start() else: from jesse.exchanges import Sandbox self.drivers[e] = Sandbox(e)
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)
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 current_exchange = selectors.get_exchange(self.exchange) # skip rounding if the exchange doesn't have values for 'precisions' if 'precisions' not in current_exchange.vars: return arr price_precision = current_exchange.vars['precisions'][self.symbol]['price_precision'] qty_precision = current_exchange.vars['precisions'][self.symbol]['qty_precision'] prices = jh.round_price_for_live_mode(arr[:, 1], price_precision) qtys = jh.round_qty_for_live_mode(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' )
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)
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)
def generate_bigger_timeframes(self, candle: np.ndarray, exchange: str, symbol: str, with_execution: bool, is_forming_candle: bool): if not jh.is_live(): return for timeframe in config['app']['considering_timeframes']: # skip '1m' if timeframe == '1m': continue last_candle = self.get_current_candle(exchange, symbol, timeframe) generate_from_count = int((candle[0] - last_candle[0]) / 60_000) required_for_complete_candle = jh.timeframe_to_one_minutes( timeframe) short_candles = self.get_candles(exchange, symbol, '1m')[-1 - generate_from_count:] if generate_from_count == (required_for_complete_candle - 1) and not is_forming_candle: is_forming_candle = False else: is_forming_candle = True # update latest candle generated_candle = generate_candle_from_one_minutes( timeframe, short_candles, True) self.add_candle(generated_candle, exchange, symbol, timeframe, with_execution, with_generation=False, is_forming_candle=is_forming_candle)
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
def initiate_drivers(self): for e in jh.get_config('app.considering_exchanges'): if jh.is_live(): def initiate_ws(exchange_name: str): exchange_class = jh.get_config('app.live_drivers.{}'.format(exchange_name)) self.drivers[exchange_name] = exchange_class() threading.Thread(target=initiate_ws, args=[e]).start() else: from jesse.exchanges import Sandbox self.drivers[e] = Sandbox(e)
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)