https://github.com/rocky-d/wqb
A better machine lib.
https://github.com/rocky-d/wqb
alpha asyncio logging pypi python requests worldquant-brain wqb
Last synced: 5 months ago
JSON representation
A better machine lib.
- Host: GitHub
- URL: https://github.com/rocky-d/wqb
- Owner: rocky-d
- License: mit
- Created: 2025-01-27T05:32:14.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2025-02-22T15:10:42.000Z (over 1 year ago)
- Last Synced: 2025-10-05T21:12:14.649Z (9 months ago)
- Topics: alpha, asyncio, logging, pypi, python, requests, worldquant-brain, wqb
- Language: Python
- Homepage: https://pypi.org/project/wqb/
- Size: 1.46 MB
- Stars: 208
- Watchers: 3
- Forks: 47
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# wqb
A better machine lib.
## HIGHLIGHTS
- [WorldQuant BRAIN](https://platform.worldquantbrain.com/)
- [PyPI (Python Package Index)](https://pypi.org/)
- [Configurable Logging](#create-a-logginglogger-object-optional-but-recommended)
- File & Console (Terminal)
- INFO & WARNING
- [Permanent Session](#create-a-wqbwqbsession-object)
- Extending [requests](https://requests.readthedocs.io/).Session
- Automatic Authentication (Anti-Expiration / Expiration-Proof)
- [Various Requests](#usage)
- [Search Operators](#operators)
- [Locate / Search Datasets](#datasets)
- [Locate / Search Fields](#fields)
- [Locate / Filter Alphas](#alphas)
- [Patch Properties](#alphas)
- [(Asynchronous & Concurrent) Simulate](#simulate)
- [(Asynchronous & Concurrent) Check Submission](#check)
- [(Asynchronous & Concurrent) Submit](#submit)
## PREREQUISITES
Please first make sure you have a proper [Python](https://www.python.org/) (>=3.11) enviroment ([virtualenv](https://virtualenv.pypa.io/), [conda](https://anaconda.org/), etc.).
- Python >= 3.11
- Connecting to the Internet
### Create a new conda environment *(Optional)*
```sh
conda create -y -n wqb-py311 python=3.11
conda activate wqb-py311
```
### Install
```sh
python -m pip install wqb
```
### Update
```sh
python -m pip install wqb --upgrade --extra-index-url https://pypi.org/simple
```
## USAGE
**PLEASE ALWAYS REMEMBER:**
- Manual authentication requests *(including the initial one)* are **never needed**. Just imagine using a permanent session that never expires.
- All **positional arguments** are **required**, and vice versa.
- All **keyword arguments** are **optional**, and vice versa.
- Always use the default values of these arguments when you don't know what they mean.
- `Generator[requests.Response, None, None]` can be considered as `Iterable[requests.Response]`.
- All requesting methods return `requests.Response` or `Iterable[requests.Response]`.
- Common (optional) keyword arguments:
- `log: str | None`
- `None` disables log.
- `''` enables log.
- `''` will be appended to the log entry, and also enables log.
- `retry_log: str | None`
- 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.
- `None` disables log.
- `''` enables log.
- `''` will be appended to the log entry, and also enables log.
- `log_gap: int`
- A method has `log_gap` **if and only if** it returns `Iterable[requests.Response]`, and vice versa.
- A sub-log will be logged `if 0 != log_gap and 0 == idx % log_gap` where `idx` starts from `1`.
- `0` disables sub-log.
### Create a `logging.Logger` object *(Optional but Recommended)*
```python
import wqb
# Create `logger`
logger = wqb.wqb_logger()
wqb.print(f"{logger.name = }") # print(f"{logger.name = }", flush=True)
# Manual logging
# logger.info('This is an info for testing.')
# logger.warning('This is a warning for testing.')
```
### Create a `wqb.WQBSession` object
```python
from wqb import WQBSession, print
# Create `wqbs`
wqbs = WQBSession(('', ''), logger=logger)
# If `logger` was not created, use the following line instead.
# wqbs = WQBSession(('', ''))
# Test connectivity (Optional)
resp = wqbs.auth_request()
print(resp.status_code) # 201
print(resp.ok) # True
print(resp.json()['user']['id']) #
```
### Operators
#### `wqb.WQBSession.search_operators(...)`
```python
resp = wqbs.search_operators()
# print(resp.json())
operators = [item['name'] for item in resp.json()]
# print(operators)
operators_by_category = {}
for item in resp.json():
name = item['name']
category = item['category']
if category not in operators_by_category:
operators_by_category[category] = []
operators_by_category[category].append(name)
# print(operators_by_category)
```
### Datasets
#### `wqb.WQBSession.locate_dataset(...)`
```python
dataset_id = '' # 'pv1'
resp = wqbs.locate_dataset(dataset_id)
# print(resp.json())
```
#### `wqb.WQBSession.search_datasets_limited(...)`
```python
from wqb import FilterRange
region = '' # 'USA'
delay = 1 # 1, 0
universe = '' # 'TOP3000'
resp = wqbs.search_datasets_limited(
region,
delay,
universe,
# search='', # 'price'
# category='', # 'pv', 'model', 'analyst'
# theme=False, # True, False
# coverage=FilterRange.from_str('[0.8, inf)'),
# value_score=FilterRange.from_str('(-inf, 5]'),
# alpha_count=FilterRange.from_str('[100, 200)'),
# user_count=FilterRange.from_str('[1, 99]'),
# order='', # 'coverage', '-coverage', 'valueScore', '-valueScore'
# limit=50,
# offset=0,
# others=[], # ['other_param_0=xxx', 'other_param_1=yyy']
)
# print(resp.json())
```
#### `wqb.WQBSession.search_datasets(...)`
```python
from wqb import FilterRange
region = '' # 'USA'
delay = 1 # 1, 0
universe = '' # 'TOP3000'
resps = wqbs.search_datasets(
region,
delay,
universe,
# search='', # 'price'
# category='', # 'pv', 'model', 'analyst'
# theme=False, # True, False
# coverage=FilterRange.from_str('[0.8, inf)'),
# value_score=FilterRange.from_str('(-inf, 5]'),
# alpha_count=FilterRange.from_str('[100, 200)'),
# user_count=FilterRange.from_str('[1, 99]'),
# order='', # 'coverage', '-coverage', 'valueScore', '-valueScore'
# limit=50,
# offset=0,
# others=[], # ['other_param_0=xxx', 'other_param_1=yyy']
)
for idx, resp in enumerate(resps, start=1):
print(idx)
# print(resp.json())
```
### Fields
#### `wqb.WQBSession.locate_field(...)`
```python
field_id = '' # 'open'
resp = wqbs.locate_field(field_id)
# print(resp.json())
```
#### `wqb.WQBSession.search_fields_limited(...)`
```python
from wqb import FilterRange
region = '' # 'USA'
delay = 1 # 1, 0
universe = '' # 'TOP3000'
resp = wqbs.search_fields_limited(
region,
delay,
universe,
# dataset_id='', # 'pv1'
# search='', # 'open'
# category='', # 'pv', 'model', 'analyst'
# theme=False, # True, False
# coverage=FilterRange.from_str('[0.8, inf)'),
# type='', # 'MATRIX', 'VECTOR', 'GROUP', 'UNIVERSE'
# alpha_count=FilterRange.from_str('[100, 200)'),
# user_count=FilterRange.from_str('[1, 99]'),
# order='', # 'coverage', '-coverage', 'alphaCount', '-alphaCount'
# limit=50,
# offset=0,
# others=[], # ['other_param_0=xxx', 'other_param_1=yyy']
)
# print(resp.json())
```
#### `wqb.WQBSession.search_fields(...)`
```python
from wqb import FilterRange
region = '' # 'USA'
delay = 1 # 1, 0
universe = '' # 'TOP3000'
resps = wqbs.search_fields(
region,
delay,
universe,
# dataset_id='', # 'pv1'
# search='', # 'open'
# category='', # 'pv', 'model', 'analyst'
# theme=False, # True, False
# coverage=FilterRange.from_str('[0.8, inf)'),
# type='', # 'MATRIX', 'VECTOR', 'GROUP', 'UNIVERSE'
# alpha_count=FilterRange.from_str('[100, 200)'),
# user_count=FilterRange.from_str('[1, 99]'),
# order='', # 'coverage', '-coverage', 'alphaCount', '-alphaCount'
# limit=50,
# offset=0,
# others=[], # ['other_param_0=xxx', 'other_param_1=yyy']
)
for idx, resp in enumerate(resps, start=1):
print(idx)
# print(resp.json())
```
### Alphas
#### `wqb.WQBSession.locate_alpha(...)`
```python
alpha_id = ''
resp = wqbs.locate_alpha(alpha_id)
# print(resp.json())
```
#### `wqb.WQBSession.filter_alphas_limited(...)`
```python
from datetime import datetime
from wqb import FilterRange
lo = datetime.fromisoformat('2025-01-28T00:00:00-05:00')
hi = datetime.fromisoformat('2025-01-29T00:00:00-05:00')
resp = wqbs.filter_alphas_limited(
status='UNSUBMITTED',
region='USA',
delay=1,
universe='TOP3000',
sharpe=FilterRange.from_str('[1.58, inf)'),
fitness=FilterRange.from_str('[1, inf)'),
turnover=FilterRange.from_str('(-inf, 0.7]'),
date_created=FilterRange.from_str(f"[{lo.isoformat()}, {hi.isoformat()})"),
order='dateCreated',
)
alpha_ids = [item['id'] for item in resp.json()['results']]
# print(alpha_ids)
```
#### `wqb.WQBSession.filter_alphas(...)`
```python
from datetime import datetime
from wqb import FilterRange
lo = datetime.fromisoformat('2025-01-28T00:00:00-05:00')
hi = datetime.fromisoformat('2025-01-29T00:00:00-05:00')
resps = wqbs.filter_alphas(
status='UNSUBMITTED',
region='USA',
delay=1,
universe='TOP3000',
sharpe=FilterRange.from_str('[1.58, inf)'),
fitness=FilterRange.from_str('[1, inf)'),
turnover=FilterRange.from_str('(-inf, 0.7]'),
date_created=FilterRange.from_str(f"[{lo.isoformat()}, {hi.isoformat()})"),
order='dateCreated',
)
alpha_ids = []
for resp in resps:
alpha_ids.extend(item['id'] for item in resp.json()['results'])
# print(alpha_ids)
```
#### `wqb.WQBSession.patch_properties(...)`
```python
from wqb import NULL
# `None` means not to set the property
# `wqb.NULL` means to set the property as `null` (JSON)
alpha_id = ''
resp = wqbs.patch_properties(
alpha_id,
# favorite=False, # False, True
# hidden=False, # False, True
# name=NULL, # ''
# category=NULL, # 'ANALYST', 'FUNDAMENTAL'
# tags=NULL, # '', ['tag_0', 'tag_1', 'tag_2']
# color=NULL, # 'RED', 'YELLOW', 'GREEN', 'BLUE', 'PURPLE'
# regular_description=NULL, # ''
)
# print(resp.json())
```
### Simulate
#### `wqb.WQBSession.simulate(...)`
```python
import asyncio
alpha = {
'type': 'REGULAR',
'settings': {
'instrumentType': 'EQUITY',
'region': 'USA',
'universe': 'TOP3000',
'delay': 1,
'decay': 13,
'neutralization': 'INDUSTRY',
'truncation': 0.13,
'pasteurization': 'ON',
'unitHandling': 'VERIFY',
'nanHandling': 'OFF',
'language': 'FASTEXPR',
'visualization': False
},
'regular': 'liabilities/assets',
}
# multi_alpha = [, , ]
resp = asyncio.run(
wqbs.simulate(
alpha, # `alpha` or `multi_alpha`
# on_nolocation=lambda vars: print(vars['target'], vars['resp'], sep='\n'),
# on_start=lambda vars: print(vars['url']),
# on_finish=lambda vars: print(vars['resp']),
# on_success=lambda vars: print(vars['resp']),
# on_failure=lambda vars: print(vars['resp']),
)
)
# print(resp.status_code)
# print(resp.text)
```
#### `wqb.WQBSession.concurrent_simulate(...)`
```python
import asyncio
import wqb
alphas = [{...}, {...}, {...}] # [, , ]
multi_alphas = wqb.to_multi_alphas(alphas, 10)
concurrency = 8 # 1 <= concurrency <= 10
resps = asyncio.run(
wqbs.concurrent_simulate(
multi_alphas, # `alphas` or `multi_alphas`
concurrency,
# return_exceptions=True,
# on_nolocation=lambda vars: print(vars['target'], vars['resp'], sep='\n'),
# on_start=lambda vars: print(vars['url']),
# on_finish=lambda vars: print(vars['resp']),
# on_success=lambda vars: print(vars['resp']),
# on_failure=lambda vars: print(vars['resp']),
)
)
for idx, resp in enumerate(resps, start=1):
print(idx)
# print(resp.status_code)
# print(resp.text)
```
### Check
#### `wqb.WQBSession.check(...)`
```python
import asyncio
alpha_id = ''
resp = asyncio.run(
wqbs.check(
alpha_id,
# on_start=lambda vars: print(vars['url']),
# on_finish=lambda vars: print(vars['resp']),
# on_success=lambda vars: print(vars['resp']),
# on_failure=lambda vars: print(vars['resp']),
),
)
# print(resp.status_code)
# print(resp.text)
```
#### `wqb.WQBSession.concurrent_check(...)`
```python
import asyncio
alpha_ids = ['', '', '']
concurrency = 2
resps = asyncio.run(
wqbs.concurrent_check(
alpha_ids,
concurrency,
# return_exceptions=True,
# on_start=lambda vars: print(vars['url']),
# on_finish=lambda vars: print(vars['resp']),
# on_success=lambda vars: print(vars['resp']),
# on_failure=lambda vars: print(vars['resp']),
),
)
for idx, resp in enumerate(resps, start=1):
print(idx)
# print(resp.status_code)
# print(resp.text)
```
### Submit
#### `wqb.WQBSession.submit(...)`
Not fully implemented yet.
May not work well.
```python
import asyncio
alpha_id = ''
resp = asyncio.run(
wqbs.submit(
alpha_id,
# on_start=lambda vars: print(vars['url']),
# on_finish=lambda vars: print(vars['resp']),
# on_success=lambda vars: print(vars['resp']),
# on_failure=lambda vars: print(vars['resp']),
),
)
# print(resp.status_code)
# print(resp.text)
```
---
