diff --git a/alpha_check/machine_lib.py b/alpha_check/machine_lib.py new file mode 100644 index 0000000..83dd751 --- /dev/null +++ b/alpha_check/machine_lib.py @@ -0,0 +1,760 @@ +import requests +from os import environ +from time import sleep +import time +import json +import pandas as pd +import random +import pickle +from urllib.parse import urljoin +from itertools import product +from itertools import combinations +from collections import defaultdict +import pickle + + + +basic_ops = ["reverse", "inverse", "rank", "zscore", "quantile", "normalize"] + +ts_ops = ["ts_rank", "ts_zscore", "ts_delta", "ts_sum", "ts_delay", + "ts_std_dev", "ts_mean", "ts_arg_min", "ts_arg_max","ts_scale", "ts_quantile"] + +ops_set = basic_ops + ts_ops + +def login(): + + username = "" + password = "" + + # Create a session to persistently store the headers + s = requests.Session() + + # Save credentials into session + s.auth = (username, password) + + # Send a POST request to the /authentication API + response = s.post('https://api.worldquantbrain.com/authentication') + print(response.content) + return s + + +def get_datasets( + s, + instrument_type: str = 'EQUITY', + region: str = 'USA', + delay: int = 1, + universe: str = 'TOP3000' +): + url = "https://api.worldquantbrain.com/data-sets?" +\ + f"instrumentType={instrument_type}®ion={region}&delay={str(delay)}&universe={universe}" + result = s.get(url) + datasets_df = pd.DataFrame(result.json()['results']) + return datasets_df + + +def get_datafields( + s, + instrument_type: str = 'EQUITY', + region: str = 'USA', + delay: int = 1, + universe: str = 'TOP3000', + dataset_id: str = '', + search: str = '' +): + if len(search) == 0: + url_template = "https://api.worldquantbrain.com/data-fields?" +\ + f"&instrumentType={instrument_type}" +\ + f"®ion={region}&delay={str(delay)}&universe={universe}&dataset.id={dataset_id}&limit=50" +\ + "&offset={x}" + count = s.get(url_template.format(x=0)).json()['count'] + + else: + url_template = "https://api.worldquantbrain.com/data-fields?" +\ + f"&instrumentType={instrument_type}" +\ + f"®ion={region}&delay={str(delay)}&universe={universe}&limit=50" +\ + f"&search={search}" +\ + "&offset={x}" + count = 100 + + datafields_list = [] + for x in range(0, count, 50): + datafields = s.get(url_template.format(x=x)) + datafields_list.append(datafields.json()['results']) + + datafields_list_flat = [item for sublist in datafields_list for item in sublist] + + datafields_df = pd.DataFrame(datafields_list_flat) + return datafields_df + +def get_vec_fields(fields): + + # 请在此处添加获得权限的Vector操作符 + vec_ops = ["vec_avg", "vec_sum"] + vec_fields = [] + + for field in fields: + for vec_op in vec_ops: + if vec_op == "vec_choose": + vec_fields.append("%s(%s, nth=-1)"%(vec_op, field)) + vec_fields.append("%s(%s, nth=0)"%(vec_op, field)) + else: + vec_fields.append("%s(%s)"%(vec_op, field)) + + return(vec_fields) + +def process_datafields(df): + + datafields = [] + datafields += df[df['type'] == "MATRIX"]["id"].tolist() + datafields += get_vec_fields(df[df['type'] == "VECTOR"]["id"].tolist()) + return ["winsorize(ts_backfill(%s, 120), std=4)"%field for field in datafields] + +def ts_factory(op, field): + output = [] + #days = [3, 5, 10, 20, 60, 120, 240] + days = [5, 22, 66, 120, 240] + + for day in days: + + alpha = "%s(%s, %d)"%(op, field, day) + output.append(alpha) + + return output + +def first_order_factory(fields, ops_set): + alpha_set = [] + #for field in fields: + for field in fields: + #reverse op does the work + alpha_set.append(field) + #alpha_set.append("-%s"%field) + for op in ops_set: + + if op == "ts_percentage": + + alpha_set += ts_comp_factory(op, field, "percentage", [0.5]) + + elif op == "ts_decay_exp_window": + + alpha_set += ts_comp_factory(op, field, "factor", [0.5]) + + elif op == "ts_moment": + + alpha_set += ts_comp_factory(op, field, "k", [2, 3, 4]) + + elif op == "ts_entropy": + + alpha_set += ts_comp_factory(op, field, "buckets", [10]) + + elif op.startswith("ts_") or op == "inst_tvr": + + alpha_set += ts_factory(op, field) + + elif op.startswith("vector"): + + alpha_set += vector_factory(op, field) + + elif op == "signed_power": + + alpha = "%s(%s, 2)"%(op, field) + alpha_set.append(alpha) + + else: + alpha = "%s(%s)"%(op, field) + alpha_set.append(alpha) + + return alpha_set + + +def load_task_pool(alpha_list, limit_of_children_simulations, limit_of_multi_simulations): + ''' + Input: + alpha_list : list of (alpha, decay) tuples + limit_of_multi_simulations : number of children simulation in a multi-simulation + limit_of_multi_simulations : number of simultaneous multi-simulations + Output: + task : [10 * (alpha, decay)] for a multi-simulation + pool : [10 * [10 * (alpha, decay)]] for simultaneous multi-simulations + pools : [[10 * [10 * (alpha, decay)]]] + + ''' + tasks = [alpha_list[i:i + limit_of_children_simulations] for i in range(0, len(alpha_list), limit_of_children_simulations)] + pools = [tasks[i:i + limit_of_multi_simulations] for i in range(0, len(tasks), limit_of_multi_simulations)] + return pools + +def multi_simulate(alpha_pools, neut, region, universe, start): + + s = login() + + brain_api_url = 'https://api.worldquantbrain.com' + + for x, pool in enumerate(alpha_pools): + if x < start: continue + progress_urls = [] + for y, task in enumerate(pool): + # 10 tasks, 10 alpha in each task + sim_data_list = generate_sim_data(task, region, universe, neut) + try: + simulation_response = s.post('https://api.worldquantbrain.com/simulations', json=sim_data_list) + simulation_progress_url = simulation_response.headers['Location'] + progress_urls.append(simulation_progress_url) + except: + print("location key error: %s"%simulation_response.content) + sleep(600) + s = login() + + print("pool %d task %d post done"%(x,y)) + + for j, progress in enumerate(progress_urls): + try: + while True: + simulation_progress = s.get(progress) + if simulation_progress.headers.get("Retry-After", 0) == 0: + break + #print("Sleeping for " + simulation_progress.headers["Retry-After"] + " seconds") + sleep(float(simulation_progress.headers["Retry-After"])) + + status = simulation_progress.json().get("status", 0) + if status != "COMPLETE": + print("Not complete : %s"%(progress)) + + """ + #alpha_id = simulation_progress.json()["alpha"] + children = simulation_progress.json().get("children", 0) + children_list = [] + for child in children: + child_progress = s.get(brain_api_url + "/simulations/" + child) + alpha_id = child_progress.json()["alpha"] + + set_alpha_properties(s, + alpha_id, + name = "%s"%name, + color = None,) + """ + except KeyError: + print("look into: %s"%progress) + except Exception as e: + print(f"other error: {e}") + + + print("pool %d task %d simulate done"%(x, j)) + + print("Simulate done") + +def generate_sim_data(alpha_list, region, uni, neut): + sim_data_list = [] + for alpha, decay in alpha_list: + simulation_data = { + 'type': 'REGULAR', + 'settings': { + 'instrumentType': 'EQUITY', + 'region': region, + 'universe': uni, + 'delay': 1, + 'decay': decay, + 'neutralization': neut, + 'truncation': 0.08, + 'pasteurization': 'ON', + 'testPeriod': 'P0Y', + 'unitHandling': 'VERIFY', + 'nanHandling': 'ON', + 'language': 'FASTEXPR', + 'visualization': False, + }, + 'regular': alpha} + + sim_data_list.append(simulation_data) + return sim_data_list + +def set_alpha_properties( + s, + alpha_id, + name: str = None, + color: str = None, + selection_desc: str = "None", + combo_desc: str = "None", + tags: str = ["ace_tag"], +): + """ + Function changes alpha's description parameters + """ + + params = { + "color": color, + "name": name, + "tags": tags, + "category": None, + "regular": {"description": None}, + "combo": {"description": combo_desc}, + "selection": {"description": selection_desc}, + } + response = s.patch( + "https://api.worldquantbrain.com/alphas/" + alpha_id, json=params + ) + +def get_alphas(start_date, end_date, sharpe_th, fitness_th, region, alpha_num, usage): + s = login() + output = [] + # 3E large 3C less + count = 0 + for i in range(0, alpha_num, 100): + print(i) + url_e = "https://api.worldquantbrain.com/users/self/alphas?limit=100&offset=%d"%(i) \ + + "&status=UNSUBMITTED%1FIS_FAIL&dateCreated%3E=2025-" + start_date \ + + "T00:00:00-04:00&dateCreated%3C2025-" + end_date \ + + "T00:00:00-04:00&is.fitness%3E" + str(fitness_th) + "&is.sharpe%3E" \ + + str(sharpe_th) + "&settings.region=" + region + "&order=-is.sharpe&hidden=false&type!=SUPER" + url_c = "https://api.worldquantbrain.com/users/self/alphas?limit=100&offset=%d"%(i) \ + + "&status=UNSUBMITTED%1FIS_FAIL&dateCreated%3E=2025-" + start_date \ + + "T00:00:00-04:00&dateCreated%3C2025-" + end_date \ + + "T00:00:00-04:00&is.fitness%3C-" + str(fitness_th) + "&is.sharpe%3C-" \ + + str(sharpe_th) + "&settings.region=" + region + "&order=is.sharpe&hidden=false&type!=SUPER" + urls = [url_e] + if usage != "submit": + urls.append(url_c) + for url in urls: + response = s.get(url) + #print(response.json()) + try: + alpha_list = response.json()["results"] + #print(response.json()) + for j in range(len(alpha_list)): + alpha_id = alpha_list[j]["id"] + name = alpha_list[j]["name"] + dateCreated = alpha_list[j]["dateCreated"] + sharpe = alpha_list[j]["is"]["sharpe"] + fitness = alpha_list[j]["is"]["fitness"] + turnover = alpha_list[j]["is"]["turnover"] + margin = alpha_list[j]["is"]["margin"] + longCount = alpha_list[j]["is"]["longCount"] + shortCount = alpha_list[j]["is"]["shortCount"] + decay = alpha_list[j]["settings"]["decay"] + exp = alpha_list[j]['regular']['code'] + count += 1 + #if (sharpe > 1.2 and sharpe < 1.6) or (sharpe < -1.2 and sharpe > -1.6): + if (longCount + shortCount) > 100: + if sharpe < -sharpe_th: + exp = "-%s"%exp + rec = [alpha_id, exp, sharpe, turnover, fitness, margin, dateCreated, decay] + print(rec) + if turnover > 0.7: + rec.append(decay*4) + elif turnover > 0.6: + rec.append(decay*3+3) + elif turnover > 0.5: + rec.append(decay*3) + elif turnover > 0.4: + rec.append(decay*2) + elif turnover > 0.35: + rec.append(decay+4) + elif turnover > 0.3: + rec.append(decay+2) + output.append(rec) + except: + print("%d finished re-login"%i) + s = login() + + print("count: %d"%count) + return output, s # 新增返回会话s + +def prune(next_alpha_recs, prefix, keep_num): + # prefix is the datafield prefix, fnd6, mdl175 ... + # keep_num is the num of top sharpe same-datafield alpha + output = [] + num_dict = defaultdict(int) + for rec in next_alpha_recs: + exp = rec[1] + field = exp.split(prefix)[-1].split(",")[0] + sharpe = rec[2] + if sharpe < 0: + field = "-%s"%field + if num_dict[field] < keep_num: + num_dict[field] += 1 + decay = rec[-1] + exp = rec[1] + output.append([exp,decay]) + return output + +def get_group_second_order_factory(first_order, group_ops, region): + second_order = [] + for fo in first_order: + for group_op in group_ops: + second_order += group_factory(group_op, fo, region) + return second_order + + +def group_factory(op, field, region): + output = [] + vectors = ["cap"] + + chn_group_13 = ['pv13_h_min2_sector', 'pv13_di_6l', 'pv13_rcsed_6l', 'pv13_di_5l', 'pv13_di_4l', + 'pv13_di_3l', 'pv13_di_2l', 'pv13_di_1l', 'pv13_parent', 'pv13_level'] + + + chn_group_1 = ['sta1_top3000c30','sta1_top3000c20','sta1_top3000c10','sta1_top3000c2','sta1_top3000c5'] + + chn_group_2 = ['sta2_top3000_fact4_c10','sta2_top2000_fact4_c50','sta2_top3000_fact3_c20'] + + hkg_group_13 = ['pv13_10_f3_g2_minvol_1m_sector', 'pv13_10_minvol_1m_sector', 'pv13_20_minvol_1m_sector', + 'pv13_2_minvol_1m_sector', 'pv13_5_minvol_1m_sector', 'pv13_1l_scibr', 'pv13_3l_scibr', + 'pv13_2l_scibr', 'pv13_4l_scibr', 'pv13_5l_scibr'] + + hkg_group_1 = ['sta1_allc50','sta1_allc5','sta1_allxjp_513_c20','sta1_top2000xjp_513_c5'] + + hkg_group_2 = ['sta2_all_xjp_513_all_fact4_c10','sta2_top2000_xjp_513_top2000_fact3_c10', + 'sta2_allfactor_xjp_513_13','sta2_top2000_xjp_513_top2000_fact3_c20'] + + twn_group_13 = ['pv13_2_minvol_1m_sector','pv13_20_minvol_1m_sector','pv13_10_minvol_1m_sector', + 'pv13_5_minvol_1m_sector','pv13_10_f3_g2_minvol_1m_sector','pv13_5_f3_g2_minvol_1m_sector', + 'pv13_2_f4_g3_minvol_1m_sector'] + + twn_group_1 = ['sta1_allc50','sta1_allxjp_513_c50','sta1_allxjp_513_c20','sta1_allxjp_513_c2', + 'sta1_allc20','sta1_allxjp_513_c5','sta1_allxjp_513_c10','sta1_allc2','sta1_allc5'] + + twn_group_2 = ['sta2_allfactor_xjp_513_0','sta2_all_xjp_513_all_fact3_c20', + 'sta2_all_xjp_513_all_fact4_c20','sta2_all_xjp_513_all_fact4_c50'] + + usa_group_13 = ['pv13_h_min2_3000_sector','pv13_r2_min20_3000_sector','pv13_r2_min2_3000_sector', + 'pv13_r2_min2_3000_sector', 'pv13_h_min2_focused_pureplay_3000_sector'] + + usa_group_1 = ['sta1_top3000c50','sta1_allc20','sta1_allc10','sta1_top3000c20','sta1_allc5'] + + usa_group_2 = ['sta2_top3000_fact3_c50','sta2_top3000_fact4_c20','sta2_top3000_fact4_c10'] + + usa_group_6 = ['mdl10_group_name'] + + asi_group_13 = ['pv13_20_minvol_1m_sector', 'pv13_5_f3_g2_minvol_1m_sector', 'pv13_10_f3_g2_minvol_1m_sector', + 'pv13_2_f4_g3_minvol_1m_sector', 'pv13_10_minvol_1m_sector', 'pv13_5_minvol_1m_sector'] + + asi_group_1 = ['sta1_allc50', 'sta1_allc10', 'sta1_minvol1mc50','sta1_minvol1mc20', + 'sta1_minvol1m_normc20', 'sta1_minvol1m_normc50'] + + jpn_group_1 = ['sta1_alljpn_513_c5', 'sta1_alljpn_513_c50', 'sta1_alljpn_513_c2', 'sta1_alljpn_513_c20'] + + jpn_group_2 = ['sta2_top2000_jpn_513_top2000_fact3_c20', 'sta2_all_jpn_513_all_fact1_c5', + 'sta2_allfactor_jpn_513_9', 'sta2_all_jpn_513_all_fact1_c10'] + + jpn_group_13 = ['pv13_2_minvol_1m_sector', 'pv13_2_f4_g3_minvol_1m_sector', 'pv13_10_minvol_1m_sector', + 'pv13_10_f3_g2_minvol_1m_sector', 'pv13_all_delay_1_parent', 'pv13_all_delay_1_level'] + + kor_group_13 = ['pv13_10_f3_g2_minvol_1m_sector', 'pv13_5_minvol_1m_sector', 'pv13_5_f3_g2_minvol_1m_sector', + 'pv13_2_minvol_1m_sector', 'pv13_20_minvol_1m_sector', 'pv13_2_f4_g3_minvol_1m_sector'] + + kor_group_1 = ['sta1_allc20','sta1_allc50','sta1_allc2','sta1_allc10','sta1_minvol1mc50', + 'sta1_allxjp_513_c10', 'sta1_top2000xjp_513_c50'] + + kor_group_2 =['sta2_all_xjp_513_all_fact1_c50','sta2_top2000_xjp_513_top2000_fact2_c50', + 'sta2_all_xjp_513_all_fact4_c50','sta2_all_xjp_513_all_fact4_c5'] + + eur_group_13 = ['pv13_5_sector', 'pv13_2_sector', 'pv13_v3_3l_scibr', 'pv13_v3_2l_scibr', 'pv13_2l_scibr', + 'pv13_52_sector', 'pv13_v3_6l_scibr', 'pv13_v3_4l_scibr', 'pv13_v3_1l_scibr'] + + eur_group_1 = ['sta1_allc10', 'sta1_allc2', 'sta1_top1200c2', 'sta1_allc20', 'sta1_top1200c10'] + + eur_group_2 = ['sta2_top1200_fact3_c50','sta2_top1200_fact3_c20','sta2_top1200_fact4_c50'] + + glb_group_13 = ["pv13_10_f2_g3_sector", "pv13_2_f3_g2_sector", "pv13_2_sector", "pv13_52_all_delay_1_sector"] + + glb_group_1 = ['sta1_allc20', 'sta1_allc10', 'sta1_allc50', 'sta1_allc5'] + + glb_group_2 = ['sta2_all_fact4_c50', 'sta2_all_fact4_c20', 'sta2_all_fact3_c20', 'sta2_all_fact4_c10'] + + glb_group_13 = ['pv13_2_sector', 'pv13_10_sector', 'pv13_3l_scibr', 'pv13_2l_scibr', 'pv13_1l_scibr', + 'pv13_52_minvol_1m_all_delay_1_sector','pv13_52_minvol_1m_sector','pv13_52_minvol_1m_sector'] + + amr_group_13 = ['pv13_4l_scibr', 'pv13_1l_scibr', 'pv13_hierarchy_min51_f1_sector', + 'pv13_hierarchy_min2_600_sector', 'pv13_r2_min2_sector', 'pv13_h_min20_600_sector'] + + #bps_group = "bucket(rank(fnd28_value_05480), range='0.1, 1, 0.1')" + #pb_group = "bucket(rank(close/fnd28_value_05480), range='0.1, 1, 0.1')" + cap_group = "bucket(rank(cap), range='0.1, 1, 0.1')" + asset_group = "bucket(rank(assets),range='0.1, 1, 0.1')" + sector_cap_group = "bucket(group_rank(cap, sector),range='0.1, 1, 0.1')" + sector_asset_group = "bucket(group_rank(assets, sector),range='0.1, 1, 0.1')" + + vol_group = "bucket(rank(ts_std_dev(returns,20)),range = '0.1, 1, 0.1')" + + liquidity_group = "bucket(rank(close*volume),range = '0.1, 1, 0.1')" + + groups = ["market","sector", "industry", "subindustry", + cap_group, asset_group, sector_cap_group, sector_asset_group, vol_group, liquidity_group] + + if region == "CHN": + groups += chn_group_13 + chn_group_1 + chn_group_2 + if region == "TWN": + groups += twn_group_13 + twn_group_1 + twn_group_2 + if region == "ASI": + groups += asi_group_13 + asi_group_1 + if region == "USA": + groups += usa_group_13 + usa_group_1 + usa_group_2 + if region == "HKG": + groups += hkg_group_13 + hkg_group_1 + hkg_group_2 + if region == "KOR": + groups += kor_group_13 + kor_group_1 + kor_group_2 + if region == "EUR": + groups += eur_group_13 + eur_group_1 + eur_group_2 + if region == "GLB": + groups += glb_group_13 + glb_group_1 + glb_group_2 + if region == "AMR": + groups += amr_group_13 + if region == "JPN": + groups += jpn_group_1 + jpn_group_2 + jpn_group_13 + + for group in groups: + if op.startswith("group_vector"): + for vector in vectors: + alpha = "%s(%s,%s,densify(%s))"%(op, field, vector, group) + output.append(alpha) + elif op.startswith("group_percentage"): + alpha = "%s(%s,densify(%s),percentage=0.5)"%(op, field, group) + output.append(alpha) + else: + alpha = "%s(%s,densify(%s))"%(op, field, group) + output.append(alpha) + + return output + + +def trade_when_factory(op,field,region): + output = [] + open_events = ["ts_arg_max(volume, 5) == 0", "ts_corr(close, volume, 20) < 0", + "ts_corr(close, volume, 5) < 0", "ts_mean(volume,10)>ts_mean(volume,60)", + "group_rank(ts_std_dev(returns,60), sector) > 0.7", "ts_zscore(returns,60) > 2", + "ts_arg_min(volume, 5) > 3", + "ts_std_dev(returns, 5) > ts_std_dev(returns, 20)", + "ts_arg_max(close, 5) == 0", "ts_arg_max(close, 20) == 0", + "ts_corr(close, volume, 5) > 0", "ts_corr(close, volume, 5) > 0.3", "ts_corr(close, volume, 5) > 0.5", + "ts_corr(close, volume, 20) > 0", "ts_corr(close, volume, 20) > 0.3", "ts_corr(close, volume, 20) > 0.5", + "ts_regression(returns, %s, 5, lag = 0, rettype = 2) > 0"%field, + "ts_regression(returns, %s, 20, lag = 0, rettype = 2) > 0"%field, + "ts_regression(returns, ts_step(20), 20, lag = 0, rettype = 2) > 0", + "ts_regression(returns, ts_step(5), 5, lag = 0, rettype = 2) > 0"] + + exit_events = ["abs(returns) > 0.1", "-1"] + + usa_events = ["rank(rp_css_business) > 0.8", "ts_rank(rp_css_business, 22) > 0.8", "rank(vec_avg(mws82_sentiment)) > 0.8", + "ts_rank(vec_avg(mws82_sentiment),22) > 0.8", "rank(vec_avg(nws48_ssc)) > 0.8", + "ts_rank(vec_avg(nws48_ssc),22) > 0.8", "rank(vec_avg(mws50_ssc)) > 0.8", "ts_rank(vec_avg(mws50_ssc),22) > 0.8", + "ts_rank(vec_sum(scl12_alltype_buzzvec),22) > 0.9", "pcr_oi_270 < 1", "pcr_oi_270 > 1",] + + asi_events = ["rank(vec_avg(mws38_score)) > 0.8", "ts_rank(vec_avg(mws38_score),22) > 0.8"] + + eur_events = ["rank(rp_css_business) > 0.8", "ts_rank(rp_css_business, 22) > 0.8", + "rank(vec_avg(oth429_research_reports_fundamental_keywords_4_method_2_pos)) > 0.8", + "ts_rank(vec_avg(oth429_research_reports_fundamental_keywords_4_method_2_pos),22) > 0.8", + "rank(vec_avg(mws84_sentiment)) > 0.8", "ts_rank(vec_avg(mws84_sentiment),22) > 0.8", + "rank(vec_avg(mws85_sentiment)) > 0.8", "ts_rank(vec_avg(mws85_sentiment),22) > 0.8", + "rank(mdl110_analyst_sentiment) > 0.8", "ts_rank(mdl110_analyst_sentiment, 22) > 0.8", + "rank(vec_avg(nws3_scores_posnormscr)) > 0.8", + "ts_rank(vec_avg(nws3_scores_posnormscr),22) > 0.8", + "rank(vec_avg(mws36_sentiment_words_positive)) > 0.8", + "ts_rank(vec_avg(mws36_sentiment_words_positive),22) > 0.8"] + + glb_events = ["rank(vec_avg(mdl109_news_sent_1m)) > 0.8", + "ts_rank(vec_avg(mdl109_news_sent_1m),22) > 0.8", + "rank(vec_avg(nws20_ssc)) > 0.8", + "ts_rank(vec_avg(nws20_ssc),22) > 0.8", + "vec_avg(nws20_ssc) > 0", + "rank(vec_avg(nws20_bee)) > 0.8", + "ts_rank(vec_avg(nws20_bee),22) > 0.8", + "rank(vec_avg(nws20_qmb)) > 0.8", + "ts_rank(vec_avg(nws20_qmb),22) > 0.8"] + + chn_events = ["rank(vec_avg(oth111_xueqiunaturaldaybasicdivisionstat_senti_conform)) > 0.8", + "ts_rank(vec_avg(oth111_xueqiunaturaldaybasicdivisionstat_senti_conform),22) > 0.8", + "rank(vec_avg(oth111_gubanaturaldaydevicedivisionstat_senti_conform)) > 0.8", + "ts_rank(vec_avg(oth111_gubanaturaldaydevicedivisionstat_senti_conform),22) > 0.8", + "rank(vec_avg(oth111_baragedivisionstat_regi_senti_conform)) > 0.8", + "ts_rank(vec_avg(oth111_baragedivisionstat_regi_senti_conform),22) > 0.8"] + + kor_events = ["rank(vec_avg(mdl110_analyst_sentiment)) > 0.8", + "ts_rank(vec_avg(mdl110_analyst_sentiment),22) > 0.8", + "rank(vec_avg(mws38_score)) > 0.8", + "ts_rank(vec_avg(mws38_score),22) > 0.8"] + + twn_events = ["rank(vec_avg(mdl109_news_sent_1m)) > 0.8", + "ts_rank(vec_avg(mdl109_news_sent_1m),22) > 0.8", + "rank(rp_ess_business) > 0.8", + "ts_rank(rp_ess_business,22) > 0.8"] + + for oe in open_events: + for ee in exit_events: + alpha = "%s(%s, %s, %s)"%(op, oe, field, ee) + output.append(alpha) + return output + + +def check_submission(alpha_bag, gold_bag, start): + depot = [] + s = login() + for idx, g in enumerate(alpha_bag): + if idx < start: + continue + if idx % 5 == 0: + print(idx) + if idx % 200 == 0: + s = login() + #print(idx) + pc = get_check_submission(s, g) + if pc == "sleep": + sleep(100) + s = login() + alpha_bag.append(g) + elif pc != pc: + # pc is nan + print("check self-corrlation error") + sleep(100) + alpha_bag.append(g) + elif pc == "fail": + continue + elif pc == "error": + depot.append(g) + else: + print(g) + gold_bag.append((g, pc)) + print(depot) + return gold_bag + +def get_check_submission(s, alpha_id): + while True: + result = s.get("https://api.worldquantbrain.com/alphas/" + alpha_id + "/check") + if "retry-after" in result.headers: + time.sleep(float(result.headers["Retry-After"])) + else: + break + try: + if result.json().get("is", 0) == 0: + print("logged out") + return "sleep" + checks_df = pd.DataFrame( + result.json()["is"]["checks"] + ) + pc = checks_df[checks_df.name == "PROD_CORRELATION"]["value"].values[0] + if not any(checks_df["result"] == "FAIL"): + return pc + else: + return "fail" + except: + print("catch: %s"%(alpha_id)) + return "error" + + +def view_alphas(gold_bag): + s = login() + sharp_list = [] + for gold, pc in gold_bag: + + triple = locate_alpha(s, gold) + info = [triple[0], triple[2], triple[3], triple[4], triple[5], triple[6], triple[1]] + info.append(pc) + sharp_list.append(info) + + sharp_list.sort(reverse=True, key = lambda x : x[1]) + for i in sharp_list: + print(i) + +def locate_alpha(s, alpha_id): + while True: + alpha = s.get("https://api.worldquantbrain.com/alphas/" + alpha_id) + if "retry-after" in alpha.headers: + time.sleep(float(alpha.headers["Retry-After"])) + else: + break + string = alpha.content.decode('utf-8') + metrics = json.loads(string) + #print(metrics["regular"]["code"]) + + dateCreated = metrics["dateCreated"] + sharpe = metrics["is"]["sharpe"] + fitness = metrics["is"]["fitness"] + turnover = metrics["is"]["turnover"] + margin = metrics["is"]["margin"] + decay = metrics["settings"]["decay"] + exp = metrics['regular']['code'] + + triple = [alpha_id, exp, sharpe, turnover, fitness, margin, dateCreated, decay] + return triple + + + +# some factory for other operators +def vector_factory(op, field): + output = [] + vectors = ["cap"] + + for vector in vectors: + + alpha = "%s(%s, %s)"%(op, field, vector) + output.append(alpha) + + return output + + +def ts_comp_factory(op, field, factor, paras): + output = [] + #l1, l2 = [3, 5, 10, 20, 60, 120, 240], paras + l1, l2 = [5, 22, 66, 240], paras + comb = list(product(l1, l2)) + + for day,para in comb: + + if type(para) == float: + alpha = "%s(%s, %d, %s=%.1f)"%(op, field, day, factor, para) + elif type(para) == int: + alpha = "%s(%s, %d, %s=%d)"%(op, field, day, factor, para) + + output.append(alpha) + + return output + +def twin_field_factory(op, field, fields): + + output = [] + #days = [3, 5, 10, 20, 60, 120, 240] + days = [5, 22, 66, 240] + outset = list(set(fields) - set([field])) + + for day in days: + for counterpart in outset: + alpha = "%s(%s, %s, %d)"%(op, field, counterpart, day) + output.append(alpha) + + return output + +def login_hk(): + + username = "" + password = "" + + # Create a session to persistently store the headers + s = requests.Session() + + # Save credentials into session + s.auth = (username, password) + + # Send a POST request to the /authentication API + response = s.post('https://api.worldquantbrain.com/authentication') + + if response.status_code == requests.codes.unauthorized: + # Check if biometrics is required + if response.headers.get("WWW-Authenticate") == "persona": + print( + "Complete biometrics authentication by scanning your face. Follow the link: \n" + + urljoin(response.url, response.headers["Location"]) + "\n" + ) + input("Press any key after you complete the biometrics authentication.") + + # Retry the authentication after biometrics + biometrics_response = s.post(urljoin(response.url, response.headers["Location"])) + + while biometrics_response.status_code != 201: + input("Biometrics authentication is not complete. Please try again and press any key when completed.") + biometrics_response = s.post(urljoin(response.url, response.headers["Location"])) + + print("Biometrics authentication completed.") + else: + print("\nIncorrect username or password. Please check your credentials.\n") + else: + print("Logged in successfully.") + + return s diff --git a/alpha_check/main.go b/alpha_check/main.go new file mode 100644 index 0000000..f99f280 --- /dev/null +++ b/alpha_check/main.go @@ -0,0 +1,477 @@ +package main + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "math" + "strconv" + "strings" + "time" + + "github.com/valyala/fasthttp" +) + +const ( + baseURL = "https://api.worldquantbrain.com" + zeroStreakThreshold = 5 * 252 + requiredDays = 2920 +) + +type Client struct { + client *fasthttp.Client + username string + password string +} + +type AlphaRecord struct { + ID string `json:"id"` + Name string `json:"name"` + DateCreated string `json:"dateCreated"` + Sharpe float64 `json:"sharpe"` + Fitness float64 `json:"fitness"` + Turnover float64 `json:"turnover"` + Margin float64 `json:"margin"` + LongCount float64 `json:"longCount"` + ShortCount float64 `json:"shortCount"` + Decay int `json:"decay"` + Code string `json:"code"` +} + +type AlphaResponse struct { + Results []struct { + ID string `json:"id"` + Name string `json:"name"` + DateCreated string `json:"dateCreated"` + Is map[string]interface{} `json:"is"` + Settings struct { + Decay int `json:"decay"` + } `json:"settings"` + Regular struct { + Code string `json:"code"` + } `json:"regular"` + } `json:"results"` +} + +type PnlResponse struct { + Records [][]interface{} `json:"records"` +} + +func NewClient(username, password string) *Client { + return &Client{ + client: &fasthttp.Client{}, + username: username, + password: password, + } +} + +func (c *Client) getAuthHeader() string { + auth := base64.StdEncoding.EncodeToString([]byte(c.username + ":" + c.password)) + return "Basic " + auth +} + +func (c *Client) Login() error { + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseRequest(req) + defer fasthttp.ReleaseResponse(resp) + + req.SetRequestURI(baseURL + "/authentication") + req.Header.SetMethod("POST") + req.Header.Set("Authorization", c.getAuthHeader()) + + err := c.client.Do(req, resp) + if err != nil { + return err + } + + fmt.Println(string(resp.Body())) + return nil +} + +func (c *Client) WaitGet(url string, maxRetries int) (*fasthttp.Response, error) { + retries := 0 + for retries < maxRetries { + for { + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + req.SetRequestURI(url) + req.Header.SetMethod("GET") + req.Header.Set("Authorization", c.getAuthHeader()) + + err := c.client.Do(req, resp) + if err != nil { + fasthttp.ReleaseRequest(req) + fasthttp.ReleaseResponse(resp) + return nil, err + } + + retryAfter := resp.Header.Peek("Retry-After") + if len(retryAfter) == 0 { + fasthttp.ReleaseRequest(req) + if resp.StatusCode() < 400 { + return resp, nil + } + fasthttp.ReleaseResponse(resp) + break + } + + sleepSec, _ := strconv.ParseFloat(string(retryAfter), 64) + time.Sleep(time.Duration(sleepSec) * time.Second) + fasthttp.ReleaseRequest(req) + fasthttp.ReleaseResponse(resp) + } + + time.Sleep(time.Duration(math.Pow(2, float64(retries))) * time.Second) + retries++ + } + + return nil, fmt.Errorf("max retries exceeded") +} + +func (c *Client) Get(url string) (*fasthttp.Response, error) { + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + req.SetRequestURI(url) + req.Header.SetMethod("GET") + req.Header.Set("Authorization", c.getAuthHeader()) + + err := c.client.Do(req, resp) + if err != nil { + fasthttp.ReleaseRequest(req) + fasthttp.ReleaseResponse(resp) + return nil, err + } + + return resp, nil +} + +func (c *Client) Patch(url string, data map[string]interface{}) (*fasthttp.Response, error) { + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseRequest(req) + + req.SetRequestURI(url) + req.Header.SetMethod("PATCH") + req.Header.Set("Authorization", c.getAuthHeader()) + req.Header.SetContentType("application/json") + + jsonData, _ := json.Marshal(data) + req.SetBody(jsonData) + + err := c.client.Do(req, resp) + if err != nil { + fasthttp.ReleaseResponse(resp) + return nil, err + } + + return resp, nil +} + +func GetAlphas(c *Client, startDate, endDate string, sharpeTh, fitnessTh float64, region string, alphaNum int, usage string) ([][]interface{}, *Client, error) { + output := make([][]interface{}, 0) + count := 0 + + for i := 0; i < alphaNum; i += 100 { + fmt.Println(i) + + urlE := fmt.Sprintf("%s/users/self/alphas?limit=100&offset=%d&status=UNSUBMITTED%%1FIS_FAIL&dateCreated%%3E=2025-%sT00:00:00-04:00&dateCreated%%3C2025-%sT00:00:00-04:00&is.fitness%%3E%f&is.sharpe%%3E%f&settings.region=%s&order=-is.sharpe&hidden=false&type!=SUPER", + baseURL, i, startDate, endDate, fitnessTh, sharpeTh, region) + + urlC := fmt.Sprintf("%s/users/self/alphas?limit=100&offset=%d&status=UNSUBMITTED%%1FIS_FAIL&dateCreated%%3E=2025-%sT00:00:00-04:00&dateCreated%%3C2025-%sT00:00:00-04:00&is.fitness%%3C-%f&is.sharpe%%3C-%f&settings.region=%s&order=is.sharpe&hidden=false&type!=SUPER", + baseURL, i, startDate, endDate, fitnessTh, sharpeTh, region) + + urls := []string{urlE} + if usage != "submit" { + urls = append(urls, urlC) + } + + for _, url := range urls { + resp, err := c.Get(url) + if err != nil { + fmt.Printf("%d finished re-login\n", i) + c.Login() + continue + } + + var alphaResp AlphaResponse + if err := json.Unmarshal(resp.Body(), &alphaResp); err != nil { + fasthttp.ReleaseResponse(resp) + fmt.Printf("%d finished re-login\n", i) + c.Login() + continue + } + fasthttp.ReleaseResponse(resp) + + for _, item := range alphaResp.Results { + alphaID := item.ID + name := item.Name + dateCreated := item.DateCreated + sharpe := getFloat(item.Is, "sharpe") + fitness := getFloat(item.Is, "fitness") + turnover := getFloat(item.Is, "turnover") + margin := getFloat(item.Is, "margin") + longCount := getFloat(item.Is, "longCount") + shortCount := getFloat(item.Is, "shortCount") + decay := item.Settings.Decay + exp := item.Regular.Code + + count++ + + if (longCount + shortCount) > 100 { + if sharpe < -sharpeTh { + exp = "-" + exp + } + + rec := []interface{}{alphaID, exp, sharpe, turnover, fitness, margin, dateCreated, decay} + fmt.Println(rec) + + if turnover > 0.7 { + rec = append(rec, float64(decay)*4) + } else if turnover > 0.6 { + rec = append(rec, float64(decay)*3+3) + } else if turnover > 0.5 { + rec = append(rec, float64(decay)*3) + } else if turnover > 0.4 { + rec = append(rec, float64(decay)*2) + } else if turnover > 0.35 { + rec = append(rec, float64(decay)+4) + } else if turnover > 0.3 { + rec = append(rec, float64(decay)+2) + } + + output = append(output, rec) + } + } + } + } + + fmt.Printf("count: %d\n", count) + return output, c, nil +} + +func getFloat(m map[string]interface{}, key string) float64 { + if v, ok := m[key]; ok { + switch val := v.(type) { + case float64: + return val + case int: + return float64(val) + } + } + return 0 +} + +func CheckConsecutiveNonZeroValues(alphaID string, data [][]interface{}, requiredStreak int) bool { + if len(data) < requiredStreak { + return true + } + + checkColumn := func(columnData []float64) bool { + if len(columnData) < requiredStreak { + return true + } + + currentStreakCount := 0 + var currentStreakValue interface{} + + for _, value := range columnData { + if value != 0 { + if currentStreakValue != nil && value == currentStreakValue { + currentStreakCount++ + } else { + currentStreakValue = value + currentStreakCount = 1 + } + } else { + currentStreakValue = nil + currentStreakCount = 0 + } + + if currentStreakCount >= requiredStreak { + return false + } + } + return true + } + + var column1Values, column2Values []float64 + for _, row := range data { + if len(row) >= 3 { + if v, ok := row[1].(float64); ok { + column1Values = append(column1Values, v) + } + if v, ok := row[2].(float64); ok { + column2Values = append(column2Values, v) + } + } + } + + if len(column1Values) > 0 && len(column2Values) > 0 { + isCol1AllZeros := allZeros(column1Values) + isCol2AllZeros := allZeros(column2Values) + if isCol1AllZeros || isCol2AllZeros { + fmt.Println(alphaID, "不合法") + return false + } + } + + if !checkColumn(column1Values) { + fmt.Println(alphaID, "不合法") + return false + } + + if !checkColumn(column2Values) { + fmt.Println(alphaID, "不合法") + return false + } + + return true +} + +func allZeros(arr []float64) bool { + for _, v := range arr { + if v != 0 { + return false + } + } + return true +} + +func GetAlphaPnlLegal(c *Client, alphaID string) bool { + notLegalID := make([]string, 0) + + url := baseURL + "/alphas/" + alphaID + "/recordsets/pnl" + resp, err := c.WaitGet(url, 10) + if err != nil { + return false + } + defer fasthttp.ReleaseResponse(resp) + + var pnlResp PnlResponse + if err := json.Unmarshal(resp.Body(), &pnlResp); err != nil { + return false + } + + records := pnlResp.Records + if len(records) == 0 { + return false + } + + var dateList []time.Time + for _, record := range records { + if len(record) == 0 { + continue + } + dateStr, ok := record[0].(string) + if !ok { + return false + } + dateObj, err := time.Parse("2006-01-02", dateStr) + if err != nil { + return false + } + dateList = append(dateList, dateObj) + } + + if len(dateList) == 0 { + return false + } + + minDate := dateList[0] + maxDate := dateList[0] + for _, d := range dateList { + if d.Before(minDate) { + minDate = d + } + if d.After(maxDate) { + maxDate = d + } + } + + totalDays := int(maxDate.Sub(minDate).Hours() / 24) + if totalDays < requiredDays { + return false + } + + col1Zeros := make([]bool, 0) + for _, record := range records { + if len(record) >= 2 { + if v, ok := record[1].(float64); ok { + col1Zeros = append(col1Zeros, v == 0) + } + } + } + + col1MaxZeroStreak := maxConsecutiveZeros(col1Zeros) + + if col1MaxZeroStreak >= zeroStreakThreshold { + fmt.Printf("%s 不合法:存在连续%d年零值\n", alphaID, zeroStreakThreshold/252) + notLegalID = append(notLegalID, alphaID) + return false + } + + if !CheckConsecutiveNonZeroValues(alphaID, records, 200) { + return false + } + + _ = notLegalID + return true +} + +func maxConsecutiveZeros(arr []bool) int { + maxStreak := 0 + currentStreak := 0 + for _, val := range arr { + if val { + currentStreak++ + if currentStreak > maxStreak { + maxStreak = currentStreak + } + } else { + currentStreak = 0 + } + } + return maxStreak +} + +func Mute(c *Client, alphaID string) { + url := baseURL + "/alphas/" + alphaID + data := map[string]interface{}{ + "hidden": true, + } + c.Patch(url, data) +} + +func main() { + client := NewClient("", "") + client.Login() + + foTracker, c, err := GetAlphas(client, "12-01", "12-31", 1, 0.5, "USA", 1000, "submit") + if err != nil { + fmt.Println("Error:", err) + return + } + + fNum := len(foTracker) + fmt.Printf("%d 个alpha 进行pnl合法检测,请耐心等待\n", fNum) + fmt.Println(len(foTracker)) + + count := 0 + for i := len(foTracker) - 1; i >= 0; i-- { + if count%25 == 0 { + fmt.Printf("=========== %d ===========\n", count) + } + count++ + + alphaID, ok := foTracker[i][0].(string) + if !ok { + continue + } + + if !GetAlphaPnlLegal(c, alphaID) { + fmt.Println(alphaID, "已经隐藏") + Mute(c, alphaID) + } + } +} diff --git a/alpha_check/main.py b/alpha_check/main.py new file mode 100644 index 0000000..a738d33 --- /dev/null +++ b/alpha_check/main.py @@ -0,0 +1,223 @@ +import time +import httpx +import datetime + + +def login(): + username = "jack0210_@hotmail.com" + password = "!QAZ2wsx+0913" + + timeout = httpx.Timeout(60.0, connect=10.0) + limits = httpx.Limits(max_keepalive_connections=20, max_connections=100) + transport = httpx.HTTPTransport(retries=3) + + s = httpx.Client( + auth=(username, password), + timeout=timeout, + limits=limits, + transport=transport + ) + response = s.post('https://api.worldquantbrain.com/authentication') + print(response.content) + return s + + +def wait_get(s, url: str, max_retries: int = 10): + retries = 0 + while retries < max_retries: + while True: + simulation_progress = s.get(url) + if simulation_progress.headers.get("Retry-After", 0) == 0: + break + time.sleep(float(simulation_progress.headers["Retry-After"])) + if simulation_progress.status_code < 400: + break + else: + time.sleep(2 ** retries) + retries += 1 + return simulation_progress + + +def get_alphas(start_date, end_date, sharpe_th, fitness_th, region, alpha_num, usage): + s = login() + output = [] + count = 0 + for i in range(0, alpha_num, 100): + print(i) + url_e = "https://api.worldquantbrain.com/users/self/alphas?limit=100&offset=%d" % (i) \ + + "&status=UNSUBMITTED%1FIS_FAIL&dateCreated%3E=2025-" + start_date \ + + "T00:00:00-04:00&dateCreated%3C2025-" + end_date \ + + "T00:00:00-04:00&is.fitness%3E" + str(fitness_th) + "&is.sharpe%3E" \ + + str(sharpe_th) + "&settings.region=" + region + "&order=-is.sharpe&hidden=false&type!=SUPER" + url_c = "https://api.worldquantbrain.com/users/self/alphas?limit=100&offset=%d" % (i) \ + + "&status=UNSUBMITTED%1FIS_FAIL&dateCreated%3E=2025-" + start_date \ + + "T00:00:00-04:00&dateCreated%3C2025-" + end_date \ + + "T00:00:00-04:00&is.fitness%3C-" + str(fitness_th) + "&is.sharpe%3C-" \ + + str(sharpe_th) + "&settings.region=" + region + "&order=is.sharpe&hidden=false&type!=SUPER" + urls = [url_e] + if usage != "submit": + urls.append(url_c) + for url in urls: + response = s.get(url) + try: + alpha_list = response.json()["results"] + for j in range(len(alpha_list)): + alpha_id = alpha_list[j]["id"] + name = alpha_list[j]["name"] + dateCreated = alpha_list[j]["dateCreated"] + sharpe = alpha_list[j]["is"]["sharpe"] + fitness = alpha_list[j]["is"]["fitness"] + turnover = alpha_list[j]["is"]["turnover"] + margin = alpha_list[j]["is"]["margin"] + longCount = alpha_list[j]["is"]["longCount"] + shortCount = alpha_list[j]["is"]["shortCount"] + decay = alpha_list[j]["settings"]["decay"] + exp = alpha_list[j]['regular']['code'] + count += 1 + if (longCount + shortCount) > 100: + if sharpe < -sharpe_th: + exp = "-%s" % exp + rec = [alpha_id, exp, sharpe, turnover, fitness, margin, dateCreated, decay] + print(rec) + if turnover > 0.7: + rec.append(decay * 4) + elif turnover > 0.6: + rec.append(decay * 3 + 3) + elif turnover > 0.5: + rec.append(decay * 3) + elif turnover > 0.4: + rec.append(decay * 2) + elif turnover > 0.35: + rec.append(decay + 4) + elif turnover > 0.3: + rec.append(decay + 2) + output.append(rec) + except: + print("%d finished re-login" % i) + s = login() + + print("count: %d" % count) + return output, s + + +def check_consecutive_non_zero_values(alpha_id, data, required_streak=200): + if not data or len(data) < required_streak: + return True + + def check_column(column_data): + if len(column_data) < required_streak: + return True + + current_streak_count = 0 + current_streak_value = None + + for value in column_data: + if value != 0: + if value == current_streak_value: + current_streak_count += 1 + else: + current_streak_value = value + current_streak_count = 1 + else: + current_streak_value = None + current_streak_count = 0 + + if current_streak_count >= required_streak: + return False + return True + + column1_values = [] + column2_values = [] + for row in data: + if len(row) >= 3: + column1_values.append(row[1]) + column2_values.append(row[2]) + + if column1_values and column2_values: + is_col1_all_zeros = all(v == 0 for v in column1_values) + is_col2_all_zeros = all(v == 0 for v in column2_values) + if is_col1_all_zeros or is_col2_all_zeros: + print(alpha_id, "不合法") + return False + + if not check_column(column1_values): + print(alpha_id, "不合法") + return False + + if not check_column(column2_values): + print(alpha_id, "不合法") + return False + + return True + + +def get_alpha_pnl_legal(s, alpha_id: str) -> bool: + not_legal_id = [] + pnl = wait_get(s, "https://api.worldquantbrain.com/alphas/" + alpha_id + "/recordsets/pnl").json() + records = pnl["records"] + + if not records: + return False + + date_list = [] + for record in records: + try: + date_obj = datetime.datetime.strptime(record[0], '%Y-%m-%d').date() + date_list.append(date_obj) + except Exception: + return False + + min_date = min(date_list) + max_date = max(date_list) + total_days = (max_date - min_date).days + if total_days < 2920: + return False + + zero_streak_threshold = 5 * 252 + col1_zeros = [record[1] == 0 for record in records] + + def max_consecutive_zeros(arr): + max_streak = current_streak = 0 + for val in arr: + current_streak = current_streak + 1 if val else 0 + max_streak = max(max_streak, current_streak) + return max_streak + + col1_max_zero_streak = max_consecutive_zeros(col1_zeros) + + if col1_max_zero_streak >= zero_streak_threshold: + print(f"{alpha_id} 不合法:存在连续{zero_streak_threshold // 252}年零值") + not_legal_id.append(str(alpha_id)) + return False + + if not check_consecutive_non_zero_values(alpha_id, records): + return False + + return True + + +def mute(s, alpha_id): + url = "https://api.worldquantbrain.com/alphas/" + alpha_id + data = { + "hidden": True + } + response = s.patch(url, json=data) + + +def main(): + fo_tracker, s = get_alphas('12-01', '12-31', 1, 0.5, 'USA', 1000, 'submit') + f_num = len(fo_tracker) + print(f_num, "个alpha 进行pnl合法检测,请耐心等待") + count = 0 + print(len(fo_tracker)) + for i in fo_tracker[::-1][0:]: + if count % 25 == 0: + print('===========', count, '===========') + count += 1 + if get_alpha_pnl_legal(s, i[0]) == False: + print(i[0], '已经隐藏') + mute(s, i[0]) + + +if __name__ == "__main__": + main()