def _reset_broker_tuple(self, *args, **kwargs): ''' extract broker data ''' broker_tuple = kwargs.get("broker", None) if broker_tuple: self.__clock = broker_tuple.clock self.__asset_finder = broker_tuple.asset_finder self.__broker_api = broker_tuple.broker self.__data_portal = broker_tuple.data_portal self.__auth = broker_tuple.auth else: self.__clock = kwargs.get("clock", self.__clock) self.__asset_finder = kwargs.get("asset_finder", self.__asset_finder) self.__broker_api = kwargs.get("api", self.__broker_api) self.__data_portal = kwargs.get("data_portal", self.__data_portal) self.__auth = kwargs.get("auth", self.__auth) # check for valid object types if self.__clock: if not isinstance(self.__clock, TradingClock): raise ValidationError(msg="clock supplied is of " "illegal type") if self.__asset_finder: if not isinstance(self.__asset_finder, AssetFinder): raise ValidationError(msg="asset finder supplied is of " "illegal type") if self.__data_portal: if not isinstance(self.__data_portal, DataPortal): raise ValidationError(msg="data portal supplied is of " "illegal type") if self.__broker_api: if not isinstance(self.__broker_api, AbstractBrokerAPI): raise ValidationError(msg="data portal supplied is of " "illegal type") if self.__auth: if not isinstance(self.__auth, AbstractAuth): raise ValidationError(msg="authentication supplied is of " "illegal type") self.__broker_tuple = Broker(self.__auth, self.__asset_finder, self.__data_portal, self.__broker_api, self.__clock, self.__broker_api._mode_supports) # authentication object can be null for backtester if self.__asset_finder and self.__data_portal and\ self.__broker_api and self.__clock: self.__broker_initialized = True
def live_run(self, alert_manager=None, publish_packets=False): ''' The entry point for a live run. ''' if self.mode != MODE.LIVE: raise StateMachineError(msg="mode must be live") if not self.context.is_initialized(): raise InitializationError(msg="context is not " "properly initialized") if not isinstance(self.context.clock, RealtimeClock): raise ValidationError(msg="clock must be real-time clock") # reset the clock at the start of the run, this also aligns # ticks to the nearest rounded bar depending on the frequency self._reset_clock(self.context.clock.emit_frequency) # initialize the coroutines clock_coro = self.context.clock.tick() algo_coro = self._run_live(alert_manager, publish_packets) try: tasks = asyncio.gather(clock_coro, algo_coro, return_exceptions=False) self._loop.run_until_complete(tasks) except BaseException as e: # TODO: do a proper exception handling here print("exception {}".format(e)) tasks.cancel() raise e finally: self._loop.close()
def _reset_parent(self, *args, **kwargs): new_parent = kwargs.get("parent", None) if new_parent: if not isinstance(self.parent, AlgoContext): raise ValidationError(msg="context parent is of " "invalid type", handling=ExceptionHandling.IGNORE) self.parent = new_parent
def save_config(self, file_path): ''' Write config to a file. ''' try: with open(file_path, 'w') as config_file: json.dump(self.to_dict(), config_file) except FileNotFoundError: msg='missing config file {config_file}' raise ValidationError(msg=msg)
def cancel_order(self, order_param): ''' Cancel existing order if not already executed. ''' if not self.is_TRADING_BAR(): msg = f"can't cancel order, market not open." raise ValidationError(msg=msg) order_id = order_param.oid if isinstance(order_param, Order)\ else order_param return self.context.broker.cancel_order(order_id)
def _back_test_generator(self, alert_manager=None): ''' The entry point for backtest run. This generator yields the current day performance. ''' if self.mode != MODE.BACKTEST: raise StateMachineError(msg="mode must be back-test") if not self.context.is_initialized(): raise InitializationError(msg="context is not " "properly initialized") if not isinstance(self.context.clock, SimulationClock): raise ValidationError(msg="clock must be simulation clock") self._make_broker_dispatch() # only useful for backtest for t, bar in self.context.clock: try: ts = pd.Timestamp(t, unit='ns', tz=self.context.trading_calendar.tz) if bar == BARS.ALGO_START: self.context.set_up(timestamp=ts) self.context.set_timestamp(ts) self._BROKER_FUNC_DISPATCH.get(bar, self._bar_noop)(ts) if bar == BARS.TRADING_BAR: #self.context.BAR_update(ts) pass if bar == BARS.AFTER_TRADING_HOURS: #self.context.BAR_update(ts) self.context.EOD_update(ts) perf = self.context.performance perf['timestamp'] = str(self.context.timestamp) yield perf self._USER_FUNC_DISPATCH.get(bar, self._bar_noop)(ts) except BlueShiftException as e: if not alert_manager: raise e else: timestamp = self.context.timestamp alert_manager.handle_error(e, 'algorithm', mode=self.mode, timestamp=timestamp) continue
def _create(self, *args, **kwargs): # pylint: disable=bad-super-call access_token = kwargs.pop('access_token', None) server = kwargs.pop('server', 'real') proxy_url = kwargs.pop('proxy_url', None) proxy_port = kwargs.pop('proxy_port', None) proxy_type = kwargs.pop('proxy_type', None) log_file = kwargs.pop('log_file', None) log_level = kwargs.pop('log_level', 'error') config_file = kwargs.pop('config_file', '') try: fxcmpyapi.__init__(self, access_token, config_file, log_file, log_level, server, proxy_url, proxy_port, proxy_type) except ServerError: msg = "access token missing" handling = ExceptionHandling.TERMINATE raise AuthenticationError(msg=msg, handling=handling) except RequestException as e: msg = "in broker.auth: " + str(e) handling = ExceptionHandling.WARN raise AuthenticationError(msg=msg, handling=handling) logger = get_logger() if logger: self.logger = logger APIRateLimitMixin.__init__(self, *args, **kwargs) # max instruments that can be queried at one call self._max_instruments = kwargs.pop("max_instruments", None) self._trading_calendar = kwargs.get("trading_calendar", None) if not self._rate_limit: # TODO: check FXCM rate limits self._rate_limit = 2 self._rate_limit_count = self._rate_limit if not self._rate_period: self._rate_period = 1 if not self._max_instruments: # 50 covers all currency pairs (at present 38) self._max_instruments = 50 # we reset this value on first call self._rate_limit_since = None if not self._trading_calendar: raise ValidationError(msg="missing calendar")
def set_up(self, *args, **kwargs): ''' Setting up the context before the algo start, or at any re-start, initializes timestamp and performance object ''' timestamp = kwargs.get("timestamp", None) if not timestamp: raise InitializationError(msg="timestamp required" " to initialize" " context") if not isinstance(timestamp, pd.Timestamp): raise ValidationError(msg="timestamp must be of type" " Timestamp") self.__timestamp = timestamp self._reset_performance(*args, **kwargs)
def _save_position_transactions(self, timestamp=None): pos_write = {} try: for pos in self._current_pos: pos_write[pos.symbol] = self._current_pos[pos].to_json() except (TypeError, KeyError, BlueShiftException): raise ValidationError(msg="corrupt positions data in blotter.") else: try: if pos_write: with open(self._pos_fname, 'w') as fp: json.dump(pos_write, fp) except (TypeError, OSError): msg = f"failed to write blotter data to {self._pos_fname}" handling = ExceptionHandling.WARN raise DataWriteException(msg=msg, handling=handling) self._txns_tracker._save_transactions_list(timestamp)
def update_instruments_list(self, instruments_list=None, valid_till=None): ''' Download the instruments list for the day, if not already downloaded before. If we fail, we cannot continue and so raise a TERMINATE level exception. ''' if instruments_list is not None: if not isinstance(instruments_list, pd.DataFrame): msg = "Invalid instruments list for {self.name}" handling = ExceptionHandling.TERMINATE raise ValidationError(msg=msg, handling=handling) self._instruments_list = instruments_list # check or set the expiry if valid_till is not None: # TODO: check for consistent timezone self._instruments_list_valid_till = valid_till else: t = pd.Timestamp.now(tz=self.tz) + pd.Timedelta(days=1) self._instruments_list_valid_till = t.normalize() if self._instruments_list is not None: t = pd.Timestamp.now(tz=self.tz) if t < self._instruments_list_valid_till: return try: self._instruments_list = pd.DataFrame(self._api.\ instruments()) self._filter_instruments_list() self._extract_expiries_underlyings() t = pd.Timestamp.now(tz=self.tz) + pd.Timedelta(days=1) self._instruments_list_valid_till = t.normalize() except KiteException as e: msg = str(e) handling = ExceptionHandling.TERMINATE raise APIException(msg=msg, handling=handling) except RequestException as e: msg = str(e) handling = ExceptionHandling.TERMINATE raise APIException(msg=msg, handling=handling)
def _reset_performance(self, *args, **kwargs): self.__performance = kwargs.get("perf", None) if not self.__performance: timestamp = kwargs.get("timestamp", None) if not timestamp: raise InitializationError(msg="timestamp required" " to initialize" " context") if not isinstance(timestamp, pd.Timestamp): raise ValidationError(msg="timestamp must be of type" " Timestamp") if not self.__tracker_initialized: raise InitializationError(msg="accounts still not " "initialized") self.__performance = Performance(self.__account, self.__timestamp.value) if not isinstance(self.__performance, Performance): raise InitializationError(msg="accounts still not " "initialized") self._perf_initialized = True
def _create(self, *args, **kwargs): # pylint: disable=bad-super-call #super(self.__class__, self).__init__(*args, **kwargs) api_key = kwargs.pop('api_key', None) access_token = kwargs.pop('access_token', None) root = kwargs.pop('root', None) debug = kwargs.pop('debug', False) timeout = kwargs.pop('timeout', None) proxies = kwargs.pop('proxies', None) pool = kwargs.pop('pool', None) disable_ssl = kwargs.pop('disable_ssl', False) KiteConnect.__init__(self, api_key, access_token, root, debug, timeout, proxies, pool, disable_ssl) APIRateLimitMixin.__init__(self, *args, **kwargs) # max instruments that can be queried at one call self._max_instruments = kwargs.pop("max_instruments", None) self._trading_calendar = kwargs.get("trading_calendar", None) if not self._rate_limit: # Kite has 3 per sec, we are conservative self._rate_limit = 2 self._rate_limit_count = self._rate_limit if not self._rate_period: self._rate_period = 1 if not self._max_instruments: # max allowed is 500 for current, and one for history self._max_instruments = 50 # we reset this value on first call self._rate_limit_since = None if not self._trading_calendar: raise ValidationError(msg="missing calendar")
def _asset_from_sym(self, sym): ''' Create an asset from a matching entry in the instruments list. ''' # pylint: disable=no-self-use # TODO: replace this by create_asset_from_dict from _assets # all instrument types are Forex. sym = sym.split(":")[0] if sym not in self._instruments_list: raise SymbolNotFound(f"no instruments with symbol {sym}") base, quote = tuple(sym.split('/')) if not base or not quote: raise ValidationError(f"Invalid symbol {sym} for Forex.") # TODO: hardcoded assumptions here on ticksize, generalize this. # NOTE: actual tick size is the inverse of this number!! if quote == 'JPY': tick_size = 100 else: tick_size = 10000 asset = Forex( -1, symbol=sym, ccy_pair=base + '/' + quote, base_ccy=base, quote_ccy=quote, name=sym, mult=1000, # the micro-lot tick_size=tick_size, ccy=quote, exchange_name='FXCM') return asset