def __init__(self, pf_name): super().__init__() self.pf_name = pf_name self.positions = defaultdict(Asset) self._logger_ = logs.get_logger(SubPortfolio, types='stream')
def latest_file(path_name, keyword='', ext='', **kwargs) -> str: """ Latest modified file in folder Args: path_name: full path name keyword: keyword to search ext: file extension Returns: str: latest file name Examples: >>> target_folder = Path(abspath(__file__)) / 'tests/folders' >>> _ = target_folder.joinpath('test_2.yml').write_text('modified: 1') >>> latest_file(target_folder, keyword='test', ext='yml').split('/')[-1] 'test_2.yml' >>> latest_file(target_folder / 'notfound') '' """ files = sort_by_modified( all_files(path_name=path_name, keyword=keyword, ext=ext, full_path=True)) if not files: from xone import logs logger = logs.get_logger(latest_file, level=kwargs.pop('log', 'warning')) logger.debug(f'file is not found in folder: {path_name}') return '' return str(files[0]).replace('\\', '/')
def __init__(self, init_cash, trade_on='price', mark_on='price', **kwargs): """ Args: init_cash: initial cash trade_on: field to trade on mark_on: price field for mark-to-market purpose **kwargs: other kwargs for the class """ super().__init__() self.init_cash = init_cash self.trade_on = trade_on self.mark_on = mark_on if trade_on in ['bid_ask', 'ask_bid']: self._buy_on_, self._sell_on_ = 'ask', 'bid' else: self._buy_on_, self._sell_on_ = trade_on, trade_on # Internal values to track positions and performance self._cash_ = init_cash self._commission_ = defaultdict(float) self._market_value_ = 0. self._margin_ = 0. self._positions_ = defaultdict(Asset) self._sub_pf_ = defaultdict(SubPortfolio) self._performance_ = [] self._tolerance_ = kwargs.pop('tolerance', 5e5) self._cur_dt_ = None self.info = kwargs self._logger_ = logs.get_logger(Portfolio, types='stream', level='info')
def __init__(self, port_name, asset: Asset, snapshot, quantity, trade_on): """ Args: port_name: portfolio name asset: asset snapshot: market snapshot quantity: quantity, can be + / - trade_on: price field to trade """ super().__init__() self.port_name = port_name self.ticker = asset.ticker self.price = snapshot[self.ticker][trade_on] self.lot_size = asset.lot_size self.quantity = quantity self._comms_ = asset.comms.calculate(transaction=Trans( quantity=quantity * self.lot_size, fill_cost=self.price )) self._logger_ = logs.get_logger(Transaction, types='stream') self.total_notional = round(self.price * self.lot_size * quantity, 2) self.comm_total = self._comms_.total_comm self.comm_in_bps = self._comms_.in_bps self._logger_.info( f'{port_name}:{self.ticker}:qty={show_qty(quantity)}:' f'cost={show_value(self.price, digit=2)}:lot={show_qty(self.lot_size)}:' f'notional={show_value(self.total_notional)}:' f'comms={self.comm_total}:in_bps={self.comm_in_bps}' )
def __init__(self, timestamp, strategy: str, assets: list, order_type='MKT', target_value=None, weights=None, target_qty=None, **kwargs): """ Args: assets: list of assets order_type: order type, 'MKT' (default) or 'LMT' target_value: target notional weights: dict, weights of each asset target_qty: dict, target quantity for each asset """ self._logger_ = logs.get_logger(SignalEvent, types='steram') assert (target_value is None) and (target_qty is None), ValueError( f'[{self.__class__.__name__}] Both target value and quantity are empty' ) assert all(isinstance(asset, Asset) for asset in assets), ValueError( f'[{self.__class__.__name__}] Not all assets are of Asset class') super().__init__() self.timestamp = pd.Timestamp(timestamp) self.strategy = strategy self.assets = assets self.order_type = order_type self.target_value = target_value self.weights = proper_weights(assets=assets, weights=weights) self.target_qty = target_qty self.info = kwargs self._order_type_ = LimitOrderEvent \ if order_type.upper() == 'LMT' else MarketOrderEvent
def page_source(url: str, browser: str = '', click: str = '', headless=True, *args) -> PageSource: """ Page source from url Args: url: url browser: one of firefox, chrome and ie click: element to click - useful for scroll down page will use browser if given - default browser Chrome headless: whether to show browser Returns: PageSource """ logger = logs.get_logger(page_source) if click and (not browser): browser = 'chrome' if not browser: page = requests.get(url) if page.status_code == 200: return PageSource( source=page.content, soup=BeautifulSoup(page.content, 'lxml'), ) else: logger.warning( f'Invalid status code {page.status_code} for url:\n{url}') return PageSource(source='', soup=BeautifulSoup('', 'lxml')) if browser not in BROWSERS: raise LookupError(f'Cannot find browser: {browser}.\n' f'Valid browsers: {list(BROWSERS.keys())}.') with get_browser(browser, headless=headless, *args) as driver: driver.get(url=url) if click: try: driver.find_element_by_xpath(click).click() except (NoSuchElementException, ElementClickInterceptedException) as e: logger.error(e) time.sleep(1) return PageSource( source=driver.page_source, soup=BeautifulSoup(driver.page_source, 'lxml'), )
def load_file(data_file: str, load_func=None, **kwargs): """ Load data from cache """ logger = logs.get_logger(load_file, level=kwargs.get('log', 'info')) if (not data_file) or (not files.exists(data_file)): return if callable(load_func): return load_func(data_file) ext = data_file.split('.')[-1] if ext not in LOAD_FUNC: return logger.debug(f'Reading from {data_file} ...') return LOAD_FUNC[ext](data_file)
def run(func, keys, max_procs=None, affinity=None, proc_desc='', **kwargs): """ Provide interface for multiprocessing Args: func: callable functions keys: keys in kwargs that want to use process max_procs: max number of processes affinity: CPU affinity proc_desc: prefix for progressbar **kwargs: kwargs for func """ logger = logs.get_logger(run, level=kwargs.get('log', 'info')) if max_procs is None: max_procs = cpu_count() kw_arr = saturate_kwargs(keys=keys, **kwargs) if len(kw_arr) == 0: return if isinstance(affinity, int): try: import win32process import win32api win32process.SetProcessAffinityMask(win32api.GetCurrentProcess(), affinity) except Exception as e: logger.error(str(e)) task_queue = queue.Queue() with tqdm(total=len(kw_arr), desc=proc_desc) as bar: while len(kw_arr) > 0: for _ in range(max_procs): if len(kw_arr) == 0: break kw = kw_arr.pop(0) bar.update() p = Process(target=func, kwargs=kw) p.start() sys.stdout.flush() task_queue.put(p) while not task_queue.empty(): p = task_queue.get() p.join()
def wrapper(*args, **kwargs): default.update(kwargs) kwargs.update(default) cur_mod = sys.modules[func.__module__] logger = logs.get_logger( name_or_func=f'{cur_mod.__name__}.{func.__name__}', types='stream') root_path = cur_mod.DATA_PATH date_type = kwargs.pop('date_type', 'date') save_static = kwargs.pop('save_static', True) save_dynamic = kwargs.pop('save_dynamic', True) symbol = kwargs.get('symbol') file_kw = dict(func=func, symbol=symbol, root=root_path, date_type=date_type) d_file = cache_file(has_date=True, **file_kw) s_file = cache_file(has_date=False, **file_kw) cached = kwargs.pop('cached', False) if cached and save_static and files.exists(s_file): logger.info(f'Reading data from {s_file} ...') return pd.read_parquet(s_file) data = func(*args, **kwargs) if save_static: files.create_folder(s_file, is_file=True) save_data(data=data, file_fmt=s_file, append=False) logger.info(f'Saved data file to {s_file} ...') if save_dynamic: drop_dups = kwargs.pop('drop_dups', None) files.create_folder(d_file, is_file=True) save_data(data=data, file_fmt=d_file, append=True, drop_dups=drop_dups) logger.info(f'Saved data file to {d_file} ...') return data
def save_file(data, data_file: str, save_func=None, **kwargs): """ Save data """ logger = logs.get_logger(save_file, level=kwargs.get('log', 'info')) if not data_file: return if isinstance(data, (pd.Series, pd.DataFrame)) and data.empty: return files.create_folder(data_file, is_file=True) if callable(save_func): logger.debug(f'Saving data to {data_file} ...') save_func(data=data, data_file=data_file) ext = data_file.split('.')[-1] save_kw = {} if ext in ['csv', 'xls', 'xlsx']: save_kw['index'] = False save_func = SAVE_FUNC.get(ext, '__nothing__') if not hasattr(data, save_func): return logger.debug(f'Saving data to {data_file} ...') getattr(data, save_func)(data_file, **save_kw)
def __init__(self, events_queue, inst_exec: bool, trade_on: str): """ Args: inst_exec: if instant execution enabled trade_on: valid inputs - ['price', 'bid_ask', 'bid_ask_1', ...] """ super().__init__() self.inst_exec = inst_exec self.trade_on = trade_on if ('bid' in trade_on) and ('ask' in trade_on): lvl = trade_on[-1] if lvl.isdigit(): self._buy_on_, self._sell_on_ = f'ask_{lvl}', f'bid_{lvl}' else: self._buy_on_, self._sell_on_ = 'ask', 'bid' else: self._sell_on_, self._sell_on_ = trade_on, trade_on self._timestamp_ = None self._snap_ = MarketSnapshot() self.events_queue = events_queue self._logger_ = logs.get_logger(SimulationEngine, types='stream')
def latest_file(path_name, keyword='', ext='', **kwargs) -> str: """ Latest modified file in folder Args: path_name: full path name keyword: keyword to search ext: file extension Returns: str: latest file name Examples: >>> target_folder = f'{abspath(__file__)}/tests/folders' >>> latest_file(target_folder, keyword='test', ext='yml').split('/')[-1] 'test_2.yml' >>> latest_file(f'{target_folder}/notfound') '' """ files = all_files(path_name=path_name, keyword=keyword, ext=ext, full_path=True) if not files: from xone import logs logger = logs.get_logger(latest_file, level=kwargs.pop('log', 'warning')) logger.debug(f'file is not found in folder: {path_name}') return '' modified_time = [os.path.getmtime(f) for f in files] files = [f for (dt, f) in sorted(zip(modified_time, files))] return files[-1]