def screen_rules(rules): if not misc.is_list(rules): return False for rule in rules: if not misc.is_str(rule): return False return True
def data_univ_as_of_date(val): if misc.is_list(val): for dt in val: if misc.parse_date(dt) is None: return False return True return misc.parse_date(val) is not None
def ranking_system(ranking): if misc.is_str(ranking) or misc.is_int(ranking): return True elif not misc.is_dict(ranking): return False nodes = ranking.get('Nodes') if nodes is None: return '"Nodes" property required' is_list = misc.is_list(nodes) if not is_list and not misc.is_str(nodes): return '"Nodes" property must be a list or a string containing a XML structure' for key, value in ranking.items(): if key == 'Nodes': pass elif key == 'Rank' and is_list: if not misc.is_str(value) or value not in ('Lower', 'Higher', 'Summation'): return '"Rank" property is invalid' elif key == 'Method': if not misc.is_str(value) or value.lower( ) not in cons.RANKING_METHOD: return '"Method" property is invalid' else: return f'Unexpected "{key}" property' if is_list: ret = screen_ranking_nodes(nodes) if not misc.is_bool(ret) or not ret: return ret return True
def universe(univ): if misc.is_str(univ): return True elif not misc.is_dict(univ): return False rules = None starting_universe = None for key, value in univ.items(): if key == 'Rules': if not misc.is_list(value): return '"Rules" property is invalid' rules = value elif key == 'Starting Universe': if not misc.is_number(value) and (not misc.is_str(value) or not value): return '"Starting Universe" property is invalid' starting_universe = value else: return f'Unexpected "{key}" property' if not rules: return '"Rules" property is missing' else: for key, value in enumerate(rules): if not misc.is_number(value) and (not misc.is_str(value) or not value): return f'"Rules" item #{key + 1} is empty' if starting_universe is None: return '"Starting Universe" property is missing' return True
def data_univ_formulas(val): if not misc.is_list(val): return False for item in val: if not (misc.is_number(item) or misc.is_str(item) or (misc.is_dict(item) and len(item) == 1)): return False return True
def generate_params(*, data: dict, settings, api_client: Client, logger: logging.Logger): """ Generate API compatible parameters dictionary from human readable ones :param data: data with meta info annotations :param settings: misc settings dictionary (like 'Type') :param api_client: API client (some transform functions may require it) :param logger: :return: dict """ params = {} for entry in data.values(): if not misc.is_dict(entry) or 'meta_info' not in entry: continue meta_info = entry['meta_info'] value = entry['value'] if 'transform' in meta_info: value = meta_info['transform'](value=entry['value'], settings=settings, api_client=api_client) if value is None: return target = params if meta_info.get('type') == 'screen': if meta_info.get('field') == 'screen': if misc.is_dict(params.get('screen')): logger.error('Invalid screen (mixing definition and ID)') return else: if 'screen' not in params: params['screen'] = {'type': settings['Type']} elif not misc.is_dict(params['screen']): logger.error('Invalid screen (mixing definition and ID)') return target = params['screen'] if meta_info['field'] == 'rules' and misc.is_list( target.get(meta_info['field'])) and misc.is_list(value): target[meta_info['field']] += value else: target[meta_info['field']] = value return params
def data_p123_uids(val): if misc.is_int(val): val = [val] elif misc.is_str(val): val = val.split(' ') if not misc.is_list(val): return False for item in val: try: int(item) except ValueError: return False return True
def screen_ranking(*, value, settings: dict, api_client: Client): if misc.is_str(value) or misc.is_int(value): return value params = {} if 'Formula' in value: params['formula'] = value['Formula'] if 'Lower is Better' in value: params['lowerIsBetter'] = value['Lower is Better'] else: nodes = value['Nodes'] if misc.is_list(nodes): nodes = screen_ranking_nodes_to_xml(nodes, main_rank=value.get('Rank')) rank_params = {'type': settings['Type'], 'nodes': nodes} if 'Method' in value: rank_params['rankingMethod'] = cons.RANKING_METHOD[value['Method'].lower()] api_client.rank_update(rank_params) params = 'ApiRankingSystem' return params
def data_tickers_cusips(val): return misc.is_int(val) or misc.is_str(val) or misc.is_list(val)
def screen_ranking_nodes(nodes: list, breadcrumbs='Nodes'): total_weight = 0 for idx, node in enumerate(nodes): if not misc.is_dict(node): return screen_ranking_node_error(breadcrumbs, idx, error='is invalid') if 'Type' not in node: return screen_ranking_node_error( breadcrumbs, idx, error='"Type" property is missing') node_type = node['Type'] if not misc.is_str(node_type)\ or node_type not in ('Composite', 'StockFormula', 'IndFormula', 'SecFormula', 'Conditional'): return screen_ranking_node_error( breadcrumbs, idx, error='"Type" property is invalid') weight = 0 rank = None sub_nodes = None formula = None scope = None true_nodes = None false_nodes = None for key, value in node.items(): if key == 'Type': pass elif key == 'Weight': if not misc.is_number(value) or value < 0 or value > 100: return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"Weight" property is invalid') weight = value elif key == 'Rank': if not misc.is_str(value): return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"Rank" property is invalid') rank = value elif key == 'Name': if not misc.is_number(value) and (not misc.is_str(value) or not value): return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"Name" property is invalid') elif key == 'Nodes' and node_type == 'Composite': if not misc.is_list(value): return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"Nodes" property is invalid') sub_nodes = value elif key == 'Formula' and node_type in ( 'Conditional', 'StockFormula', 'IndFormula', 'SecFormula'): if not misc.is_number(value) and (not misc.is_str(value) or not value): return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"Formula" property is invalid') formula = value elif key == 'Scope' and node_type == 'StockFormula': if not misc.is_str(value) or not len(value): return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"Scope" property is invalid') scope = value elif key == 'True Nodes' and node_type == 'Conditional': if not misc.is_list(value): return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"True Nodes" property is invalid') true_nodes = value elif key == 'False Nodes' and node_type == 'Conditional': if not misc.is_list(value): return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"False Nodes" property is invalid') false_nodes = value else: return f'Unexpected "{key}" property' if idx > 0 and (weight == 0) ^ (total_weight == 0): return screen_ranking_node_error( breadcrumbs, idx, node_type, error='Cannot mix 0 and non 0 weight nodes') total_weight += weight if node_type == 'Composite': if rank and rank not in ('Higher', 'Lower', 'Summation'): return screen_ranking_node_error(breadcrumbs, idx, node_type, '"Rank" property is invalid') if not sub_nodes: return screen_ranking_node_error( breadcrumbs, idx, node_type, error='"Nodes" property is missing') ret = screen_ranking_nodes( sub_nodes, f'{breadcrumbs} > {node_type} ({idx + 1})') if not misc.is_bool(ret) or not ret: return ret elif node_type in ('StockFormula', 'IndFormula', 'SecFormula'): if rank and rank not in ('Higher', 'Lower', 'Boolean'): return screen_ranking_node_error(breadcrumbs, idx, node_type, '"Rank" property is invalid') if formula is None: return screen_ranking_node_error( breadcrumbs, idx, node_type, '"Formula" property is missing') if scope and scope not in ('Universe', 'Industry', 'Sector'): return screen_ranking_node_error( breadcrumbs, idx, node_type, '"Scope" property is invalid') elif node_type == 'Conditional': if rank and rank not in ('Higher', 'Lower'): return screen_ranking_node_error(breadcrumbs, idx, node_type, '"Rank" property is invalid') if formula is None: return screen_ranking_node_error( breadcrumbs, idx, node_type, '"Formula" property is missing') if not true_nodes: return screen_ranking_node_error( breadcrumbs, idx, node_type, '"True Nodes" property is missing') ret = screen_ranking_nodes( true_nodes, f'{breadcrumbs} > {node_type} ({idx + 1}) True') if not misc.is_bool(ret) or not ret: return ret if not false_nodes: return screen_ranking_node_error( breadcrumbs, idx, node_type, '"False Nodes" property is missing') ret = screen_ranking_nodes( false_nodes, f'{breadcrumbs} > {node_type} ({idx + 1}) False') if not misc.is_bool(ret) or not ret: return ret if total_weight and total_weight != 100: return screen_ranking_node_error( breadcrumbs, error='Node weights do not add up to 100') return True