{"id":37073279,"url":"https://github.com/rocky-d/wqb","last_synced_at":"2026-01-14T08:36:06.045Z","repository":{"id":274726911,"uuid":"922810659","full_name":"rocky-d/wqb","owner":"rocky-d","description":"A better machine lib.","archived":false,"fork":false,"pushed_at":"2025-02-22T15:10:42.000Z","size":1528,"stargazers_count":208,"open_issues_count":10,"forks_count":47,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-05T21:12:14.649Z","etag":null,"topics":["alpha","asyncio","logging","pypi","python","requests","worldquant-brain","wqb"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/wqb/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rocky-d.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"rocky-d"}},"created_at":"2025-01-27T05:32:14.000Z","updated_at":"2025-09-30T09:03:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"acce82fd-2ede-40dc-9f45-e51a434dcf26","html_url":"https://github.com/rocky-d/wqb","commit_stats":null,"previous_names":["rocky-d/wqb"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/rocky-d/wqb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky-d%2Fwqb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky-d%2Fwqb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky-d%2Fwqb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky-d%2Fwqb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rocky-d","download_url":"https://codeload.github.com/rocky-d/wqb/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky-d%2Fwqb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28414556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T08:31:27.429Z","status":"ssl_error","status_checked_at":"2026-01-14T08:31:19.098Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alpha","asyncio","logging","pypi","python","requests","worldquant-brain","wqb"],"created_at":"2026-01-14T08:36:05.317Z","updated_at":"2026-01-14T08:36:06.037Z","avatar_url":"https://github.com/rocky-d.png","language":"Python","funding_links":["https://github.com/sponsors/rocky-d"],"categories":[],"sub_categories":[],"readme":"# wqb\n\nA better machine lib.\n\n## HIGHLIGHTS\n\n- [WorldQuant BRAIN](https://platform.worldquantbrain.com/)\n- [PyPI (Python Package Index)](https://pypi.org/)\n- [Configurable Logging](#create-a-logginglogger-object-optional-but-recommended)\n  - File \u0026 Console (Terminal)\n  - INFO \u0026 WARNING\n- [Permanent Session](#create-a-wqbwqbsession-object)\n  - Extending [requests](https://requests.readthedocs.io/).Session\n  - Automatic Authentication (Anti-Expiration / Expiration-Proof)\n- [Various Requests](#usage)\n  - [Search Operators](#operators)\n  - [Locate / Search Datasets](#datasets)\n  - [Locate / Search Fields](#fields)\n  - [Locate / Filter Alphas](#alphas)\n  - [Patch Properties](#alphas)\n  - [(Asynchronous \u0026 Concurrent) Simulate](#simulate)\n  - [(Asynchronous \u0026 Concurrent) Check Submission](#check)\n  - [(Asynchronous \u0026 Concurrent) Submit](#submit)\n\n## PREREQUISITES\n\nPlease first make sure you have a proper [Python](https://www.python.org/) (\u003e=3.11) enviroment ([virtualenv](https://virtualenv.pypa.io/), [conda](https://anaconda.org/), etc.).\n\n- Python \u003e= 3.11\n- Connecting to the Internet\n\n### Create a new conda environment *(Optional)*\n\n```sh\nconda create -y -n wqb-py311 python=3.11\nconda activate wqb-py311\n```\n\n### Install\n\n```sh\npython -m pip install wqb\n```\n\n### Update\n\n```sh\npython -m pip install wqb --upgrade --extra-index-url https://pypi.org/simple\n```\n\n## USAGE\n\n**PLEASE ALWAYS REMEMBER:**\n\n- Manual authentication requests *(including the initial one)* are **never needed**. Just imagine using a permanent session that never expires.\n- All **positional arguments** are **required**, and vice versa.\n- All **keyword arguments** are **optional**, and vice versa.\n  - Always use the default values of these arguments when you don't know what they mean.\n- `Generator[requests.Response, None, None]` can be considered as `Iterable[requests.Response]`.\n- All requesting methods return `requests.Response` or `Iterable[requests.Response]`.\n- Common (optional) keyword arguments:\n  - `log: str | None`\n    - `None` disables log.\n    - `''` enables log.\n    - `'\u003ccontent\u003e'` will be appended to the log entry, and also enables log.\n  - `retry_log: str | None`\n    - A method has `retry_log` **if and only if** it is a coroutine function (defined by `async def`) except `wqb.WQBSession.retry` itself, and vice versa.\n    - `None` disables log.\n    - `''` enables log.\n    - `'\u003ccontent\u003e'` will be appended to the log entry, and also enables log.\n  - `log_gap: int`\n    - A method has `log_gap` **if and only if** it returns `Iterable[requests.Response]`, and vice versa.\n    - A sub-log will be logged `if 0 != log_gap and 0 == idx % log_gap` where `idx` starts from `1`.\n    - `0` disables sub-log.\n\n### Create a `logging.Logger` object *(Optional but Recommended)*\n\n```python\nimport wqb\n\n# Create `logger`\nlogger = wqb.wqb_logger()\nwqb.print(f\"{logger.name = }\")  # print(f\"{logger.name = }\", flush=True)\n\n# Manual logging\n# logger.info('This is an info for testing.')\n# logger.warning('This is a warning for testing.')\n```\n\n### Create a `wqb.WQBSession` object\n\n```python\nfrom wqb import WQBSession, print\n\n# Create `wqbs`\nwqbs = WQBSession(('\u003cemail\u003e', '\u003cpassword\u003e'), logger=logger)\n# If `logger` was not created, use the following line instead.\n# wqbs = WQBSession(('\u003cemail\u003e', '\u003cpassword\u003e'))\n\n# Test connectivity (Optional)\nresp = wqbs.auth_request()\nprint(resp.status_code)           # 201\nprint(resp.ok)                    # True\nprint(resp.json()['user']['id'])  # \u003cYour BRAIN User ID\u003e\n```\n\n### Operators\n\n#### `wqb.WQBSession.search_operators(...)`\n\n```python\nresp = wqbs.search_operators()\n# print(resp.json())\n\noperators = [item['name'] for item in resp.json()]\n# print(operators)\n\noperators_by_category = {}\nfor item in resp.json():\n    name = item['name']\n    category = item['category']\n    if category not in operators_by_category:\n        operators_by_category[category] = []\n    operators_by_category[category].append(name)\n# print(operators_by_category)\n```\n\n### Datasets\n\n#### `wqb.WQBSession.locate_dataset(...)`\n\n```python\ndataset_id = '\u003cdataset_id\u003e'  # 'pv1'\nresp = wqbs.locate_dataset(dataset_id)\n# print(resp.json())\n```\n\n#### `wqb.WQBSession.search_datasets_limited(...)`\n\n```python\nfrom wqb import FilterRange\n\nregion = '\u003cregion\u003e'  # 'USA'\ndelay = 1  # 1, 0\nuniverse = '\u003cuniverse\u003e'  # 'TOP3000'\nresp = wqbs.search_datasets_limited(\n    region,\n    delay,\n    universe,\n    # search='\u003csearch\u003e',  # 'price'\n    # category='\u003ccategory\u003e',  # 'pv', 'model', 'analyst'\n    # theme=False,  # True, False\n    # coverage=FilterRange.from_str('[0.8, inf)'),\n    # value_score=FilterRange.from_str('(-inf, 5]'),\n    # alpha_count=FilterRange.from_str('[100, 200)'),\n    # user_count=FilterRange.from_str('[1, 99]'),\n    # order='\u003corder\u003e',  # 'coverage', '-coverage', 'valueScore', '-valueScore'\n    # limit=50,\n    # offset=0,\n    # others=[],  # ['other_param_0=xxx', 'other_param_1=yyy']\n)\n# print(resp.json())\n```\n\n#### `wqb.WQBSession.search_datasets(...)`\n\n```python\nfrom wqb import FilterRange\n\nregion = '\u003cregion\u003e'  # 'USA'\ndelay = 1  # 1, 0\nuniverse = '\u003cuniverse\u003e'  # 'TOP3000'\nresps = wqbs.search_datasets(\n    region,\n    delay,\n    universe,\n    # search='\u003csearch\u003e',  # 'price'\n    # category='\u003ccategory\u003e',  # 'pv', 'model', 'analyst'\n    # theme=False,  # True, False\n    # coverage=FilterRange.from_str('[0.8, inf)'),\n    # value_score=FilterRange.from_str('(-inf, 5]'),\n    # alpha_count=FilterRange.from_str('[100, 200)'),\n    # user_count=FilterRange.from_str('[1, 99]'),\n    # order='\u003corder\u003e',  # 'coverage', '-coverage', 'valueScore', '-valueScore'\n    # limit=50,\n    # offset=0,\n    # others=[],  # ['other_param_0=xxx', 'other_param_1=yyy']\n)\nfor idx, resp in enumerate(resps, start=1):\n    print(idx)\n    # print(resp.json())\n```\n\n### Fields\n\n#### `wqb.WQBSession.locate_field(...)`\n\n```python\nfield_id = '\u003cfield_id\u003e'  # 'open'\nresp = wqbs.locate_field(field_id)\n# print(resp.json())\n```\n\n#### `wqb.WQBSession.search_fields_limited(...)`\n\n```python\nfrom wqb import FilterRange\n\nregion = '\u003cregion\u003e'  # 'USA'\ndelay = 1  # 1, 0\nuniverse = '\u003cuniverse\u003e'  # 'TOP3000'\nresp = wqbs.search_fields_limited(\n    region,\n    delay,\n    universe,\n    # dataset_id='\u003cdataset_id\u003e',  # 'pv1'\n    # search='\u003csearch\u003e',  # 'open'\n    # category='\u003ccategory\u003e',  # 'pv', 'model', 'analyst'\n    # theme=False,  # True, False\n    # coverage=FilterRange.from_str('[0.8, inf)'),\n    # type='\u003ctype\u003e',  # 'MATRIX', 'VECTOR', 'GROUP', 'UNIVERSE'\n    # alpha_count=FilterRange.from_str('[100, 200)'),\n    # user_count=FilterRange.from_str('[1, 99]'),\n    # order='\u003corder\u003e',  # 'coverage', '-coverage', 'alphaCount', '-alphaCount'\n    # limit=50,\n    # offset=0,\n    # others=[],  # ['other_param_0=xxx', 'other_param_1=yyy']\n)\n# print(resp.json())\n```\n\n#### `wqb.WQBSession.search_fields(...)`\n\n```python\nfrom wqb import FilterRange\n\nregion = '\u003cregion\u003e'  # 'USA'\ndelay = 1  # 1, 0\nuniverse = '\u003cuniverse\u003e'  # 'TOP3000'\nresps = wqbs.search_fields(\n    region,\n    delay,\n    universe,\n    # dataset_id='\u003cdataset_id\u003e',  # 'pv1'\n    # search='\u003csearch\u003e',  # 'open'\n    # category='\u003ccategory\u003e',  # 'pv', 'model', 'analyst'\n    # theme=False,  # True, False\n    # coverage=FilterRange.from_str('[0.8, inf)'),\n    # type='\u003ctype\u003e',  # 'MATRIX', 'VECTOR', 'GROUP', 'UNIVERSE'\n    # alpha_count=FilterRange.from_str('[100, 200)'),\n    # user_count=FilterRange.from_str('[1, 99]'),\n    # order='\u003corder\u003e',  # 'coverage', '-coverage', 'alphaCount', '-alphaCount'\n    # limit=50,\n    # offset=0,\n    # others=[],  # ['other_param_0=xxx', 'other_param_1=yyy']\n)\nfor idx, resp in enumerate(resps, start=1):\n    print(idx)\n    # print(resp.json())\n```\n\n### Alphas\n\n#### `wqb.WQBSession.locate_alpha(...)`\n\n```python\nalpha_id = '\u003calpha_id\u003e'\nresp = wqbs.locate_alpha(alpha_id)\n# print(resp.json())\n```\n\n#### `wqb.WQBSession.filter_alphas_limited(...)`\n\n```python\nfrom datetime import datetime\nfrom wqb import FilterRange\n\nlo = datetime.fromisoformat('2025-01-28T00:00:00-05:00')\nhi = datetime.fromisoformat('2025-01-29T00:00:00-05:00')\nresp = wqbs.filter_alphas_limited(\n    status='UNSUBMITTED',\n    region='USA',\n    delay=1,\n    universe='TOP3000',\n    sharpe=FilterRange.from_str('[1.58, inf)'),\n    fitness=FilterRange.from_str('[1, inf)'),\n    turnover=FilterRange.from_str('(-inf, 0.7]'),\n    date_created=FilterRange.from_str(f\"[{lo.isoformat()}, {hi.isoformat()})\"),\n    order='dateCreated',\n)\nalpha_ids = [item['id'] for item in resp.json()['results']]\n# print(alpha_ids)\n```\n\n#### `wqb.WQBSession.filter_alphas(...)`\n\n```python\nfrom datetime import datetime\nfrom wqb import FilterRange\n\nlo = datetime.fromisoformat('2025-01-28T00:00:00-05:00')\nhi = datetime.fromisoformat('2025-01-29T00:00:00-05:00')\nresps = wqbs.filter_alphas(\n    status='UNSUBMITTED',\n    region='USA',\n    delay=1,\n    universe='TOP3000',\n    sharpe=FilterRange.from_str('[1.58, inf)'),\n    fitness=FilterRange.from_str('[1, inf)'),\n    turnover=FilterRange.from_str('(-inf, 0.7]'),\n    date_created=FilterRange.from_str(f\"[{lo.isoformat()}, {hi.isoformat()})\"),\n    order='dateCreated',\n)\nalpha_ids = []\nfor resp in resps:\n    alpha_ids.extend(item['id'] for item in resp.json()['results'])\n# print(alpha_ids)\n```\n\n#### `wqb.WQBSession.patch_properties(...)`\n\n```python\nfrom wqb import NULL\n\n# `None` means not to set the property\n# `wqb.NULL` means to set the property as `null` (JSON)\n\nalpha_id = '\u003calpha_id\u003e'\nresp = wqbs.patch_properties(\n    alpha_id,\n    # favorite=False,  # False, True\n    # hidden=False,  # False, True\n    # name=NULL,  # '\u003cname\u003e'\n    # category=NULL,  # 'ANALYST', 'FUNDAMENTAL'\n    # tags=NULL,  # '\u003ctag\u003e', ['tag_0', 'tag_1', 'tag_2']\n    # color=NULL,  # 'RED', 'YELLOW', 'GREEN', 'BLUE', 'PURPLE'\n    # regular_description=NULL,  # '\u003cregular_description\u003e'\n)\n# print(resp.json())\n```\n\n### Simulate\n\n#### `wqb.WQBSession.simulate(...)`\n\n```python\nimport asyncio\n\nalpha = {\n    'type': 'REGULAR',\n    'settings': {\n        'instrumentType': 'EQUITY',\n        'region': 'USA',\n        'universe': 'TOP3000',\n        'delay': 1,\n        'decay': 13,\n        'neutralization': 'INDUSTRY',\n        'truncation': 0.13,\n        'pasteurization': 'ON',\n        'unitHandling': 'VERIFY',\n        'nanHandling': 'OFF',\n        'language': 'FASTEXPR',\n        'visualization': False\n    },\n    'regular': 'liabilities/assets',\n}\n# multi_alpha = [\u003calpha_0\u003e, \u003calpha_1\u003e, \u003calpha_2\u003e]\nresp = asyncio.run(\n    wqbs.simulate(\n        alpha,  # `alpha` or `multi_alpha`\n        # on_nolocation=lambda vars: print(vars['target'], vars['resp'], sep='\\n'),\n        # on_start=lambda vars: print(vars['url']),\n        # on_finish=lambda vars: print(vars['resp']),\n        # on_success=lambda vars: print(vars['resp']),\n        # on_failure=lambda vars: print(vars['resp']),\n    )\n)\n# print(resp.status_code)\n# print(resp.text)\n```\n\n#### `wqb.WQBSession.concurrent_simulate(...)`\n\n```python\nimport asyncio\nimport wqb\n\nalphas = [{...}, {...}, {...}]  # [\u003calpha_0\u003e, \u003calpha_1\u003e, \u003calpha_2\u003e]\nmulti_alphas = wqb.to_multi_alphas(alphas, 10)\nconcurrency = 8  # 1 \u003c= concurrency \u003c= 10\nresps = asyncio.run(\n    wqbs.concurrent_simulate(\n        multi_alphas,  # `alphas` or `multi_alphas`\n        concurrency,\n        # return_exceptions=True,\n        # on_nolocation=lambda vars: print(vars['target'], vars['resp'], sep='\\n'),\n        # on_start=lambda vars: print(vars['url']),\n        # on_finish=lambda vars: print(vars['resp']),\n        # on_success=lambda vars: print(vars['resp']),\n        # on_failure=lambda vars: print(vars['resp']),\n    )\n)\nfor idx, resp in enumerate(resps, start=1):\n    print(idx)\n    # print(resp.status_code)\n    # print(resp.text)\n```\n\n### Check\n\n#### `wqb.WQBSession.check(...)`\n\n```python\nimport asyncio\n\nalpha_id = '\u003calpha_id\u003e'\nresp = asyncio.run(\n    wqbs.check(\n        alpha_id,\n        # on_start=lambda vars: print(vars['url']),\n        # on_finish=lambda vars: print(vars['resp']),\n        # on_success=lambda vars: print(vars['resp']),\n        # on_failure=lambda vars: print(vars['resp']),\n    ),\n)\n# print(resp.status_code)\n# print(resp.text)\n```\n\n#### `wqb.WQBSession.concurrent_check(...)`\n\n```python\nimport asyncio\n\nalpha_ids = ['\u003calpha_id_0\u003e', '\u003calpha_id_1\u003e', '\u003calpha_id_2\u003e']\nconcurrency = 2\nresps = asyncio.run(\n    wqbs.concurrent_check(\n        alpha_ids,\n        concurrency,\n        # return_exceptions=True,\n        # on_start=lambda vars: print(vars['url']),\n        # on_finish=lambda vars: print(vars['resp']),\n        # on_success=lambda vars: print(vars['resp']),\n        # on_failure=lambda vars: print(vars['resp']),\n    ),\n)\nfor idx, resp in enumerate(resps, start=1):\n    print(idx)\n    # print(resp.status_code)\n    # print(resp.text)\n```\n\n### Submit\n\n#### `wqb.WQBSession.submit(...)`\n\nNot fully implemented yet.\nMay not work well.\n\n```python\nimport asyncio\n\nalpha_id = '\u003calpha_id\u003e'\nresp = asyncio.run(\n    wqbs.submit(\n        alpha_id,\n        # on_start=lambda vars: print(vars['url']),\n        # on_finish=lambda vars: print(vars['resp']),\n        # on_success=lambda vars: print(vars['resp']),\n        # on_failure=lambda vars: print(vars['resp']),\n    ),\n)\n# print(resp.status_code)\n# print(resp.text)\n```\n\n---\n\n![wqb logo](https://github.com/rocky-d/wqb/blob/master/img/wqb_1024x1024.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frocky-d%2Fwqb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frocky-d%2Fwqb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frocky-d%2Fwqb/lists"}