Full Code of Polymarket/py-clob-client for AI

main cc1740c11adf cached
110 files
365.5 KB
92.2k tokens
356 symbols
2 requests
Download .txt
Showing preview only (392K chars total). Download the full file or copy to clipboard to get everything.
Repository: Polymarket/py-clob-client
Branch: main
Commit: cc1740c11adf
Files: 110
Total size: 365.5 KB

Directory structure:
gitextract_cz0xsyu3/

├── .editorconfig
├── .github/
│   ├── CODEOWNERS
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── release.yaml
│       └── workflow.yaml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── examples/
│   ├── GTD_order.py
│   ├── are_orders_scoring.py
│   ├── cancel_all.py
│   ├── cancel_market_orders.py
│   ├── cancel_order.py
│   ├── cancel_orders.py
│   ├── create_api_key.py
│   ├── create_readonly_api_key.py
│   ├── delete_readonly_api_key.py
│   ├── derive_api_key.py
│   ├── drop_notifications.py
│   ├── get_api_keys.py
│   ├── get_balance_allowance.py
│   ├── get_builder_trades.py
│   ├── get_closed_only_mode.py
│   ├── get_last_trade_price.py
│   ├── get_last_trades_prices.py
│   ├── get_market_trades_events.py
│   ├── get_markets.py
│   ├── get_mid_market_price.py
│   ├── get_mid_markets_prices.py
│   ├── get_notifications.py
│   ├── get_ok.py
│   ├── get_open_orders_with_readonly_key.py
│   ├── get_order.py
│   ├── get_orderbook.py
│   ├── get_orderbooks.py
│   ├── get_orders.py
│   ├── get_price.py
│   ├── get_prices.py
│   ├── get_readonly_api_keys.py
│   ├── get_server_time.py
│   ├── get_spread.py
│   ├── get_spreads.py
│   ├── get_trades.py
│   ├── is_order_scoring.py
│   ├── market_buy_order.py
│   ├── market_sell_order.py
│   ├── order.py
│   ├── orders.py
│   ├── place_builder_order.py
│   ├── post_heartbeat.py
│   ├── post_only_order.py
│   ├── rfq_accept_quote.py
│   ├── rfq_approve_order.py
│   ├── rfq_cancel_quote.py
│   ├── rfq_cancel_request.py
│   ├── rfq_config.py
│   ├── rfq_create_quote.py
│   ├── rfq_create_request.py
│   ├── rfq_full_flow.py
│   ├── rfq_get_best_quote.py
│   ├── rfq_get_quotes.py
│   ├── rfq_get_requests.py
│   └── update_balance_allowance.py
├── py_clob_client/
│   ├── __init__.py
│   ├── client.py
│   ├── clob_types.py
│   ├── config.py
│   ├── constants.py
│   ├── endpoints.py
│   ├── exceptions.py
│   ├── headers/
│   │   ├── __init__.py
│   │   └── headers.py
│   ├── http_helpers/
│   │   ├── __init__.py
│   │   └── helpers.py
│   ├── order_builder/
│   │   ├── __init__.py
│   │   ├── builder.py
│   │   ├── constants.py
│   │   └── helpers.py
│   ├── rfq/
│   │   ├── __init__.py
│   │   ├── rfq_client.py
│   │   ├── rfq_helpers.py
│   │   └── rfq_types.py
│   ├── signer.py
│   ├── signing/
│   │   ├── __init__.py
│   │   ├── eip712.py
│   │   ├── hmac.py
│   │   └── model.py
│   └── utilities.py
├── requirements.txt
├── setup.py
└── tests/
    ├── __init__.py
    ├── headers/
    │   ├── __init__.py
    │   └── test_headers.py
    ├── http_helpers/
    │   ├── __init__.py
    │   └── test_helpers.py
    ├── order_builder/
    │   ├── __init__.py
    │   ├── test_builder.py
    │   └── test_helpers.py
    ├── rfq/
    │   ├── test_rfq_payload.py
    │   └── test_rfq_query_params.py
    ├── signing/
    │   ├── __init__.py
    │   ├── test_eip712.py
    │   └── test_hmac.py
    └── test_utilities.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true

[*.{py}]
indent_style = space
indent_size = 4


[*.json]
indent_style = space
indent_size = 2

[*.yaml]
indent_style = space
indent_size = 2
quote_type = single


================================================
FILE: .github/CODEOWNERS
================================================
# All PRs on any file must be reviewed by one of the following team members
# Wildcard (*) for all files
* @Polymarket/eng-platform


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socioeconomic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

- The use of sexualized language or imagery and unwelcome sexual attention or
  advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
  address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [`liam@polymarket.com`](mailto:liam@polymarket.com). All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq


================================================
FILE: .github/CONTRIBUTING.md
================================================
## python-order-utils Monorepo Contribution Guide

All contributions to the python-order-utils are welcome and greatly appreciated! This document serves to outline the process for contributions and help you get set up.

### Steps to get started

1. Fork 'Polymarket/python-order-utils'
2. Clone your fork
3. Follow the [installation instructions](./README.md) in the monorepo's top level README.
4. Open pull requests with the `[WIP]` flag against the `main` branch and include a description of the intended change in the PR description.

Before removing the `[WIP]` tag and submitting a PR for review, make sure that:

- it passes our linter checks
- the test suite passes for all packages
- it passes our continuous integration tests
- your fork is up to date with `main`

### Branch structure & naming

Our main branch, `main`, represents the current development state of the codebase. All pull requests should be opened against `main`.

Name your branch with the format `{fix | feat | refactor | chore }/{ description }`

- A `fix` addresses a bug or other issue
- A `feat` adds new functionality/interface surface area
- A `refactor` changes no business logic or interfaces, but improves implementation
- A `chore` addresses minor improvements or configuration changes



================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!-- Please use tags to indicate weather this issue is a bug, new feature, or discussion. -->
<!-- Omit and add sections as necessary. -->

## Overview

<!-- One or two sentences. Essentially a expanded title. -->

## Description

<!-- Most information should be here. -->
<!-- Include as much detail as necessary, and add sub-sections if needed. -->

## Notes

<!-- Additional info, links, considerations, etc. -->

## Action items

- [ ] Tag issue (bug, enhancement, feature, etc.)
- [ ] Initial discussion/assign team member
- [ ] Move discussion to PR if necessary and when ready


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!-- Delete any sub-sections not used rather than leaving them empty. -->

## Overview

<!-- Provide a brief (1-3 sentence) summary of the PR and it's purpose. May include plans if a [WIP]. -->

## Description

<!-- Describe in detail what changes you plan to make in this section and sub-sections. -->

## Testing instructions

<!-- If the PR changes how tests should be run, describe here. -->

## Types of changes

<!-- Check one of the boxes below, and add additional information as necessary. -->

- [ ] Refactor/enhancement <!-- Non-breaking (patch bump). -->
- [ ] Bug fix/behavior correction <!-- Non-breaking (patch bump). -->
- [ ] New feature <!-- Non-breaking (minor bump), unless also specified as breaking. -->
- [ ] Breaking change <!-- Feature or bug fix that changes behavior and requires a major version bump. -->
- [ ] Other, additional <!-- Describe below/above. -->

## Notes

<!-- Include any additional comments, links, questions, or discussion items here. -->

## Status

<!-- Check any boxes that are already complete upon creation of the PR, and update whenever necessary. -->
<!-- Make sure to check the "Ready for review" box when you are signing off on your changes for merge! -->

- [ ] Prefix PR title with `[WIP]` if necessary (changes not yet made).
- [ ] Add tests to cover changes as needed.
- [ ] Update documentation/changelog as needed.
- [ ] Verify all tests run correctly in CI and pass.
- [ ] Ready for review/merge.


================================================
FILE: .github/workflows/release.yaml
================================================
name: Release

on:
  release:
    types: [published]

permissions:
  id-token: write

jobs:
  release:
    name: release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v6
        with:
          persist-credentials: false

      - uses: actions/setup-python@v5
        with:
          python-version: 3.9

      - run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -e .

      - name: Run Tests
        run: make test

      # If your build system reads version dynamically, set it here.
      # Common pattern: setuptools-scm or hatch-vcs will pick up the git tag automatically.
      - name: Install build tooling
        run: |
          pip install build

      - name: Build sdist and wheel
        env:
          # Many tools will respect this if you wire it up (optional)
          PACKAGE_VERSION: ${{ github.ref_name }}
        run: python -m build

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1


================================================
FILE: .github/workflows/workflow.yaml
================================================
name: Test

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  pull-requests: read

jobs:
  build-lint-test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v6
        with:
          persist-credentials: false

      - uses: actions/setup-python@v5
        with:
          python-version: 3.9

      - run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -e .

      - name: Run Tests
        run: make test


================================================
FILE: .gitignore
================================================
.idea/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/


================================================
FILE: CONTRIBUTING.md
================================================
### Contributing

Thanks for helping improve `py-clob-client`.

- Fork the repo and create a feature branch.
- Create focused edits with tests where applicable.
- Run the examples to sanity‑check trading flows against a sandbox.
- Open a PR with a clear description of the change and motivation.

For security issues, please see `SECURITY.md`.




================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 Polymarket

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
init:
	pip install -r requirements.txt

test:
	pytest -s

fmt:
	black ./.

================================================
FILE: README.md
================================================
# Polymarket Python CLOB Client

<a href='https://pypi.org/project/py-clob-client'>
    <img src='https://img.shields.io/pypi/v/py-clob-client.svg' alt='PyPI'/>
</a>

Python client for the Polymarket Central Limit Order Book (CLOB).

## Documentation

## Installation

```bash
# install from PyPI (Python 3.9>)
pip install py-clob-client
```
## Usage

The examples below are short and copy‑pasteable.

- What you need:
  - **Python 3.9+**
  - **Private key** that owns funds on Polymarket
  - Optional: a **proxy/funder address** if you use an email or smart‑contract wallet
  - Tip: store secrets in environment variables (e.g., with `.env`)

### Quickstart (read‑only)

```python
from py_clob_client.client import ClobClient

client = ClobClient("https://clob.polymarket.com")  # Level 0 (no auth)

ok = client.get_ok()
time = client.get_server_time()
print(ok, time)
```

### Start trading (EOA)

**Note**: If using MetaMask or hardware wallet, you must first set token allowances. See [Token Allowances section](#important-token-allowances-for-metamaskeoa-users) below.

```python
from py_clob_client.client import ClobClient

HOST = "https://clob.polymarket.com"
CHAIN_ID = 137
PRIVATE_KEY = "<your-private-key>"
FUNDER = "<your-funder-address>"

client = ClobClient(
    HOST,  # The CLOB API endpoint
    key=PRIVATE_KEY,  # Your wallet's private key
    chain_id=CHAIN_ID,  # Polygon chain ID (137)
    signature_type=1,  # 1 for email/Magic wallet signatures
    funder=FUNDER  # Address that holds your funds
)
client.set_api_creds(client.create_or_derive_api_creds())
```

### Start trading (proxy wallet)

For email/Magic or browser wallet proxies, you need to specify two additional parameters:

#### Funder Address
The **funder address** is the actual address that holds your funds on Polymarket. When using proxy wallets (email wallets like Magic or browser extension wallets), the signing key differs from the address holding the funds. The funder address ensures orders are properly attributed to your funded account.

#### Signature Types
The **signature_type** parameter tells the system how to verify your signatures:
- `signature_type=0` (default): Standard EOA (Externally Owned Account) signatures - includes MetaMask, hardware wallets, and any wallet where you control the private key directly
- `signature_type=1`: Email/Magic wallet signatures (delegated signing)
- `signature_type=2`: Browser wallet proxy signatures (when using a proxy contract, not direct wallet connections)

```python
from py_clob_client.client import ClobClient

HOST = "https://clob.polymarket.com"
CHAIN_ID = 137
PRIVATE_KEY = "<your-private-key>"
PROXY_FUNDER = "<your-proxy-or-smart-wallet-address>"  # Address that holds your funds

client = ClobClient(
    HOST,  # The CLOB API endpoint
    key=PRIVATE_KEY,  # Your wallet's private key
    chain_id=CHAIN_ID,  # Polygon chain ID (137)
    signature_type=1,  # 1 for email/Magic wallet signatures
    funder=PROXY_FUNDER  # Address that holds your funds
)
client.set_api_creds(client.create_or_derive_api_creds())
```

### Find markets, prices, and orderbooks

```python
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BookParams

client = ClobClient("https://clob.polymarket.com")  # read-only

token_id = "<token-id>"  # Get a token ID: https://docs.polymarket.com/developers/gamma-markets-api/get-markets

mid = client.get_midpoint(token_id)
price = client.get_price(token_id, side="BUY")
book = client.get_order_book(token_id)
books = client.get_order_books([BookParams(token_id=token_id)])
print(mid, price, book.market, len(books))
```

### Place a market order (buy by $ amount)

**Note**: EOA/MetaMask users must set token allowances before trading. See [Token Allowances section](#important-token-allowances-for-metamaskeoa-users) below.

```python
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import MarketOrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY

HOST = "https://clob.polymarket.com"
CHAIN_ID = 137
PRIVATE_KEY = "<your-private-key>"
FUNDER = "<your-funder-address>"

client = ClobClient(
    HOST,  # The CLOB API endpoint
    key=PRIVATE_KEY,  # Your wallet's private key
    chain_id=CHAIN_ID,  # Polygon chain ID (137)
    signature_type=1,  # 1 for email/Magic wallet signatures
    funder=FUNDER  # Address that holds your funds
)
client.set_api_creds(client.create_or_derive_api_creds())

mo = MarketOrderArgs(token_id="<token-id>", amount=25.0, side=BUY, order_type=OrderType.FOK)  # Get a token ID: https://docs.polymarket.com/developers/gamma-markets-api/get-markets
signed = client.create_market_order(mo)
resp = client.post_order(signed, OrderType.FOK)
print(resp)
```

### Place a limit order (shares at a price)

**Note**: EOA/MetaMask users must set token allowances before trading. See [Token Allowances section](#important-token-allowances-for-metamaskeoa-users) below.

```python
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs, OrderType
from py_clob_client.order_builder.constants import BUY

HOST = "https://clob.polymarket.com"
CHAIN_ID = 137
PRIVATE_KEY = "<your-private-key>"
FUNDER = "<your-funder-address>"

client = ClobClient(
    HOST,  # The CLOB API endpoint
    key=PRIVATE_KEY,  # Your wallet's private key
    chain_id=CHAIN_ID,  # Polygon chain ID (137)
    signature_type=1,  # 1 for email/Magic wallet signatures
    funder=FUNDER  # Address that holds your funds
)
client.set_api_creds(client.create_or_derive_api_creds())

order = OrderArgs(token_id="<token-id>", price=0.01, size=5.0, side=BUY)  # Get a token ID: https://docs.polymarket.com/developers/gamma-markets-api/get-markets
signed = client.create_order(order)
resp = client.post_order(signed, OrderType.GTC)
print(resp)
```

### Manage orders

**Note**: EOA/MetaMask users must set token allowances before trading. See [Token Allowances section](#important-token-allowances-for-metamaskeoa-users) below.

```python
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OpenOrderParams

HOST = "https://clob.polymarket.com"
CHAIN_ID = 137
PRIVATE_KEY = "<your-private-key>"
FUNDER = "<your-funder-address>"

client = ClobClient(
    HOST,  # The CLOB API endpoint
    key=PRIVATE_KEY,  # Your wallet's private key
    chain_id=CHAIN_ID,  # Polygon chain ID (137)
    signature_type=1,  # 1 for email/Magic wallet signatures
    funder=FUNDER  # Address that holds your funds
)
client.set_api_creds(client.create_or_derive_api_creds())

open_orders = client.get_orders(OpenOrderParams())

order_id = open_orders[0]["id"] if open_orders else None
if order_id:
    client.cancel(order_id)

client.cancel_all()
```

### Markets (read‑only)

```python
from py_clob_client.client import ClobClient

client = ClobClient("https://clob.polymarket.com")
markets = client.get_simplified_markets()
print(markets["data"][:1])
```

### User trades (requires auth)

**Note**: EOA/MetaMask users must set token allowances before trading. See [Token Allowances section](#important-token-allowances-for-metamaskeoa-users) below.

```python
from py_clob_client.client import ClobClient

HOST = "https://clob.polymarket.com"
CHAIN_ID = 137
PRIVATE_KEY = "<your-private-key>"
FUNDER = "<your-funder-address>"

client = ClobClient(
    HOST,  # The CLOB API endpoint
    key=PRIVATE_KEY,  # Your wallet's private key
    chain_id=CHAIN_ID,  # Polygon chain ID (137)
    signature_type=1,  # 1 for email/Magic wallet signatures
    funder=FUNDER  # Address that holds your funds
)
client.set_api_creds(client.create_or_derive_api_creds())

last = client.get_last_trade_price("<token-id>")
trades = client.get_trades()
print(last, len(trades))
```

## Important: Token Allowances for MetaMask/EOA Users

### Do I need to set allowances?
- **Using email/Magic wallet?** No action needed - allowances are set automatically.
- **Using MetaMask or hardware wallet?** You need to set allowances before trading.

### What are allowances?
Think of allowances as permissions. Before Polymarket can move your funds to execute trades, you need to give the exchange contracts permission to access your USDC and conditional tokens.

### Quick Setup
You need to approve two types of tokens:
1. **USDC** (for deposits and trading)
2. **Conditional Tokens** (the outcome tokens you trade)

Each needs approval for the exchange contracts to work properly.

### Setting Allowances
Here's a simple breakdown of what needs to be approved:

**For USDC (your trading currency):**
- Token: `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174`
- Approve for these contracts:
  - `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` (Main exchange)
  - `0xC5d563A36AE78145C45a50134d48A1215220f80a` (Neg risk markets)
  - `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` (Neg risk adapter)

**For Conditional Tokens (your outcome tokens):**
- Token: `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045`
- Approve for the same three contracts above

### Example Code
See [this Python example](https://gist.github.com/poly-rodr/44313920481de58d5a3f6d1f8226bd5e) for setting allowances programmatically.

**Pro tip**: You only need to set these once per wallet. After that, you can trade freely.

## Notes
- To discover token IDs, use the Markets API Explorer: [Get Markets](https://docs.polymarket.com/developers/gamma-markets-api/get-markets).
- Prices are in dollars from 0.00 to 1.00. Shares are whole or fractional units of the outcome token.

See [/example](/examples) for more.

================================================
FILE: SECURITY.md
================================================
### Security

If you believe you’ve found a security vulnerability, please email security@polymarket.com. Do not open a public issue.

Include:
- A description of the issue and potential impact
- Steps to reproduce or a minimal proof of concept
- Any relevant logs or environment details

We will acknowledge receipt, investigate, and provide guidance on next steps.




================================================
FILE: examples/GTD_order.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, OrderArgs, OrderType
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

from py_clob_client.order_builder.constants import BUY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # Create and sign a limit order buying 100 YES tokens for 0.50c each
    order_args = OrderArgs(
        price=0.50,
        size=100.0,
        side=BUY,
        token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
        expiration="1000000000000",
    )
    signed_order = client.create_order(order_args)
    resp = client.post_order(signed_order, OrderType.GTD)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/are_orders_scoring.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, OrdersScoringParams
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    scoring = client.are_orders_scoring(
        OrdersScoringParams(
            orderIds=[
                "0xb816482a5187a3d3db49cbaf6fe3ddf24f53e6c712b5a4bf5e01d0ec7b11dabc"
            ]
        )
    )
    print(scoring)
    print("Done!")


main()


================================================
FILE: examples/cancel_all.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.cancel_all()
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/cancel_market_orders.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.cancel_market_orders(market="0xaaa", asset_id="100")
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/cancel_order.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.cancel(order_id="0xaaaa")
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/cancel_orders.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.cancel_orders(["0xaaaa"])
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/create_api_key.py
================================================
from dotenv import load_dotenv
import os

from py_clob_client.client import ClobClient
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id)

    print(client.create_api_key())


main()


================================================
FILE: examples/create_readonly_api_key.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.create_readonly_api_key()
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/delete_readonly_api_key.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # Replace with the readonly API key you want to delete
    readonly_api_key = "019aee85-4ea1-79cd-a287-8508f21209a2"

    resp = client.delete_readonly_api_key(readonly_api_key)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/derive_api_key.py
================================================
from dotenv import load_dotenv
import os

from py_clob_client.client import ClobClient
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id)

    print(client.derive_api_key())


main()


================================================
FILE: examples/drop_notifications.py
================================================
import os

from py_clob_client.client import ClobClient

from py_clob_client.clob_types import ApiCreds, DropNotificationParams
from dotenv import load_dotenv

from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    print(client.drop_notifications(params=DropNotificationParams(ids=["1", "2"])))


main()


================================================
FILE: examples/get_api_keys.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    print(client.get_api_keys())


main()


================================================
FILE: examples/get_balance_allowance.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, BalanceAllowanceParams, AssetType
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    collateral = client.get_balance_allowance(
        params=BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)
    )
    print(collateral)

    yes = client.get_balance_allowance(
        params=BalanceAllowanceParams(
            asset_type=AssetType.CONDITIONAL,
            token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426",
        )
    )
    print(yes)

    no = client.get_balance_allowance(
        params=BalanceAllowanceParams(
            asset_type=AssetType.CONDITIONAL,
            token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
        )
    )
    print(no)


main()


================================================
FILE: examples/get_builder_trades.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, TradeParams
from py_builder_signing_sdk.config import BuilderConfig, BuilderApiKeyCreds

from dotenv import load_dotenv
from pprint import pprint

from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    builder_config = BuilderConfig(
        local_builder_creds=BuilderApiKeyCreds(
            key=os.getenv("BUILDER_API_KEY"),
            secret=os.getenv("BUILDER_SECRET"),
            passphrase=os.getenv("BUILDER_PASS_PHRASE"),
        )
    )

    client = ClobClient(
        host, key=key, chain_id=chain_id, creds=creds, builder_config=builder_config
    )

    resp = client.get_builder_trades()
    pprint(resp)
    print("Done!")


main()


================================================
FILE: examples/get_closed_only_mode.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    print(client.get_closed_only_mode())


main()


================================================
FILE: examples/get_last_trade_price.py
================================================
import os

from py_clob_client.client import ClobClient
from dotenv import load_dotenv
from pprint import pprint

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id)

    resp = client.get_last_trade_price(
        "71321045679252212594626385532706912750332728571942532289631379312455583992563"
    )
    pprint(resp)
    print("Done!")


main()


================================================
FILE: examples/get_last_trades_prices.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BookParams
from dotenv import load_dotenv
from pprint import pprint

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id)

    resp = client.get_last_trades_prices(
        params=[
            BookParams(
                token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563"
            ),
            BookParams(
                token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426"
            ),
        ]
    )
    pprint(resp)
    print("Done!")


main()


================================================
FILE: examples/get_market_trades_events.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    print(client.get_market_trades_events("condition_id"))

    print("Done!")


main()


================================================
FILE: examples/get_markets.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    print(client.get_markets())
    print(client.get_simplified_markets())
    print(client.get_sampling_markets())
    print(client.get_sampling_simplified_markets())
    print(client.get_market("condition_id"))

    print("Done!")


main()


================================================
FILE: examples/get_mid_market_price.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv
from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.get_midpoint(
        "71321045679252212594626385532706912750332728571942532289631379312455583992563"
    )
    # {'mid': '0.55'}
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_mid_markets_prices.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, BookParams
from dotenv import load_dotenv
from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.get_midpoints(
        params=[
            BookParams(
                token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563"
            ),
            BookParams(
                token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426"
            ),
        ]
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_notifications.py
================================================
import os

from py_clob_client.client import ClobClient

from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    print(client.get_notifications())


main()


================================================
FILE: examples/get_ok.py
================================================
from py_clob_client.client import ClobClient


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    client = ClobClient(host)

    print(client.get_ok())

    pass


main()


================================================
FILE: examples/get_open_orders_with_readonly_key.py
================================================
import os

import httpx
from dotenv import load_dotenv

from py_clob_client.endpoints import ORDERS


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")

    # Replace with your address and readonly API key
    address = os.getenv("POLY_ADDRESS", "0xc68576124eC1fF645F81a560E14003C8deF2e8fb")
    readonly_api_key = os.getenv("CLOB_READONLY_API_KEY", "019aee85-4ea1-79cd-a287-8508f21209a2")

    # Get all open orders for the address
    response = httpx.get(
        f"{host}{ORDERS}",
        headers={
            "POLY_READONLY_API_KEY": readonly_api_key,
            "POLY_ADDRESS": address,
            "Content-Type": "application/json",
        },
        params={
            "maker_address": address,
        },
        follow_redirects=True,
    )
    if response.status_code == 200:
        print(response.json())
    else:
        print(f"Error {response.status_code}: {response.text}")
    print("Done!")


main()


================================================
FILE: examples/get_order.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.get_order(
        "0xf5667d8509bdc78ac43676fe2c980da1365c471ee92153820c89c488fc15d539"
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_orderbook.py
================================================
from py_clob_client.client import ClobClient
import os
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    client = ClobClient(host)

    orderbook = client.get_order_book(
        "34097058504275310827233323421517291090691602969494795225921954353603704046623"
    )
    print("orderbook", orderbook)

    hash = client.get_order_book_hash(orderbook)
    print("orderbook hash", hash)


main()


================================================
FILE: examples/get_orderbooks.py
================================================
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BookParams


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    client = ClobClient(host)

    resp = client.get_order_books(
        params=[
            BookParams(
                token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563"
            ),
            BookParams(
                token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426"
            ),
        ]
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_orders.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, OpenOrderParams
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.get_orders(
        OpenOrderParams(
            market="0x37a6a2dd9f3469495d9ec2467b0a764c5905371a294ce544bc3b2c944eb3e84a",
        )
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_price.py
================================================
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BookParams


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    client = ClobClient(host)

    resp = client.get_price(
        "71321045679252212594626385532706912750332728571942532289631379312455583992563",
        "BUY",
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_prices.py
================================================
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BookParams


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    client = ClobClient(host)

    resp = client.get_prices(
        params=[
            BookParams(
                token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
                side="BUY",
            ),
            BookParams(
                token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
                side="SELL",
            ),
            BookParams(
                token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426",
                side="BUY",
            ),
            BookParams(
                token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426",
                side="SELL",
            ),
        ]
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_readonly_api_keys.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.get_readonly_api_keys()
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_server_time.py
================================================
from py_clob_client.client import ClobClient


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    client = ClobClient(host)

    print(client.get_server_time())


main()


================================================
FILE: examples/get_spread.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv
from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.get_spread(
        "71321045679252212594626385532706912750332728571942532289631379312455583992563"
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_spreads.py
================================================
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BookParams


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    client = ClobClient(host)

    resp = client.get_spreads(
        params=[
            BookParams(
                token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563"
            ),
            BookParams(
                token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426"
            ),
        ]
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/get_trades.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, TradeParams
from dotenv import load_dotenv
from pprint import pprint

from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.get_trades(
        TradeParams(
            maker_address=client.get_address(),
            market="0x5f65177b394277fd294cd75650044e32ba009a95022d88a0c1d565897d72f8f1",
        )
    )
    pprint(resp)
    print("Done!")


main()


================================================
FILE: examples/is_order_scoring.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, OrderScoringParams
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    scoring = client.is_order_scoring(
        OrderScoringParams(
            orderId="0xb816482a5187a3d3db49cbaf6fe3ddf24f53e6c712b5a4bf5e01d0ec7b11dabc"
        )
    )
    print(scoring)
    print("Done!")


main()


================================================
FILE: examples/market_buy_order.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, MarketOrderArgs, OrderType
from dotenv import load_dotenv
from py_clob_client.constants import AMOY
from py_clob_client.order_builder.constants import BUY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # create a market buy order for the equivalent of 100 USDC at the market price
    order_args = MarketOrderArgs(
        token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
        amount=100,  # $$$
        side=BUY,
    )
    signed_order = client.create_market_order(order_args)
    resp = client.post_order(signed_order, orderType=OrderType.FOK)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/market_sell_order.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, MarketOrderArgs, OrderType
from dotenv import load_dotenv
from py_clob_client.constants import AMOY
from py_clob_client.order_builder.constants import SELL


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # create a market buy order for the equivalent of 100 USDC at the market price
    order_args = MarketOrderArgs(
        token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
        amount=100,  # SHARES
        side=SELL,
    )
    signed_order = client.create_market_order(order_args)
    resp = client.post_order(signed_order, orderType=OrderType.FOK)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/order.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, OrderArgs
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

from py_clob_client.order_builder.constants import BUY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # Create and sign a limit order buying 100 YES tokens for 0.0005 each
    order_args = OrderArgs(
        price=0.0005,
        size=20,
        side=BUY,
        token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
    )
    signed_order = client.create_order(order_args)
    resp = client.post_order(signed_order)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/orders.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, OrderArgs, PostOrdersArgs, OrderType
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

from py_clob_client.order_builder.constants import BUY, SELL


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.post_orders(
        [
            PostOrdersArgs(
                # Create and sign a limit order buying 100 YES tokens for 0.50 each
                order=client.create_order(
                    OrderArgs(
                        price=0.5,
                        size=100,
                        side=BUY,
                        token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
                    )
                ),
                orderType=OrderType.GTC,  # Good 'Til Cancelled
                postOnly=False,
            ),
            PostOrdersArgs(
                # Create and sign a limit order selling 200 NO tokens for 0.25 each
                order=client.create_order(
                    OrderArgs(
                        price=0.25,
                        size=200,
                        side=SELL,
                        token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426",
                    )
                ),
                orderType=OrderType.GTC,  # Good 'Til Cancelled
                postOnly=False, # Defaults to false, can be set to true to avoid matching on post
            ),
        ]
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/place_builder_order.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, OrderArgs
from dotenv import load_dotenv
from py_clob_client.constants import POLYGON

from py_clob_client.order_builder.constants import BUY
from py_builder_signing_sdk.config import BuilderConfig, BuilderApiKeyCreds

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = POLYGON
    builder_config = BuilderConfig(
        local_builder_creds=BuilderApiKeyCreds(
            key=os.getenv("BUILDER_API_KEY"),
            secret=os.getenv("BUILDER_SECRET"),
            passphrase=os.getenv("BUILDER_PASS_PHRASE"),
        )
    )
    client = ClobClient(
        host, key=key, chain_id=chain_id, creds=creds, builder_config=builder_config
    )

    order_args = OrderArgs(
        price=0.06,
        size=20,
        side=BUY,
        token_id="104173557214744537570424345347209544585775842950109756851652855913015295701992",
    )
    signed_order = client.create_order(order_args)
    resp = client.post_order(signed_order)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/post_heartbeat.py
================================================
import os
import time

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from dotenv import load_dotenv

from py_clob_client.constants import AMOY


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    heartbeat_id = None
    while True:
        resp = client.post_heartbeat(heartbeat_id)
        print(resp)
        heartbeat_id = resp["heartbeat_id"]
        # Wait 5 seconds before sending the next heartbeat
        time.sleep(5)
    # Example output on invalid heartbeat ID once heartbeats started:
    # PolyApiException[status_code=400, error_message={"error":"Invalid Heartbeat ID","heartbeat_id":"7f335bb3-36cb-433d-b8ff-4f9a2233d833"}




main()


================================================
FILE: examples/post_only_order.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, OrderArgs
from dotenv import load_dotenv
from py_clob_client.constants import AMOY
from py_clob_client.clob_types import (OrderType)
from py_clob_client.order_builder.constants import BUY, SELL


load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)
    order_args = OrderArgs(
        price=0.4,
        size=5,
        side=BUY,
        token_id="102200530570339469387764365697342150521708074903735836831685780223982723092914",
    )
    order = client.create_order(order_args)
    resp = client.post_order(order, orderType=OrderType.GTC, post_only=True)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/rfq_accept_quote.py
================================================
import os
import time

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import AcceptQuoteParams
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # Accept a quote (requester side)
    # Refers to the order expiry, not request expiry. For request expiry, check the server RFQ config.
    expiration = int(time.time()) + 3600

    accept_params = AcceptQuoteParams(
        request_id="019b04a4-2f4b-73b3-8fa2-2760b2754601",
        quote_id="019b04a7-5205-7eeb-9a48-227e8d53bd17",
        expiration=expiration,
    )

    resp = client.rfq.accept_rfq_quote(accept_params)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/rfq_approve_order.py
================================================
import os
import time

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import ApproveOrderParams
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # Approve an order (quoter side)
    # Refers to the order expiry, not quote expiry. For quote expiry, check the server RFQ config.
    expiration = int(time.time()) + 3600

    approve_params = ApproveOrderParams(
        request_id="019b04a4-2f4b-73b3-8fa2-2760b2754601",
        quote_id="019b04a7-5205-7eeb-9a48-227e8d53bd17",
        expiration=expiration,
    )

    resp = client.rfq.approve_rfq_order(approve_params)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/rfq_cancel_quote.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import CancelRfqQuoteParams
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.rfq.cancel_rfq_quote(
        CancelRfqQuoteParams(quote_id="0197656d-56ee-74a4-a06a-3b179121f3bf")
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/rfq_cancel_request.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import CancelRfqRequestParams
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.rfq.cancel_rfq_request(
        CancelRfqRequestParams(request_id="0197656d-56ee-74a4-a06a-3b179121f3bf")
    )
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/rfq_config.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    resp = client.rfq.rfq_config()
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/rfq_create_quote.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import RfqUserQuote
from py_clob_client.order_builder.constants import SELL
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # Create a quote for an RFQ request to SELL 40 tokens at $0.50 each
    user_quote = RfqUserQuote(
        request_id="019b04a4-2f4b-73b3-8fa2-2760b2754601",
        token_id="34097058504275310827233323421517291090691602969494795225921954353603704046623",
        price=0.50,
        side=SELL,
        size=40.0,
    )

    resp = client.rfq.create_rfq_quote(user_quote)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/rfq_create_request.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, PartialCreateOrderOptions
from py_clob_client.rfq import RfqUserRequest
from py_clob_client.order_builder.constants import BUY
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # Create an RFQ request to BUY 100 tokens at $0.50 each
    user_request = RfqUserRequest(
        token_id="34097058504275310827233323421517291090691602969494795225921954353603704046623",
        price=0.50,
        side=BUY,
        size=40.0,
    )

    resp = client.rfq.create_rfq_request(user_request, options=PartialCreateOrderOptions(tick_size=0.01))
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/rfq_full_flow.py
================================================
"""
RFQ Full Flow Example

This script demonstrates the complete RFQ (Request for Quote) flow between two parties.
For a single manual test, edit the REQUEST_PARAMS and QUOTE_PARAMS at the top.
This example assumes two EOA wallets.
For using different signature types, you need to set the funder address signature type when initializing the client.

Usage: python rfq_full_flow.py

TROUBLESHOOTING "invalid request" errors:
1. Ensure the tokenID exists in your environment (staging vs production)
2. Check that the market is active, RFQ-enabled and has liquidity
3. Verify your quoter has been whitelisted
4. Run `python examples/get_markets.py` to get valid token IDs
5. Make sure you're using the correct CLOB_API_URL for your environment


ENV VARIABLES:
REQUESTER_PK: Private key of the requester
REQUESTER_API_KEY: API key of the requester
REQUESTER_SECRET: Secret of the requester
REQUESTER_PASS_PHRASE: Passphrase of the requester
QUOTER_PK: Private key of the quoter
QUOTER_API_KEY: API key of the quoter
QUOTER_SECRET: Secret of the quoter
QUOTER_PASS_PHRASE: Passphrase of the quoter
CHAIN_ID: Chain ID of the network
CLOB_API_URL: URL of the CLOB API
"""

import os
import time

from dotenv import load_dotenv

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import RfqUserRequest, RfqUserQuote, AcceptQuoteParams, ApproveOrderParams
from py_clob_client.order_builder.constants import BUY, SELL
from py_clob_client.constants import AMOY

load_dotenv()

# ============================================
# RFQ REQUEST PARAMETERS (REQUESTER) - EDIT THESE
# ============================================
TOKEN_ID = "34097058504275310827233323421517291090691602969494795225921954353603704046623"

USER_REQUEST = RfqUserRequest(
    token_id=TOKEN_ID,
    price=0.50,       # Price per token (e.g., 0.50 = 50 cents)
    side=BUY,         # BUY or SELL
    size=100.0,       # Number of tokens
)

# ============================================
# RFQ QUOTE PARAMETERS (QUOTER) - EDIT THESE
# ============================================
QUOTE_TOKEN_ID = TOKEN_ID  # Token ID for the quote (defaults to same as request)
QUOTE_PRICE = 0.50  # Quoted price per token
QUOTE_SIZE = 100.0  # Number of tokens to quote
QUOTE_SIDE = SELL   # BUY or SELL

# ============================================
# EXPIRATION CONFIGURATION
# ============================================
EXPIRATION_SECONDS = 3600  # 1 hour


def main():
    # ============================================
    # Setup: Initialize both requester and quoter clients
    # ============================================
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))

    # Requester (creates the request and accepts the quote)
    requester_key = os.getenv("REQUESTER_PK")
    requester_creds = ApiCreds(
        api_key=os.getenv("REQUESTER_API_KEY"),
        api_secret=os.getenv("REQUESTER_SECRET"),
        api_passphrase=os.getenv("REQUESTER_PASS_PHRASE"),
    )
    requester_client = ClobClient(host, key=requester_key, chain_id=chain_id, creds=requester_creds)

    # Quoter (creates the quote and approves the order)
    quoter_key = os.getenv("QUOTER_PK")
    quoter_creds = ApiCreds(
        api_key=os.getenv("QUOTER_API_KEY"),
        api_secret=os.getenv("QUOTER_SECRET"),
        api_passphrase=os.getenv("QUOTER_PASS_PHRASE"),
    )
    quoter_client = ClobClient(host, key=quoter_key, chain_id=chain_id, creds=quoter_creds)

    print("=" * 60)
    print("RFQ Full Flow")
    print("=" * 60)
    print(f"Host: {host}")
    print(f"Chain ID: {chain_id}")
    print("=" * 60)
    
    # ============================================
    # Step 1: Requester creates RFQ request
    # ============================================
    print("\n[Step 1] Requester creating RFQ request...")
    print(f"  Token ID: {USER_REQUEST.token_id}")
    print(f"  Side: {USER_REQUEST.side}")
    print(f"  Size: {USER_REQUEST.size}")
    print(f"  Price: {USER_REQUEST.price}")

    rfq_request_response = requester_client.rfq.create_rfq_request(USER_REQUEST)

    # Check for errors
    if rfq_request_response.get("error"):
        print(f"Failed to create request. Error: {rfq_request_response['error']}")
        raise Exception(f"Request creation failed: {rfq_request_response['error']}")

    request_id = rfq_request_response.get("requestId")
    if not request_id:
        print(f"Failed to create request. Response: {rfq_request_response}")
        raise Exception("Request creation failed - no requestId returned")

    print("Request created successfully!")
    print(f"  Request ID: {request_id}")
    print(f"  Full response: {rfq_request_response}")
    
    # ============================================
    # Step 2: Quoter creates quote for the request
    # ============================================
    print("\n[Step 2] Quoter creating quote for request...")
    print(f"  Request ID: {request_id}")
    print(f"  Token ID: {QUOTE_TOKEN_ID}")
    print(f"  Price: {QUOTE_PRICE}")
    print(f"  Side: {QUOTE_SIDE}")
    print(f"  Size: {QUOTE_SIZE}")

    user_quote = RfqUserQuote(
        request_id=request_id,
        token_id=QUOTE_TOKEN_ID,
        price=QUOTE_PRICE,
        side=QUOTE_SIDE,
        size=QUOTE_SIZE,
    )
    rfq_quote_response = quoter_client.rfq.create_rfq_quote(user_quote)

    # Check for errors
    if rfq_quote_response.get("error"):
        print(f"Failed to create quote. Error: {rfq_quote_response['error']}")
        raise Exception(f"Quote creation failed: {rfq_quote_response['error']}")

    quote_id = rfq_quote_response.get("quoteId")
    if not quote_id:
        print(f"Failed to create quote. Response: {rfq_quote_response}")
        raise Exception("Quote creation failed - no quoteId returned")

    print("Quote created successfully!")
    print(f"  Quote ID: {quote_id}")
    print(f"  Request ID: {request_id}")
    print(f"  Full response: {rfq_quote_response}")
    
    # ============================================
    # Step 3: Requester accepts the quote
    # ============================================
    print("\n[Step 3] Requester accepting quote...")

    expiration = int(time.time()) + EXPIRATION_SECONDS

    accept_params = AcceptQuoteParams(
        request_id=request_id,
        quote_id=quote_id,
        expiration=expiration,
    )
    accept_result = requester_client.rfq.accept_rfq_quote(accept_params)

    print(accept_result)
    print("Quote accepted successfully!")
    print(f"  Request ID: {request_id}")
    print(f"  Quote ID: {quote_id}")
    
    # ============================================
    # Step 4: Quoter approves the order
    # ============================================
    print("\n[Step 4] Quoter approving order...")

    approve_params = ApproveOrderParams(
        request_id=request_id,
        quote_id=quote_id,
        expiration=expiration,
    )
    approve_result = quoter_client.rfq.approve_rfq_order(approve_params)

    print(approve_result)
    print("Order approved successfully!")
    print(f"  Request ID: {request_id}")
    print(f"  Quote ID: {quote_id}")
    
    # ============================================
    # Summary
    # ============================================
    print("\n" + "=" * 60)
    print("RFQ FLOW COMPLETED SUCCESSFULLY!")
    print("=" * 60)
    print(f"Request ID: {request_id}")
    print(f"Quote ID:   {quote_id}")
    print("=" * 60)


main()

================================================
FILE: examples/rfq_get_best_quote.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import GetRfqBestQuoteParams
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    params = GetRfqBestQuoteParams(
        request_id=os.getenv("RFQ_REQUEST_ID", "019b9f8d-2336-70db-a0dd-071363fb1ff3")
    )
    resp = client.rfq.get_rfq_best_quote(params)
    print(resp)
    print("Done!")


main()




================================================
FILE: examples/rfq_get_quotes.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import GetRfqQuotesParams
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    params = GetRfqQuotesParams(
        # quote_ids=["0197656d-56ee-74a4-a06a-3b179121f3bf"],
        state="active",
        # markets=["0x0000000000000000000000000000000000000000"],
        # size_min=5,
        # size_max=100,
        # price_min=0.1,
        # price_max=0.9,
        # sort_by="price",
        # sort_dir="asc",
        limit=10,
        # offset="MA==",
    )

    # Get quotes on your requests (requester view)
    # Returns quotes that others have made on your RFQ requests
    print("Requester quotes (quotes on my requests):")
    requester_resp = client.rfq.get_rfq_requester_quotes(params)
    print(requester_resp)

    # Get quotes you've created (quoter view)
    # Returns quotes that you have made on others' RFQ requests
    print("\nQuoter quotes (quotes I've made):")
    quoter_resp = client.rfq.get_rfq_quoter_quotes(params)
    print(quoter_resp)

    print("Done!")


main()


================================================
FILE: examples/rfq_get_requests.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds
from py_clob_client.rfq import GetRfqRequestsParams
from py_clob_client.constants import AMOY
from dotenv import load_dotenv

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob-staging.polymarket.com/")
    chain_id = int(os.getenv("CHAIN_ID", AMOY))
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # Get all active RFQ requests
    params = GetRfqRequestsParams(
        # request_ids=["0197656d-56ee-74a4-a06a-3b179121f3bf"],
        state="active",
        # markets=["0x0000000000000000000000000000000000000000"],
        # size_min=5,
        # size_max=100,
        # price_min=0.1,
        # price_max=0.9,
        # sort_by="price",
        # sort_dir="asc",
        limit=10,
        offset="MA==",
    )
    resp = client.rfq.get_rfq_requests(params)
    print(resp)
    print("Done!")


main()


================================================
FILE: examples/update_balance_allowance.py
================================================
import os

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import ApiCreds, BalanceAllowanceParams, AssetType
from dotenv import load_dotenv
from py_clob_client.constants import AMOY

load_dotenv()


def main():
    host = os.getenv("CLOB_API_URL", "https://clob.polymarket.com")
    key = os.getenv("PK")
    creds = ApiCreds(
        api_key=os.getenv("CLOB_API_KEY"),
        api_secret=os.getenv("CLOB_SECRET"),
        api_passphrase=os.getenv("CLOB_PASS_PHRASE"),
    )
    chain_id = AMOY
    client = ClobClient(host, key=key, chain_id=chain_id, creds=creds)

    # USDC
    client.update_balance_allowance(
        params=BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)
    )

    # YES
    client.update_balance_allowance(
        params=BalanceAllowanceParams(
            asset_type=AssetType.CONDITIONAL,
            token_id="52114319501245915516055106046884209969926127482827954674443846427813813222426",
        )
    )

    # NO
    client.update_balance_allowance(
        params=BalanceAllowanceParams(
            asset_type=AssetType.CONDITIONAL,
            token_id="71321045679252212594626385532706912750332728571942532289631379312455583992563",
        )
    )


main()


================================================
FILE: py_clob_client/__init__.py
================================================
from .client import ClobClient
from .clob_types import (
    ApiCreds,
    OrderArgs,
    MarketOrderArgs,
    OrderType,
    TickSize,
    BookParams,
    TradeParams,
    OpenOrderParams,
    BalanceAllowanceParams,
    AssetType,
    PartialCreateOrderOptions,
    CreateOrderOptions,
)

# RFQ exports
from .rfq import (
    RfqClient,
    RfqUserRequest,
    RfqUserQuote,
    CreateRfqRequestParams,
    CreateRfqQuoteParams,
    CancelRfqRequestParams,
    CancelRfqQuoteParams,
    AcceptQuoteParams,
    ApproveOrderParams,
    GetRfqRequestsParams,
    GetRfqQuotesParams,
    GetRfqBestQuoteParams,
    RfqRequest,
    RfqQuote,
    RfqRequestResponse,
    RfqQuoteResponse,
    RfqPaginatedResponse,
)

__all__ = [
    # Main client
    "ClobClient",
    # Core types
    "ApiCreds",
    "OrderArgs",
    "MarketOrderArgs",
    "OrderType",
    "TickSize",
    "BookParams",
    "TradeParams",
    "OpenOrderParams",
    "BalanceAllowanceParams",
    "AssetType",
    "PartialCreateOrderOptions",
    "CreateOrderOptions",
    # RFQ client
    "RfqClient",
    # RFQ input types
    "RfqUserRequest",
    "RfqUserQuote",
    "CreateRfqRequestParams",
    "CreateRfqQuoteParams",
    "CancelRfqRequestParams",
    "CancelRfqQuoteParams",
    "AcceptQuoteParams",
    "ApproveOrderParams",
    "GetRfqRequestsParams",
    "GetRfqQuotesParams",
    "GetRfqBestQuoteParams",
    # RFQ response types
    "RfqRequest",
    "RfqQuote",
    "RfqRequestResponse",
    "RfqQuoteResponse",
    "RfqPaginatedResponse",
]


================================================
FILE: py_clob_client/client.py
================================================
import logging
import json
import time
from typing import Optional

from py_builder_signing_sdk.config import BuilderConfig

from .order_builder.builder import OrderBuilder
from .headers.headers import (
    create_level_1_headers,
    create_level_2_headers,
    enrich_l2_headers_with_builder_headers,
)
from .signer import Signer
from .config import get_contract_config

from .endpoints import (
    CANCEL,
    CANCEL_ORDERS,
    CANCEL_MARKET_ORDERS,
    CANCEL_ALL,
    CREATE_API_KEY,
    DELETE_API_KEY,
    DERIVE_API_KEY,
    GET_API_KEYS,
    CLOSED_ONLY,
    CREATE_READONLY_API_KEY,
    GET_READONLY_API_KEYS,
    DELETE_READONLY_API_KEY,
    VALIDATE_READONLY_API_KEY,
    GET_LAST_TRADE_PRICE,
    GET_ORDER,
    GET_ORDER_BOOK,
    MID_POINT,
    ORDERS,
    POST_ORDER,
    POST_ORDERS,
    PRICE,
    TIME,
    TRADES,
    GET_NOTIFICATIONS,
    DROP_NOTIFICATIONS,
    GET_BALANCE_ALLOWANCE,
    UPDATE_BALANCE_ALLOWANCE,
    IS_ORDER_SCORING,
    GET_TICK_SIZE,
    GET_NEG_RISK,
    GET_FEE_RATE,
    ARE_ORDERS_SCORING,
    GET_SIMPLIFIED_MARKETS,
    GET_MARKETS,
    GET_MARKET,
    GET_SAMPLING_SIMPLIFIED_MARKETS,
    GET_SAMPLING_MARKETS,
    GET_MARKET_TRADES_EVENTS,
    GET_LAST_TRADES_PRICES,
    MID_POINTS,
    GET_ORDER_BOOKS,
    GET_PRICES,
    GET_SPREAD,
    GET_SPREADS,
    GET_BUILDER_TRADES,
    POST_HEARTBEAT,
)
from .clob_types import (
    ApiCreds,
    ReadonlyApiKeyResponse,
    TradeParams,
    OpenOrderParams,
    OrderArgs,
    RequestArgs,
    DropNotificationParams,
    OrderBookSummary,
    BalanceAllowanceParams,
    OrderScoringParams,
    TickSize,
    CreateOrderOptions,
    OrdersScoringParams,
    OrderType,
    PartialCreateOrderOptions,
    BookParams,
    MarketOrderArgs,
    PostOrdersArgs,
)
from .exceptions import PolyException
from .http_helpers.helpers import (
    add_query_trade_params,
    add_query_open_orders_params,
    delete,
    get,
    post,
    drop_notifications_query_params,
    add_balance_allowance_params_to_url,
    add_order_scoring_params_to_url,
)

from .constants import (
    L0,
    L1,
    L1_AUTH_UNAVAILABLE,
    L2,
    L2_AUTH_UNAVAILABLE,
    END_CURSOR,
    BUILDER_AUTH_UNAVAILABLE,
)
from .utilities import (
    parse_raw_orderbook_summary,
    generate_orderbook_summary_hash,
    order_to_json,
    is_tick_size_smaller,
    price_valid,
)
from .rfq import RfqClient


class ClobClient:
    def __init__(
        self,
        host,
        chain_id: int = None,
        key: str = None,
        creds: ApiCreds = None,
        signature_type: int = None,
        funder: str = None,
        builder_config: BuilderConfig = None,
        tick_size_ttl: float = 300.0,
    ):
        """
        Initializes the clob client
        The client can be started in 3 modes:
        1) Level 0: Requires only the clob host url
                    Allows access to open CLOB endpoints

        2) Level 1: Requires the host, chain_id and a private key.
                    Allows access to L1 authenticated endpoints + all unauthenticated endpoints

        3) Level 2: Requires the host, chain_id, a private key, and Credentials.
                    Allows access to all endpoints
        """
        self.host = host[0:-1] if host.endswith("/") else host
        self.chain_id = chain_id
        self.signer = Signer(key, chain_id) if key else None
        self.creds = creds
        self.mode = self._get_client_mode()

        if self.signer:
            self.builder = OrderBuilder(
                self.signer, sig_type=signature_type, funder=funder
            )

        self.builder_config = None
        if builder_config:
            self.builder_config = builder_config

        # local cache
        self.__tick_sizes = {}
        self.__tick_size_timestamps = {}
        self.__tick_size_ttl = tick_size_ttl
        self.__neg_risk = {}
        self.__fee_rates = {}

        # RFQ client
        self.rfq = RfqClient(self)

        self.logger = logging.getLogger(self.__class__.__name__)

    def get_address(self):
        """
        Returns the public address of the signer
        """
        return self.signer.address() if self.signer else None

    def get_collateral_address(self):
        """
        Returns the collateral token address
        """
        contract_config = get_contract_config(self.chain_id)
        if contract_config:
            return contract_config.collateral

    def get_conditional_address(self):
        """
        Returns the conditional token address
        """
        contract_config = get_contract_config(self.chain_id)
        if contract_config:
            return contract_config.conditional_tokens

    def get_exchange_address(self, neg_risk=False):
        """
        Returns the exchange address
        """
        contract_config = get_contract_config(self.chain_id, neg_risk)
        if contract_config:
            return contract_config.exchange

    def get_ok(self):
        """
        Health check: Confirms that the server is up
        Does not need authentication
        """
        return get("{}/".format(self.host))

    def get_server_time(self):
        """
        Returns the current timestamp on the server
        Does not need authentication
        """
        return get("{}{}".format(self.host, TIME))

    def create_api_key(self, nonce: int = None) -> ApiCreds:
        """
        Creates a new CLOB API key for the given
        """
        self.assert_level_1_auth()

        endpoint = "{}{}".format(self.host, CREATE_API_KEY)
        headers = create_level_1_headers(self.signer, nonce)

        creds_raw = post(endpoint, headers=headers)
        try:
            creds = ApiCreds(
                api_key=creds_raw["apiKey"],
                api_secret=creds_raw["secret"],
                api_passphrase=creds_raw["passphrase"],
            )
        except:
            self.logger.error("Couldn't parse created CLOB creds")
            return None
        return creds

    def derive_api_key(self, nonce: int = None) -> ApiCreds:
        """
        Derives an already existing CLOB API key for the given address and nonce
        """
        self.assert_level_1_auth()

        endpoint = "{}{}".format(self.host, DERIVE_API_KEY)
        headers = create_level_1_headers(self.signer, nonce)

        creds_raw = get(endpoint, headers=headers)
        try:
            creds = ApiCreds(
                api_key=creds_raw["apiKey"],
                api_secret=creds_raw["secret"],
                api_passphrase=creds_raw["passphrase"],
            )
        except:
            self.logger.error("Couldn't parse derived CLOB creds")
            return None
        return creds

    def create_or_derive_api_creds(self, nonce: int = None) -> ApiCreds:
        """
        Creates API creds if not already created for nonce, otherwise derives them
        """
        try:
            return self.create_api_key(nonce)
        except:
            return self.derive_api_key(nonce)

    def set_api_creds(self, creds: ApiCreds):
        """
        Sets client api creds
        """
        self.creds = creds
        self.mode = self._get_client_mode()

    def get_api_keys(self):
        """
        Gets the available API keys for this address
        Level 2 Auth required
        """
        self.assert_level_2_auth()

        request_args = RequestArgs(method="GET", request_path=GET_API_KEYS)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return get("{}{}".format(self.host, GET_API_KEYS), headers=headers)

    def get_closed_only_mode(self):
        """
        Gets the closed only mode flag for thsi address
        Level 2 Auth required
        """
        self.assert_level_2_auth()

        request_args = RequestArgs(method="GET", request_path=CLOSED_ONLY)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return get("{}{}".format(self.host, CLOSED_ONLY), headers=headers)

    def delete_api_key(self):
        """
        Deletes an API key
        Level 2 Auth required
        """
        self.assert_level_2_auth()

        request_args = RequestArgs(method="DELETE", request_path=DELETE_API_KEY)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return delete("{}{}".format(self.host, DELETE_API_KEY), headers=headers)

    def create_readonly_api_key(self) -> ReadonlyApiKeyResponse:
        """
        Creates a new readonly API key for a user
        Level 2 Auth required
        """
        self.assert_level_2_auth()

        request_args = RequestArgs(method="POST", request_path=CREATE_READONLY_API_KEY)
        headers = create_level_2_headers(self.signer, self.creds, request_args)

        response = post("{}{}".format(self.host, CREATE_READONLY_API_KEY), headers=headers)
        try:
            return ReadonlyApiKeyResponse(api_key=response["apiKey"])
        except:
            self.logger.error("Couldn't parse readonly API key response")
            return None

    def get_readonly_api_keys(self) -> list[str]:
        """
        Gets the available readonly API keys for this address
        Level 2 Auth required
        """
        self.assert_level_2_auth()

        request_args = RequestArgs(method="GET", request_path=GET_READONLY_API_KEYS)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return get("{}{}".format(self.host, GET_READONLY_API_KEYS), headers=headers)

    def delete_readonly_api_key(self, key: str) -> bool:
        """
        Deletes a readonly API key for a user
        Level 2 Auth required
        """
        self.assert_level_2_auth()

        body = {"key": key}
        serialized = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        request_args = RequestArgs(
            method="DELETE",
            request_path=DELETE_READONLY_API_KEY,
            body=body,
            serialized_body=serialized,
        )
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return delete(
            "{}{}".format(self.host, DELETE_READONLY_API_KEY),
            headers=headers,
            data=serialized,
        )

    def validate_readonly_api_key(self, address: str, key: str) -> str:
        """
        Validates a readonly API key for a given address
        This is a public endpoint, no authentication required
        """
        return get(
            "{}{}?address={}&key={}".format(
                self.host, VALIDATE_READONLY_API_KEY, address, key
            )
        )

    def get_midpoint(self, token_id):
        """
        Get the mid market price for the given market
        """
        return get("{}{}?token_id={}".format(self.host, MID_POINT, token_id))

    def get_midpoints(self, params: list[BookParams]):
        """
        Get the mid market prices for a set of token ids
        """
        body = [{"token_id": param.token_id} for param in params]
        return post("{}{}".format(self.host, MID_POINTS), data=body)

    def get_price(self, token_id, side):
        """
        Get the market price for the given market
        """
        return get("{}{}?token_id={}&side={}".format(self.host, PRICE, token_id, side))

    def get_prices(self, params: list[BookParams]):
        """
        Get the market prices for a set
        """
        body = [{"token_id": param.token_id, "side": param.side} for param in params]
        return post("{}{}".format(self.host, GET_PRICES), data=body)

    def get_spread(self, token_id):
        """
        Get the spread for the given market
        """
        return get("{}{}?token_id={}".format(self.host, GET_SPREAD, token_id))

    def get_spreads(self, params: list[BookParams]):
        """
        Get the spreads for a set of token ids
        """
        body = [{"token_id": param.token_id} for param in params]
        return post("{}{}".format(self.host, GET_SPREADS), data=body)

    def get_tick_size(self, token_id: str) -> TickSize:
        cached_at = self.__tick_size_timestamps.get(token_id)

        if (
            token_id in self.__tick_sizes
            and cached_at is not None
            and (time.monotonic() - cached_at) < self.__tick_size_ttl
        ):
            return self.__tick_sizes[token_id]

        result = get("{}{}?token_id={}".format(self.host, GET_TICK_SIZE, token_id))
        self.__tick_sizes[token_id] = str(result["minimum_tick_size"])
        self.__tick_size_timestamps[token_id] = time.monotonic()

        return self.__tick_sizes[token_id]

    def clear_tick_size_cache(self, token_id: str = None):
        """
        Clears the tick size cache, forcing fresh fetches on the next access.

        Args:
            token_id: If provided, only clears the cache for this token.
                      Otherwise clears all cached tick sizes.
        """
        if token_id is not None:
            self.__tick_sizes.pop(token_id, None)
            self.__tick_size_timestamps.pop(token_id, None)
        else:
            self.__tick_sizes.clear()
            self.__tick_size_timestamps.clear()

    def _update_tick_size_from_order_book(self, book: OrderBookSummary):
        """
        Opportunistically updates the tick size cache from an order book response.
        """
        if book and book.asset_id and book.tick_size:
            self.__tick_sizes[book.asset_id] = str(book.tick_size)
            self.__tick_size_timestamps[book.asset_id] = time.monotonic()

    def get_neg_risk(self, token_id: str) -> bool:
        if token_id in self.__neg_risk:
            return self.__neg_risk[token_id]

        result = get("{}{}?token_id={}".format(self.host, GET_NEG_RISK, token_id))
        self.__neg_risk[token_id] = result["neg_risk"]

        return result["neg_risk"]

    def get_fee_rate_bps(self, token_id: str) -> int:
        if token_id in self.__fee_rates:
            return self.__fee_rates[token_id]

        result = get("{}{}?token_id={}".format(self.host, GET_FEE_RATE, token_id))
        fee_rate = result.get("base_fee") or 0
        self.__fee_rates[token_id] = fee_rate

        return fee_rate

    def __resolve_tick_size(
        self, token_id: str, tick_size: TickSize = None
    ) -> TickSize:
        min_tick_size = self.get_tick_size(token_id)
        if tick_size is not None:
            if is_tick_size_smaller(tick_size, min_tick_size):
                raise Exception(
                    "invalid tick size ("
                    + str(tick_size)
                    + "), minimum for the market is "
                    + str(min_tick_size),
                )
        else:
            tick_size = min_tick_size
        return tick_size

    def __resolve_fee_rate(self, token_id: str, user_fee_rate: int = None) -> int:
        market_fee_rate_bps = self.get_fee_rate_bps(token_id)
        # If both fee rate on the market and the user supplied fee rate are non-zero, validate that they match
        # else return the market fee rate
        if (
            market_fee_rate_bps is not None
            and market_fee_rate_bps > 0
            and user_fee_rate is not None
            and user_fee_rate > 0
            and user_fee_rate != market_fee_rate_bps
        ):
            raise Exception(
                f"invalid user provided fee rate: ({user_fee_rate}), fee rate for the market must be {market_fee_rate_bps}"
            )
        return market_fee_rate_bps

    def create_order(
        self, order_args: OrderArgs, options: Optional[PartialCreateOrderOptions] = None
    ):
        """
        Creates and signs an order
        Level 1 Auth required
        """
        self.assert_level_1_auth()

        # add resolve_order_options, or similar
        tick_size = self.__resolve_tick_size(
            order_args.token_id,
            options.tick_size if options else None,
        )

        if not price_valid(order_args.price, tick_size):
            raise Exception(
                "price ("
                + str(order_args.price)
                + "), min: "
                + str(tick_size)
                + " - max: "
                + str(1 - float(tick_size))
            )

        neg_risk = (
            options.neg_risk
            if options and options.neg_risk
            else self.get_neg_risk(order_args.token_id)
        )

        # fee rate
        fee_rate_bps = self.__resolve_fee_rate(
            order_args.token_id, order_args.fee_rate_bps
        )
        order_args.fee_rate_bps = fee_rate_bps

        return self.builder.create_order(
            order_args,
            CreateOrderOptions(
                tick_size=tick_size,
                neg_risk=neg_risk,
            ),
        )

    def create_market_order(
        self,
        order_args: MarketOrderArgs,
        options: Optional[PartialCreateOrderOptions] = None,
    ):
        """
        Creates and signs an order
        Level 1 Auth required
        """
        self.assert_level_1_auth()

        # add resolve_order_options, or similar
        tick_size = self.__resolve_tick_size(
            order_args.token_id,
            options.tick_size if options else None,
        )

        if order_args.price is None or order_args.price <= 0:
            order_args.price = self.calculate_market_price(
                order_args.token_id,
                order_args.side,
                order_args.amount,
                order_args.order_type,
            )

        if not price_valid(order_args.price, tick_size):
            raise Exception(
                "price ("
                + str(order_args.price)
                + "), min: "
                + str(tick_size)
                + " - max: "
                + str(1 - float(tick_size))
            )

        neg_risk = (
            options.neg_risk
            if options and options.neg_risk
            else self.get_neg_risk(order_args.token_id)
        )

        # fee rate
        fee_rate_bps = self.__resolve_fee_rate(
            order_args.token_id, order_args.fee_rate_bps
        )
        order_args.fee_rate_bps = fee_rate_bps

        return self.builder.create_market_order(
            order_args,
            CreateOrderOptions(
                tick_size=tick_size,
                neg_risk=neg_risk,
            ),
        )

    def post_orders(self, args: list[PostOrdersArgs]):
        """
        Posts orders
        """
        self.assert_level_2_auth()
        body = [
            order_to_json(arg.order, self.creds.api_key, arg.orderType, arg.postOnly) for arg in args
        ]
        request_args = RequestArgs(
            method="POST",
            request_path=POST_ORDERS,
            body=body,
            serialized_body=json.dumps(body, separators=(",", ":"), ensure_ascii=False),
        )
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        # Builder flow
        if self.can_builder_auth():
            builder_headers = self._generate_builder_headers(request_args, headers)
            if builder_headers is not None:
                return post(
                    "{}{}".format(self.host, POST_ORDERS),
                    headers=builder_headers,
                    data=request_args.serialized_body,
                )
        # send exact serialized bytes
        return post(
            "{}{}".format(self.host, POST_ORDERS),
            headers=headers,
            data=request_args.serialized_body,
        )

    def post_order(self, order, orderType: OrderType = OrderType.GTC, post_only: bool = False):
        """
        Posts the order
        """
        if post_only and (orderType != OrderType.GTC and orderType != OrderType.GTD):
            raise Exception("post_only orders can only be of type GTC or GTD")

        self.assert_level_2_auth()
        body = order_to_json(order, self.creds.api_key, orderType, post_only)
        request_args = RequestArgs(
            method="POST",
            request_path=POST_ORDER,
            body=body,
            serialized_body=json.dumps(body, separators=(",", ":"), ensure_ascii=False),
        )
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        # Builder flow
        if self.can_builder_auth():
            builder_headers = self._generate_builder_headers(request_args, headers)
            if builder_headers is not None:
                return post(
                    "{}{}".format(self.host, POST_ORDER),
                    headers=builder_headers,
                    data=request_args.serialized_body,
                )
        return post(
            "{}{}".format(self.host, POST_ORDER),
            headers=headers,
            data=request_args.serialized_body,
        )

    def create_and_post_order(
        self, order_args: OrderArgs, options: PartialCreateOrderOptions = None
    ):
        """
        Utility function to create and publish an order
        """
        ord = self.create_order(order_args, options)
        return self.post_order(ord)

    def cancel(self, order_id):
        """
        Cancels an order
        Level 2 Auth required
        """
        self.assert_level_2_auth()
        body = {"orderID": order_id}

        request_args = RequestArgs(
            method="DELETE",
            request_path=CANCEL,
            body=body,
            serialized_body=json.dumps(body, separators=(",", ":"), ensure_ascii=False),
        )
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return delete(
            "{}{}".format(self.host, CANCEL),
            headers=headers,
            data=request_args.serialized_body,
        )

    def cancel_orders(self, order_ids):
        """
        Cancels orders
        Level 2 Auth required
        """
        self.assert_level_2_auth()
        body = order_ids
        serialized = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        request_args = RequestArgs(
            method="DELETE",
            request_path=CANCEL_ORDERS,
            body=body,
            serialized_body=serialized,
        )
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return delete(
            "{}{}".format(self.host, CANCEL_ORDERS), headers=headers, data=serialized
        )

    def cancel_all(self):
        """
        Cancels all available orders for the user
        Level 2 Auth required
        """
        self.assert_level_2_auth()
        request_args = RequestArgs(method="DELETE", request_path=CANCEL_ALL)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return delete("{}{}".format(self.host, CANCEL_ALL), headers=headers)

    def post_heartbeat(self, heartbeat_id: Optional[str]):
        """
        Sends a heartbeat to the server, if heartbeats are started and one isn't sent within 10s, all orders will be cancelled
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        body = {"heartbeat_id": heartbeat_id}
        serialized = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        request_args = RequestArgs(method="POST", request_path=POST_HEARTBEAT, body=body, serialized_body=serialized)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return post(
            "{}{}".format(self.host, POST_HEARTBEAT),
            headers=headers,
            data=serialized
        )

    def cancel_market_orders(self, market: str = "", asset_id: str = ""):
        """
        Cancels orders
        Level 2 Auth required
        """
        self.assert_level_2_auth()
        body = {"market": market, "asset_id": asset_id}
        serialized = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        request_args = RequestArgs(
            method="DELETE",
            request_path=CANCEL_MARKET_ORDERS,
            body=body,
            serialized_body=serialized,
        )
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return delete(
            "{}{}".format(self.host, CANCEL_MARKET_ORDERS),
            headers=headers,
            data=serialized,
        )

    def get_orders(self, params: OpenOrderParams = None, next_cursor="MA=="):
        """
        Gets orders for the API key
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        request_args = RequestArgs(method="GET", request_path=ORDERS)
        headers = create_level_2_headers(self.signer, self.creds, request_args)

        results = []
        next_cursor = next_cursor if next_cursor is not None else "MA=="
        while next_cursor != END_CURSOR:
            url = add_query_open_orders_params(
                "{}{}".format(self.host, ORDERS), params, next_cursor
            )
            response = get(url, headers=headers)
            next_cursor = response["next_cursor"]
            results += response["data"]

        return results

    def get_order_book(self, token_id) -> OrderBookSummary:
        """
        Fetches the orderbook for the token_id
        """
        raw_obs = get("{}{}?token_id={}".format(self.host, GET_ORDER_BOOK, token_id))
        result = parse_raw_orderbook_summary(raw_obs)
        self._update_tick_size_from_order_book(result)
        return result

    def get_order_books(self, params: list[BookParams]) -> list[OrderBookSummary]:
        """
        Fetches the orderbook for a set of token ids
        """
        body = [{"token_id": param.token_id} for param in params]
        raw_obs = post("{}{}".format(self.host, GET_ORDER_BOOKS), data=body)
        results = [parse_raw_orderbook_summary(r) for r in raw_obs]
        for book in results:
            self._update_tick_size_from_order_book(book)
        return results

    def get_order_book_hash(self, orderbook: OrderBookSummary) -> str:
        """
        Calculates the hash for the given orderbook
        """
        return generate_orderbook_summary_hash(orderbook)

    def get_order(self, order_id):
        """
        Fetches the order corresponding to the order_id
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        endpoint = "{}{}".format(GET_ORDER, order_id)
        request_args = RequestArgs(method="GET", request_path=endpoint)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return get("{}{}".format(self.host, endpoint), headers=headers)

    def get_trades(self, params: TradeParams = None, next_cursor="MA=="):
        """
        Fetches the trade history for a user
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        request_args = RequestArgs(method="GET", request_path=TRADES)
        headers = create_level_2_headers(self.signer, self.creds, request_args)

        results = []
        next_cursor = next_cursor if next_cursor is not None else "MA=="
        while next_cursor != END_CURSOR:
            url = add_query_trade_params(
                "{}{}".format(self.host, TRADES), params, next_cursor
            )
            response = get(url, headers=headers)
            next_cursor = response["next_cursor"]
            results += response["data"]

        return results

    def get_last_trade_price(self, token_id):
        """
        Fetches the last trade price token_id
        """
        return get("{}{}?token_id={}".format(self.host, GET_LAST_TRADE_PRICE, token_id))

    def get_last_trades_prices(self, params: list[BookParams]):
        """
        Fetches the last trades prices for a set of token ids
        """
        body = [{"token_id": param.token_id} for param in params]
        return post("{}{}".format(self.host, GET_LAST_TRADES_PRICES), data=body)

    def assert_level_1_auth(self):
        """
        Level 1 Poly Auth
        """
        if self.mode < L1:
            raise PolyException(L1_AUTH_UNAVAILABLE)

    def assert_level_2_auth(self):
        """
        Level 2 Poly Auth
        """
        if self.mode < L2:
            raise PolyException(L2_AUTH_UNAVAILABLE)

    def assert_builder_auth(self):
        """
        Builder Auth
        """
        if not self.can_builder_auth():
            raise PolyException(BUILDER_AUTH_UNAVAILABLE)

    def can_builder_auth(self) -> bool:
        return self.builder_config is not None and self.builder_config.is_valid()

    def _get_client_mode(self):
        if self.signer is not None and self.creds is not None:
            return L2
        if self.signer is not None:
            return L1
        return L0

    def _generate_builder_headers(self, request_args: RequestArgs, headers: dict):
        """
        Generates builder headers and attaches them to the L2 Header
        """
        if self.builder_config is not None:
            builder_headers = self._get_builder_headers(
                request_args.method,
                request_args.request_path,
                request_args.serialized_body,
            )
            if builder_headers is None:
                return None
            return enrich_l2_headers_with_builder_headers(headers, builder_headers)
        return None

    def _get_builder_headers(self, method: str, path: str, body: Optional[str] = None):
        """
        Generates builder headers for the given method, path, and body.

        Args:
            method (str): HTTP method.
            path (str): Request path.
            body (Optional[str]): Pre-serialized JSON string or None.

        Returns:
            dict or None: Builder headers as a dictionary, or None if not available.
        """
        headers = self.builder_config.generate_builder_headers(method, path, body)
        if headers:
            return headers.to_dict()
        return None

    def get_notifications(self):
        """
        Fetches the notifications for a user
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        request_args = RequestArgs(method="GET", request_path=GET_NOTIFICATIONS)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        url = "{}{}?signature_type={}".format(
            self.host, GET_NOTIFICATIONS, self.builder.sig_type
        )
        return get(url, headers=headers)

    def drop_notifications(self, params: DropNotificationParams = None):
        """
        Drops the notifications for a user
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        request_args = RequestArgs(method="DELETE", request_path=DROP_NOTIFICATIONS)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        url = drop_notifications_query_params(
            "{}{}".format(self.host, DROP_NOTIFICATIONS), params
        )
        return delete(url, headers=headers)

    def get_balance_allowance(self, params: BalanceAllowanceParams = None):
        """
        Fetches the balance & allowance for a user
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        request_args = RequestArgs(method="GET", request_path=GET_BALANCE_ALLOWANCE)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        if params.signature_type == -1:
            params.signature_type = self.builder.sig_type
        url = add_balance_allowance_params_to_url(
            "{}{}".format(self.host, GET_BALANCE_ALLOWANCE), params
        )
        return get(url, headers=headers)

    def update_balance_allowance(self, params: BalanceAllowanceParams = None):
        """
        Updates the balance & allowance for a user
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        request_args = RequestArgs(method="GET", request_path=UPDATE_BALANCE_ALLOWANCE)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        if params.signature_type == -1:
            params.signature_type = self.builder.sig_type
        url = add_balance_allowance_params_to_url(
            "{}{}".format(self.host, UPDATE_BALANCE_ALLOWANCE), params
        )
        return get(url, headers=headers)

    def is_order_scoring(self, params: OrderScoringParams):
        """
        Check if the order is currently scoring
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        request_args = RequestArgs(method="GET", request_path=IS_ORDER_SCORING)
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        url = add_order_scoring_params_to_url(
            "{}{}".format(self.host, IS_ORDER_SCORING), params
        )
        return get(url, headers=headers)

    def are_orders_scoring(self, params: OrdersScoringParams):
        """
        Check if the orders are currently scoring
        Requires Level 2 authentication
        """
        self.assert_level_2_auth()
        body = params.orderIds
        serialized = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        request_args = RequestArgs(
            method="POST",
            request_path=ARE_ORDERS_SCORING,
            body=body,
            serialized_body=serialized,
        )
        headers = create_level_2_headers(self.signer, self.creds, request_args)
        return post(
            "{}{}".format(self.host, ARE_ORDERS_SCORING),
            headers=headers,
            data=serialized,
        )

    def get_sampling_markets(self, next_cursor="MA=="):
        """
        Get the current sampling markets
        """
        return get(
            "{}{}?next_cursor={}".format(self.host, GET_SAMPLING_MARKETS, next_cursor)
        )

    def get_sampling_simplified_markets(self, next_cursor="MA=="):
        """
        Get the current sampling simplified markets
        """
        return get(
            "{}{}?next_cursor={}".format(
                self.host, GET_SAMPLING_SIMPLIFIED_MARKETS, next_cursor
            )
        )

    def get_markets(self, next_cursor="MA=="):
        """
        Get the current markets
        """
        return get("{}{}?next_cursor={}".format(self.host, GET_MARKETS, next_cursor))

    def get_simplified_markets(self, next_cursor="MA=="):
        """
        Get the current simplified markets
        """
        return get(
            "{}{}?next_cursor={}".format(self.host, GET_SIMPLIFIED_MARKETS, next_cursor)
        )

    def get_market(self, condition_id):
        """
        Get a market by condition_id
        """
        return get("{}{}{}".format(self.host, GET_MARKET, condition_id))

    def get_market_trades_events(self, condition_id):
        """
        Get the market's trades events by condition id
        """
        return get("{}{}{}".format(self.host, GET_MARKET_TRADES_EVENTS, condition_id))

    def get_builder_trades(self, params: TradeParams = None, next_cursor="MA=="):
        """
        Get trades originated by the builder
        """
        self.assert_builder_auth()

        request_args = RequestArgs(method="GET", request_path=GET_BUILDER_TRADES)
        headers = self._get_builder_headers(
            request_args.method, request_args.request_path, request_args.body
        )

        results = []
        next_cursor = next_cursor if next_cursor is not None else "MA=="
        while next_cursor != END_CURSOR:
            url = add_query_trade_params(
                "{}{}".format(self.host, GET_BUILDER_TRADES), params, next_cursor
            )
            response = get(url, headers=headers)
            next_cursor = response["next_cursor"]
            results += response["data"]

        return results

    def calculate_market_price(
        self, token_id: str, side: str, amount: float, order_type: OrderType
    ) -> float:
        """
        Calculates the matching price considering an amount and the current orderbook
        """
        book = self.get_order_book(token_id)
        if book is None:
            raise Exception("no orderbook")
        if side == "BUY":
            if book.asks is None:
                raise Exception("no match")
            return self.builder.calculate_buy_market_price(
                book.asks, amount, order_type
            )
        else:
            if book.bids is None:
                raise Exception("no match")
            return self.builder.calculate_sell_market_price(
                book.bids, amount, order_type
            )


================================================
FILE: py_clob_client/clob_types.py
================================================
from typing import Any
from dataclasses import dataclass, asdict
from json import dumps
from typing import Literal, Optional
from py_order_utils.model import (
    SignedOrder,
)

from .constants import ZERO_ADDRESS


class OrderType(enumerate):
    GTC = "GTC"
    FOK = "FOK"
    GTD = "GTD"
    FAK = "FAK"


@dataclass
class ApiCreds:
    api_key: str
    api_secret: str
    api_passphrase: str


@dataclass
class ReadonlyApiKeyResponse:
    api_key: str


@dataclass
class RequestArgs:
    method: str
    request_path: str
    body: Any = None
    serialized_body: Optional[str] = None


@dataclass
class BookParams:
    token_id: str
    side: str = ""


@dataclass
class OrderArgs:
    token_id: str
    """
    TokenID of the Conditional token asset being traded
    """

    price: float
    """
    Price used to create the order
    """

    size: float
    """
    Size in terms of the ConditionalToken
    """

    side: str
    """
    Side of the order
    """

    fee_rate_bps: int = 0
    """
    Fee rate, in basis points, charged to the order maker, charged on proceeds
    """

    nonce: int = 0
    """
    Nonce used for onchain cancellations
    """

    expiration: int = 0
    """
    Timestamp after which the order is expired.
    """

    taker: str = ZERO_ADDRESS
    """
    Address of the order taker. The zero address is used to indicate a public order
    """


@dataclass
class MarketOrderArgs:
    token_id: str
    """
    TokenID of the Conditional token asset being traded
    """

    amount: float
    """
    BUY orders: $$$ Amount to buy
    SELL orders: Shares to sell
    """

    side: str
    """
    Side of the order
    """

    price: float = 0
    """
    Price used to create the order
    """

    fee_rate_bps: int = 0
    """
    Fee rate, in basis points, charged to the order maker, charged on proceeds
    """

    nonce: int = 0
    """
    Nonce used for onchain cancellations
    """

    taker: str = ZERO_ADDRESS
    """
    Address of the order taker. The zero address is used to indicate a public order
    """

    order_type: OrderType = OrderType.FOK


@dataclass
class TradeParams:
    id: str = None
    maker_address: str = None
    market: str = None
    asset_id: str = None
    before: int = None
    after: int = None


@dataclass
class OpenOrderParams:
    id: str = None
    market: str = None
    asset_id: str = None


@dataclass
class DropNotificationParams:
    ids: list[str] = None


@dataclass
class OrderSummary:
    price: str = None
    size: str = None

    @property
    def __dict__(self):
        return asdict(self)

    @property
    def json(self):
        return dumps(self.__dict__)


@dataclass
class OrderBookSummary:
    market: str = None
    asset_id: str = None
    timestamp: str = None
    bids: list[OrderSummary] = None
    asks: list[OrderSummary] = None
    min_order_size: str = None
    neg_risk: bool = None
    tick_size: str = None
    last_trade_price: str = None
    hash: str = None

    @property
    def __dict__(self):
        return asdict(self)

    @property
    def json(self):
        return dumps(self.__dict__, separators=(",", ":"))


class AssetType(enumerate):
    COLLATERAL = "COLLATERAL"
    CONDITIONAL = "CONDITIONAL"


@dataclass
class BalanceAllowanceParams:
    asset_type: AssetType = None
    token_id: str = None
    signature_type: int = -1


@dataclass
class OrderScoringParams:
    orderId: str


@dataclass
class OrdersScoringParams:
    orderIds: list[str]


TickSize = Literal["0.1", "0.01", "0.001", "0.0001"]


@dataclass
class CreateOrderOptions:
    tick_size: TickSize
    neg_risk: bool


@dataclass
class PartialCreateOrderOptions:
    tick_size: Optional[TickSize] = None
    neg_risk: Optional[bool] = None


@dataclass
class RoundConfig:
    price: float
    size: float
    amount: float


@dataclass
class ContractConfig:
    """
    Contract Configuration
    """

    exchange: str
    """
    The exchange contract responsible for matching orders
    """

    collateral: str
    """
    The ERC20 token used as collateral for the exchange's markets
    """

    conditional_tokens: str
    """
    The ERC1155 conditional tokens contract
    """


@dataclass
class PostOrdersArgs:
    order: SignedOrder
    orderType: OrderType = OrderType.GTC
    postOnly: bool = False


================================================
FILE: py_clob_client/config.py
================================================
from .clob_types import ContractConfig


def get_contract_config(chainID: int, neg_risk: bool = False) -> ContractConfig:
    """
    Get the contract configuration for the chain
    """

    CONFIG = {
        137: ContractConfig(
            exchange="0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E",
            collateral="0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
            conditional_tokens="0x4D97DCd97eC945f40cF65F87097ACe5EA0476045",
        ),
        80002: ContractConfig(
            exchange="0xdFE02Eb6733538f8Ea35D585af8DE5958AD99E40",
            collateral="0x9c4e1703476e875070ee25b56a58b008cfb8fa78",
            conditional_tokens="0x69308FB512518e39F9b16112fA8d994F4e2Bf8bB",
        ),
    }

    NEG_RISK_CONFIG = {
        137: ContractConfig(
            exchange="0xC5d563A36AE78145C45a50134d48A1215220f80a",
            collateral="0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
            conditional_tokens="0x4D97DCd97eC945f40cF65F87097ACe5EA0476045",
        ),
        80002: ContractConfig(
            exchange="0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296",
            collateral="0x9c4e1703476e875070ee25b56a58b008cfb8fa78",
            conditional_tokens="0x69308FB512518e39F9b16112fA8d994F4e2Bf8bB",
        ),
    }

    if neg_risk:
        config = NEG_RISK_CONFIG.get(chainID)
    else:
        config = CONFIG.get(chainID)
    if config is None:
        raise Exception("Invalid chainID: ${}".format(chainID))

    return config


================================================
FILE: py_clob_client/constants.py
================================================
# Access levels
L0 = 0
L1 = 1
L2 = 2


CREDENTIAL_CREATION_WARNING = """🚨🚨🚨
Your credentials CANNOT be recovered after they've been created.
Be sure to store them safely!
🚨🚨🚨"""


L1_AUTH_UNAVAILABLE = "A private key is needed to interact with this endpoint!"

L2_AUTH_UNAVAILABLE = "API Credentials are needed to interact with this endpoint!"

BUILDER_AUTH_UNAVAILABLE = (
    "Builder API Credentials needed to interact with this endpoint!"
)

ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"

AMOY = 80002
POLYGON = 137

END_CURSOR = "LTE="


================================================
FILE: py_clob_client/endpoints.py
================================================
TIME = "/time"
CREATE_API_KEY = "/auth/api-key"
GET_API_KEYS = "/auth/api-keys"
DELETE_API_KEY = "/auth/api-key"
DERIVE_API_KEY = "/auth/derive-api-key"
CLOSED_ONLY = "/auth/ban-status/closed-only"

# Readonly API Key endpoints
CREATE_READONLY_API_KEY = "/auth/readonly-api-key"
GET_READONLY_API_KEYS = "/auth/readonly-api-keys"
DELETE_READONLY_API_KEY = "/auth/readonly-api-key"
VALIDATE_READONLY_API_KEY = "/auth/validate-readonly-api-key"

TRADES = "/data/trades"
GET_ORDER_BOOK = "/book"
GET_ORDER_BOOKS = "/books"
GET_ORDER = "/data/order/"
ORDERS = "/data/orders"
POST_ORDER = "/order"
POST_ORDERS = "/orders"
CANCEL = "/order"
CANCEL_ORDERS = "/orders"
CANCEL_ALL = "/cancel-all"
CANCEL_MARKET_ORDERS = "/cancel-market-orders"
MID_POINT = "/midpoint"
MID_POINTS = "/midpoints"
PRICE = "/price"
GET_PRICES = "/prices"
GET_SPREAD = "/spread"
GET_SPREADS = "/spreads"
GET_LAST_TRADE_PRICE = "/last-trade-price"
GET_LAST_TRADES_PRICES = "/last-trades-prices"
GET_NOTIFICATIONS = "/notifications"
DROP_NOTIFICATIONS = "/notifications"
GET_BALANCE_ALLOWANCE = "/balance-allowance"
UPDATE_BALANCE_ALLOWANCE = "/balance-allowance/update"
IS_ORDER_SCORING = "/order-scoring"
ARE_ORDERS_SCORING = "/orders-scoring"
GET_TICK_SIZE = "/tick-size"
GET_NEG_RISK = "/neg-risk"
GET_FEE_RATE = "/fee-rate"
GET_SAMPLING_SIMPLIFIED_MARKETS = "/sampling-simplified-markets"
GET_SAMPLING_MARKETS = "/sampling-markets"
GET_SIMPLIFIED_MARKETS = "/simplified-markets"
GET_MARKETS = "/markets"
GET_MARKET = "/markets/"
GET_MARKET_TRADES_EVENTS = "/live-activity/events/"
POST_HEARTBEAT = "/v1/heartbeats"
GET_BUILDER_TRADES = "/builder/trades"

# RFQ Endpoints
CREATE_RFQ_REQUEST = "/rfq/request"
CANCEL_RFQ_REQUEST = "/rfq/request"
GET_RFQ_REQUESTS = "/rfq/data/requests"
CREATE_RFQ_QUOTE = "/rfq/quote"
CANCEL_RFQ_QUOTE = "/rfq/quote"
GET_RFQ_REQUESTER_QUOTES = "/rfq/data/requester/quotes"
GET_RFQ_QUOTER_QUOTES = "/rfq/data/quoter/quotes"
GET_RFQ_BEST_QUOTE = "/rfq/data/best-quote"
RFQ_REQUESTS_ACCEPT = "/rfq/request/accept"
RFQ_QUOTE_APPROVE = "/rfq/quote/approve"
RFQ_CONFIG = "/rfq/config"


================================================
FILE: py_clob_client/exceptions.py
================================================
from typing import Optional

import httpx


class PolyException(Exception):
    def __init__(self, msg):
        self.msg = msg


class PolyApiException(PolyException):
    def __init__(self, resp: Optional[httpx.Response] = None, error_msg=None):
        assert resp is not None or error_msg is not None

        if resp is not None:
            self.status_code = resp.status_code
            self.error_msg = self._get_message(resp)
        else:
            self.status_code = None
            self.error_msg = error_msg

    def _get_message(self, resp: httpx.Response):
        try:
            return resp.json()
        except Exception:
            return resp.text

    def __repr__(self):
        return f"PolyApiException[status_code={self.status_code}, error_message={self.error_msg}]"

    def __str__(self):
        return self.__repr__()


================================================
FILE: py_clob_client/headers/__init__.py
================================================


================================================
FILE: py_clob_client/headers/headers.py
================================================
from ..clob_types import ApiCreds, RequestArgs
from ..signing.hmac import build_hmac_signature
from ..signer import Signer
from ..signing.eip712 import sign_clob_auth_message

from datetime import datetime

POLY_ADDRESS = "POLY_ADDRESS"
POLY_SIGNATURE = "POLY_SIGNATURE"
POLY_TIMESTAMP = "POLY_TIMESTAMP"
POLY_NONCE = "POLY_NONCE"
POLY_API_KEY = "POLY_API_KEY"
POLY_PASSPHRASE = "POLY_PASSPHRASE"


def create_level_1_headers(signer: Signer, nonce: int = None):
    """
    Creates Level 1 Poly headers for a request
    """
    timestamp = int(datetime.now().timestamp())

    n = 0
    if nonce is not None:
        n = nonce

    signature = sign_clob_auth_message(signer, timestamp, n)
    headers = {
        POLY_ADDRESS: signer.address(),
        POLY_SIGNATURE: signature,
        POLY_TIMESTAMP: str(timestamp),
        POLY_NONCE: str(n),
    }

    return headers


def create_level_2_headers(signer: Signer, creds: ApiCreds, request_args: RequestArgs):
    """Creates Level 2 Poly headers for a request using pre-serialized body if provided"""
    timestamp = int(datetime.now().timestamp())

    # Prefer the pre-serialized body string for deterministic signing if available
    body_for_sig = (
        request_args.serialized_body
        if request_args.serialized_body is not None
        else request_args.body
    )

    hmac_sig = build_hmac_signature(
        creds.api_secret,
        timestamp,
        request_args.method,
        request_args.request_path,
        body_for_sig,
    )

    return {
        POLY_ADDRESS: signer.address(),
        POLY_SIGNATURE: hmac_sig,
        POLY_TIMESTAMP: str(timestamp),
        POLY_API_KEY: creds.api_key,
        POLY_PASSPHRASE: creds.api_passphrase,
    }


def enrich_l2_headers_with_builder_headers(
    headers: dict, builder_headers: dict
) -> dict:
    return {**headers, **builder_headers}


================================================
FILE: py_clob_client/http_helpers/__init__.py
================================================


================================================
FILE: py_clob_client/http_helpers/helpers.py
================================================
import httpx

from py_clob_client.clob_types import (
    DropNotificationParams,
    BalanceAllowanceParams,
    OrderScoringParams,
    OrdersScoringParams,
    TradeParams,
    OpenOrderParams,
)

from ..exceptions import PolyApiException

GET = "GET"
POST = "POST"
DELETE = "DELETE"
PUT = "PUT"

_http_client = httpx.Client(http2=True)


def overloadHeaders(method: str, headers: dict) -> dict:
    if headers is None:
        headers = dict()
    headers["User-Agent"] = "py_clob_client"

    headers["Accept"] = "*/*"
    headers["Connection"] = "keep-alive"
    headers["Content-Type"] = "application/json"

    if method == GET:
        headers["Accept-Encoding"] = "gzip"

    return headers


def request(endpoint: str, method: str, headers=None, data=None):
    try:
        headers = overloadHeaders(method, headers)
        if isinstance(data, str):
            # Pre-serialized body: send exact bytes
            resp = _http_client.request(
                method=method,
                url=endpoint,
                headers=headers,
                content=data.encode("utf-8"),
            )
        else:
            resp = _http_client.request(
                method=method,
                url=endpoint,
                headers=headers,
                json=data,
            )

        if resp.status_code != 200:
            raise PolyApiException(resp)

        try:
            return resp.json()
        except ValueError:
            return resp.text

    except httpx.RequestError:
        raise PolyApiException(error_msg="Request exception!")


def post(endpoint, headers=None, data=None):
    return request(endpoint, POST, headers, data)


def get(endpoint, headers=None, data=None):
    return request(endpoint, GET, headers, data)


def delete(endpoint, headers=None, data=None):
    return request(endpoint, DELETE, headers, data)


def put(endpoint, headers=None, data=None):
    return request(endpoint, PUT, headers, data)


def build_query_params(url: str, param: str, val: str) -> str:
    url_with_params = url
    last = url_with_params[-1]
    # if last character in url string == "?", append the param directly: api.com?param=value
    if last == "?":
        url_with_params = "{}{}={}".format(url_with_params, param, val)
    else:
        # else add "&", then append the param
        url_with_params = "{}&{}={}".format(url_with_params, param, val)
    return url_with_params


def add_query_trade_params(
    base_url: str, params: TradeParams = None, next_cursor="MA=="
) -> str:
    """
    Adds query parameters to a url
    """
    url = base_url
    # Include `next_cursor` even when `params` is None to advance pagination.
    has_query = bool(next_cursor) or (
        bool(params)
        and any(
            [
                params.market,
                params.asset_id,
                params.after,
                params.before,
                params.maker_address,
                params.id,
            ]
        )
    )
    if has_query:
        url = url + "?"
    if params:
        if params.market:
            url = build_query_params(url, "market", params.market)
        if params.asset_id:
            url = build_query_params(url, "asset_id", params.asset_id)
        if params.after:
            url = build_query_params(url, "after", params.after)
        if params.before:
            url = build_query_params(url, "before", params.before)
        if params.maker_address:
            url = build_query_params(url, "maker_address", params.maker_address)
        if params.id:
            url = build_query_params(url, "id", params.id)
    if next_cursor:
        url = build_query_params(url, "next_cursor", next_cursor)
    return url


def add_query_open_orders_params(
    base_url: str, params: OpenOrderParams = None, next_cursor="MA=="
) -> str:
    """
    Adds query parameters to a url
    """
    url = base_url
    # Include `next_cursor` even when `params` is None to advance pagination.
    has_query = bool(next_cursor) or (
        bool(params) and any([params.market, params.asset_id, params.id])
    )
    if has_query:
        url = url + "?"
    if params:
        if params.market:
            url = build_query_params(url, "market", params.market)
        if params.asset_id:
            url = build_query_params(url, "asset_id", params.asset_id)
        if params.id:
            url = build_query_params(url, "id", params.id)
    if next_cursor:
        url = build_query_params(url, "next_cursor", next_cursor)
    return url


def drop_notifications_query_params(
    base_url: str, params: DropNotificationParams = None
) -> str:
    """
    Adds query parameters to a url
    """
    url = base_url
    if params:
        url = url + "?"
        if params.ids:
            url = build_query_params(url, "ids", ",".join(params.ids))
    return url


def add_balance_allowance_params_to_url(
    base_url: str, params: BalanceAllowanceParams = None
) -> str:
    """
    Adds query parameters to a url
    """
    url = base_url
    if params:
        url = url + "?"
        if params.asset_type:
            url = build_query_params(url, "asset_type", params.asset_type.__str__())
        if params.token_id:
            url = build_query_params(url, "token_id", params.token_id)
        if params.signature_type is not None:
            url = build_query_params(url, "signature_type", params.signature_type)
    return url


def add_order_scoring_params_to_url(
    base_url: str, params: OrderScoringParams = None
) -> str:
    """
    Adds query parameters to a url
    """
    url = base_url
    if params:
        url = url + "?"
        if params.orderId:
            url = build_query_params(url, "order_id", params.orderId)
    return url


def add_orders_scoring_params_to_url(
    base_url: str, params: OrdersScoringParams = None
) -> str:
    """
    Adds query parameters to a url
    """
    url = base_url
    if params:
        url = url + "?"
        if params.orderIds:
            url = build_query_params(url, "order_ids", ",".join(params.orderIds))
    return url


================================================
FILE: py_clob_client/order_builder/__init__.py
================================================


================================================
FILE: py_clob_client/order_builder/builder.py
================================================
from py_order_utils.builders import OrderBuilder as UtilsOrderBuilder
from py_order_utils.signer import Signer as UtilsSigner
from py_order_utils.model import (
    EOA,
    OrderData,
    SignedOrder,
    BUY as UtilsBuy,
    SELL as UtilsSell,
)

from .helpers import (
    to_token_decimals,
    round_down,
    round_normal,
    decimal_places,
    round_up,
)
from .constants import BUY, SELL
from ..config import get_contract_config
from ..signer import Signer
from ..clob_types import (
    OrderArgs,
    CreateOrderOptions,
    TickSize,
    RoundConfig,
    MarketOrderArgs,
    OrderSummary,
    OrderType,
)

ROUNDING_CONFIG: dict[TickSize, RoundConfig] = {
    "0.1": RoundConfig(price=1, size=2, amount=3),
    "0.01": RoundConfig(price=2, size=2, amount=4),
    "0.001": RoundConfig(price=3, size=2, amount=5),
    "0.0001": RoundConfig(price=4, size=2, amount=6),
}


class OrderBuilder:
    def __init__(self, signer: Signer, sig_type=None, funder=None):
        self.signer = signer

        # Signature type used sign orders, defaults to EOA type
        self.sig_type = sig_type if sig_type is not None else EOA

        # Address which holds funds to be used.
        # Used for Polymarket proxy wallets and other smart contract wallets
        # Defaults to the address of the signer
        self.funder = funder if funder is not None else self.signer.address()

    def get_order_amounts(
        self, side: str, size: float, price: float, round_config: RoundConfig
    ):
        raw_price = round_normal(price, round_config.price)

        if side == BUY:
            raw_taker_amt = round_down(size, round_config.size)

            raw_maker_amt = raw_taker_amt * raw_price
            if decimal_places(raw_maker_amt) > round_config.amount:
                raw_maker_amt = round_up(raw_maker_amt, round_config.amount + 4)
                if decimal_places(raw_maker_amt) > round_config.amount:
                    raw_maker_amt = round_down(raw_maker_amt, round_config.amount)

            maker_amount = to_token_decimals(raw_maker_amt)
            taker_amount = to_token_decimals(raw_taker_amt)

            return UtilsBuy, maker_amount, taker_amount
        elif side == SELL:
            raw_maker_amt = round_down(size, round_config.size)

            raw_taker_amt = raw_maker_amt * raw_price
            if decimal_places(raw_taker_amt) > round_config.amount:
                raw_taker_amt = round_up(raw_taker_amt, round_config.amount + 4)
                if decimal_places(raw_taker_amt) > round_config.amount:
                    raw_taker_amt = round_down(raw_taker_amt, round_config.amount)

            maker_amount = to_token_decimals(raw_maker_amt)
            taker_amount = to_token_decimals(raw_taker_amt)

            return UtilsSell, maker_amount, taker_amount
        else:
            raise ValueError(f"order_args.side must be '{BUY}' or '{SELL}'")

    def get_market_order_amounts(
        self, side: str, amount: float, price: float, round_config: RoundConfig
    ):
        raw_price = round_normal(price, round_config.price)

        if side == BUY:
            raw_maker_amt = round_down(amount, round_config.size)
            raw_taker_amt = raw_maker_amt / raw_price
            if decimal_places(raw_taker_amt) > round_config.amount:
                raw_taker_amt = round_up(raw_taker_amt, round_config.amount + 4)
                if decimal_places(raw_taker_amt) > round_config.amount:
                    raw_taker_amt = round_down(raw_taker_amt, round_config.amount)

            maker_amount = to_token_decimals(raw_maker_amt)
            taker_amount = to_token_decimals(raw_taker_amt)

            return UtilsBuy, maker_amount, taker_amount

        elif side == SELL:
            raw_maker_amt = round_down(amount, round_config.size)

            raw_taker_amt = raw_maker_amt * raw_price
            if decimal_places(raw_taker_amt) > round_config.amount:
                raw_taker_amt = round_up(raw_taker_amt, round_config.amount + 4)
                if decimal_places(raw_taker_amt) > round_config.amount:
                    raw_taker_amt = round_down(raw_taker_amt, round_config.amount)

            maker_amount = to_token_decimals(raw_maker_amt)
            taker_amount = to_token_decimals(raw_taker_amt)

            return UtilsSell, maker_amount, taker_amount
        else:
            raise ValueError(f"order_args.side must be '{BUY}' or '{SELL}'")

    def create_order(
        self, order_args: OrderArgs, options: CreateOrderOptions
    ) -> SignedOrder:
        """
        Creates and signs an order
        """
        side, maker_amount, taker_amount = self.get_order_amounts(
            order_args.side,
            order_args.size,
            order_args.price,
            ROUNDING_CONFIG[options.tick_size],
        )

        data = OrderData(
            maker=self.funder,
            taker=order_args.taker,
            tokenId=order_args.token_id,
            makerAmount=str(maker_amount),
            takerAmount=str(taker_amount),
            side=side,
            feeRateBps=str(order_args.fee_rate_bps),
            nonce=str(order_args.nonce),
            signer=self.signer.address(),
            expiration=str(order_args.expiration),
            signatureType=self.sig_type,
        )

        contract_config = get_contract_config(
            self.signer.get_chain_id(), options.neg_risk
        )

        order_builder = UtilsOrderBuilder(
            contract_config.exchange,
            self.signer.get_chain_id(),
            UtilsSigner(key=self.signer.private_key),
        )

        return order_builder.build_signed_order(data)

    def create_market_order(
        self, order_args: MarketOrderArgs, options: CreateOrderOptions
    ) -> SignedOrder:
        """
        Creates and signs a market order
        """
        side, maker_amount, taker_amount = self.get_market_order_amounts(
            order_args.side,
            order_args.amount,
            order_args.price,
            ROUNDING_CONFIG[options.tick_size],
        )

        data = OrderData(
            maker=self.funder,
            taker=order_args.taker,
            tokenId=order_args.token_id,
            makerAmount=str(maker_amount),
            takerAmount=str(taker_amount),
            side=side,
            feeRateBps=str(order_args.fee_rate_bps),
            nonce=str(order_args.nonce),
            signer=self.signer.address(),
            expiration="0",
            signatureType=self.sig_type,
        )

        contract_config = get_contract_config(
            self.signer.get_chain_id(), options.neg_risk
        )

        order_builder = UtilsOrderBuilder(
            contract_config.exchange,
            self.signer.get_chain_id(),
            UtilsSigner(key=self.signer.private_key),
        )

        return order_builder.build_signed_order(data)

    def calculate_buy_market_price(
        self,
        positions: list[OrderSummary],
        amount_to_match: float,
        order_type: OrderType,
    ) -> float:
        if not positions:
            raise Exception("no match")

        sum = 0
        for p in reversed(positions):
            sum += float(p.size) * float(p.price)
            if sum >= amount_to_match:
                return float(p.price)

        if order_type == OrderType.FOK:
            raise Exception("no match")

        return float(positions[0].price)

    def calculate_sell_market_price(
        self,
        positions: list[OrderSummary],
        amount_to_match: float,
        order_type: OrderType,
    ) -> float:
        if not positions:
            raise Exception("no match")

        sum = 0
        for p in reversed(positions):
            sum += float(p.size)
            if sum >= amount_to_match:
                return float(p.price)

        if order_type == OrderType.FOK:
            raise Exception("no match")

        return float(positions[0].price)


================================================
FILE: py_clob_client/order_builder/constants.py
================================================
BUY = "BUY"
SELL = "SELL"


================================================
FILE: py_clob_client/order_builder/helpers.py
================================================
from math import floor, ceil
from decimal import Decimal


def round_down(x: float, sig_digits: int) -> float:
    return floor(x * (10**sig_digits)) / (10**sig_digits)


def round_normal(x: float, sig_digits: int) -> float:
    return round(x * (10**sig_digits)) / (10**sig_digits)


def round_up(x: float, sig_digits: int) -> float:
    return ceil(x * (10**sig_digits)) / (10**sig_digits)


def to_token_decimals(x: float) -> int:
    f = (10**6) * x
    if decimal_places(f) > 0:
        f = round_normal(f, 0)
    return int(f)


def decimal_places(x: float) -> int:
    return abs(Decimal(x.__str__()).as_tuple().exponent)


================================================
FILE: py_clob_client/rfq/__init__.py
================================================
from .rfq_types import (
    # Input types
    RfqUserRequest,
    RfqUserQuote,
    CreateRfqRequestParams,
    CreateRfqQuoteParams,
    CancelRfqRequestParams,
    CancelRfqQuoteParams,
    AcceptQuoteParams,
    ApproveOrderParams,
    GetRfqRequestsParams,
    GetRfqQuotesParams,
    GetRfqBestQuoteParams,
    # Response types
    RfqRequest,
    RfqQuote,
    RfqRequestResponse,
    RfqQuoteResponse,
    RfqPaginatedResponse,
)

from .rfq_helpers import (
    parse_units,
    to_camel_case,
    parse_rfq_requests_params,
    parse_rfq_quotes_params,
    COLLATERAL_TOKEN_DECIMALS,
    CONDITIONAL_TOKEN_DECIMALS,
)

from .rfq_client import RfqClient

__all__ = [
    # Client
    "RfqClient",
    # Input types
    "RfqUserRequest",
    "RfqUserQuote",
    "CreateRfqRequestParams",
    "CreateRfqQuoteParams",
    "CancelRfqRequestParams",
    "CancelRfqQuoteParams",
    "AcceptQuoteParams",
    "ApproveOrderParams",
    "GetRfqRequestsParams",
    "GetRfqQuotesParams",
    "GetRfqBestQuoteParams",
    # Response types
    "RfqRequest",
    "RfqQuote",
    "RfqRequestResponse",
    "RfqQuoteResponse",
    "RfqPaginatedResponse",
    # Helpers
    "parse_units",
    "to_camel_case",
    "parse_rfq_requests_params",
    "parse_rfq_quotes_params",
    "COLLATERAL_TOKEN_DECIMALS",
    "CONDITIONAL_TOKEN_DECIMALS",
]


================================================
FILE: py_clob_client/rfq/rfq_client.py
================================================
"""
RFQ (Request for Quote) client for the Polymarket CLOB API.

This module provides the RfqClient class which handles all RFQ operations
including creating requests, quotes, and executing trades.
"""

import logging
import json
from urllib.parse import urlencode
from typing import Optional, Any, TYPE_CHECKING

from ..clob_types import RequestArgs, OrderArgs, PartialCreateOrderOptions
from ..headers.headers import create_level_2_headers
from ..http_helpers.helpers import get, post, delete
from ..order_builder.builder import ROUNDING_CONFIG
from ..order_builder.helpers import round_normal, round_down
from ..order_builder.constants import BUY, SELL
from ..endpoints import (
    CREATE_RFQ_REQUEST,
    CANCEL_RFQ_REQUEST,
    GET_RFQ_REQUESTS,
    CREATE_RFQ_QUOTE,
    CANCEL_RFQ_QUOTE,
    GET_RFQ_REQUESTER_QUOTES,
    GET_RFQ_QUOTER_QUOTES,
    GET_RFQ_BEST_QUOTE,
    RFQ_REQUESTS_ACCEPT,
    RFQ_QUOTE_APPROVE,
    RFQ_CONFIG,
)

from .rfq_types import (
    RfqUserRequest,
    RfqUserQuote,

    CancelRfqRequestParams,
    CancelRfqQuoteParams,
    AcceptQuoteParams,
    ApproveOrderParams,
    GetRfqRequestsParams,
    GetRfqQuotesParams,
    GetRfqBestQuoteParams,
    MatchType,
)
from .rfq_helpers import (
    parse_units,
    parse_rfq_requests_params,
    parse_rfq_quotes_params,
    COLLATERAL_TOKEN_DECIMALS,
)

if TYPE_CHECKING:
    from ..client import ClobClient


class RfqClient:
    """
    RFQ client for creating and managing RFQ requests and quotes.

    This client is typically accessed via the parent ClobClient's `rfq` attribute:

        client = ClobClient(host, chain_id, key, creds)
        response = client.rfq.create_rfq_request(user_request)
    """

    def __init__(self, parent: "ClobClient"):
        """
        Initialize the RFQ client.

        Args:
            parent: The parent ClobClient instance providing auth and config.
        """
        self._parent = parent
        self.logger = logging.getLogger(self.__class__.__name__)

    def _ensure_l2_auth(self) -> None:
        """
        Verify that L2 authentication is available.

        Raises:
            PolyException: If signer or creds are not configured.
        """
        self._parent.assert_level_2_auth()

    def _get_l2_headers(self, method: str, endpoint: str, body: Any = None, serialized_body: Any = None) -> dict:
        """
        Create L2 authentication headers for a request.

        Args:
            method: HTTP method (GET, POST, PUT, DELETE)
            endpoint: API endpoint path
            body: Optional request body

        Returns:
            Dictionary of authentication headers.
        """
        request_args = RequestArgs(method=method, request_path=endpoint, body=body)
        if serialized_body is not None:
            request_args.serialized_body = serialized_body

        return create_level_2_headers(
            self._parent.signer,
            self._parent.creds,
            request_args,
        )

    def _build_url(self, endpoint: str) -> str:
        """Build full URL from endpoint."""
        return f"{self._parent.host}{endpoint}"

    # =========================================================================
    # Request-side methods
    # =========================================================================

    def create_rfq_request(
        self,
        user_request: RfqUserRequest,
        options: Optional[PartialCreateOrderOptions] = None,
    ) -> dict:
        """
        Create and post an RFQ request from a user request.

        This method:
        1. Resolves the tick size for the token
        2. Rounds price and size according to tick size rules
        3. Calculates amount_in and amount_out based on side
        4. Posts the request to the server

        Args:
            user_order: Simplified order with token_id, price, side, size
            options: Optional tick size override

        Returns:
            Response dict with request_id on success.

        Example:
            >>> response = client.rfq.create_rfq_request(
            ...     RfqUserRequest(
            ...         token_id="123...",
            ...         price=0.5,
            ...         side="BUY",
            ...         size=40,
            ...     )
            ... )
        """
        token_id = user_request.token_id
        price = user_request.price
        side = user_request.side
        size = user_request.size

        # Resolve tick size (from options or fetch from server)
        tick_size = self._parent._ClobClient__resolve_tick_size(
            token_id,
            options.tick_size if options else None,
        )

        # Get rounding configuration (ensure tick_size is a string for lookup)
        tick_size_str = str(tick_size) if not isinstance(tick_size, str) else tick_size
        round_config = ROUNDING_CONFIG[tick_size_str]

        # Round price and size
        rounded_price = round_normal(price, round_config.price)
        rounded_size = round_down(size, round_config.size)

        # Format with correct decimal places
        price_decimals = int(round_config.price)
        size_decimals = int(round_config.size)
        amount_decimals = int(round_config.amount)

        rounded_price_str = f"{rounded_price:.{price_decimals}f}"
        rounded_size_str = f"{rounded_size:.{size_decimals}f}"

        # Parse back to numbers for calculation
        size_num = float(rounded_size_str)
        price_num = float(rounded_price_str)

        # Get signature type from parent's order builder
        user_type = self._parent.builder.sig_type

        # Calculate amounts based on side
        if side == BUY:
            # Buying tokens: pay USDC, receive tokens
            # asset_in = tokens (what requester receives)
            # asset_out = USDC (what requester pays)
            amount_in = parse_units(rounded_size_str, COLLATERAL_TOKEN_DECIMALS)

            usdc_amount = size_num * price_num
            usdc_amount_str = f"{usdc_amount:.{amount_decimals}f}"
            amount_out = parse_units(usdc_amount_str, COLLATERAL_TOKEN_DECIMALS)

            asset_in = token_id
            asset_out = "0"  # USDC
        else:
            # Selling tokens: pay tokens, receive USDC
            # asset_in = USDC (what requester receives)
            # asset_out = tokens (what requester pays)
            usdc_amount = size_num * price_num
            usdc_amount_str = f"{usdc_amount:.{amount_decimals}f}"
            amount_in = parse_units(usdc_amount_str, COLLATERAL_TOKEN_DECIMALS)

            amount_out = parse_units(rounded_size_str, COLLATERAL_TOKEN_DECIMALS)

            asset_in = "0"  # USDC
            asset_out = token_id

        # Post directly to the server
        self._ensure_l2_auth()

        body = {
            "assetIn": asset_in,
            "assetOut": asset_out,
            "amountIn": str(amount_in),
            "amountOut": str(amount_out),
            "userType": user_type,
        }
        serialized_body = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        headers = self._get_l2_headers("POST", CREATE_RFQ_REQUEST, body, serialized_body)
        return post(self._build_url(CREATE_RFQ_REQUEST), headers=headers, data=serialized_body)

    def cancel_rfq_request(self, params: CancelRfqRequestParams) -> str:
        """
        Cancel an RFQ request.

        Args:
            params: Contains request_id to cancel.

        Returns:
            "OK" on success.
        """
        self._ensure_l2_auth()

        body = {"requestId": params.request_id}
        serialized_body = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        headers = self._get_l2_headers("DELETE", CANCEL_RFQ_REQUEST, body, serialized_body)
        return delete(self._build_url(CANCEL_RFQ_REQUEST), headers=headers, data=serialized_body)

    def get_rfq_requests(
        self, params: Optional[GetRfqRequestsParams] = None
    ) -> dict:
        """
        Get RFQ requests with optional filtering.

        Args:
            params: Optional filter parameters.

        Returns:
            Paginated response with RFQ requests.
        """
        self._ensure_l2_auth()

        headers = self._get_l2_headers("GET", GET_RFQ_REQUESTS)
        query_params = parse_rfq_requests_params(params)

        # Build URL with query params
        url = self._build_url(GET_RFQ_REQUESTS)
        if query_params:
            # Use doseq=True so list values become repeated query params:
            # requestIds=id1&requestIds=id2
            url = f"{url}?{urlencode(query_params, doseq=True)}"

        return get(url, headers=headers)

    # =========================================================================
    # Quote-side methods
    # =========================================================================

    def create_rfq_quote(
        self,
        user_quote: RfqUserQuote,
        options: Optional[PartialCreateOrderOptions] = None,
    ) -> dict:
        """
        Create and post an RFQ quote in response to an RFQ request.

        This method:
        1. Fetches the RFQ request to get token_id
        2. Resolves the tick size for the token
        3. Rounds price and size according to tick size rules
        4. Calculates amount_in and amount_out based on side
        5. Posts the quote to the server

        Args:
            user_quote: Simplified quote with request_id, token_id, price, side, size
            options: Optional tick size override

        Returns:
            Response dict with quote_id on success.

        Example:
            >>> response = client.rfq.create_rfq_quote(
            ...     RfqUserQuote(
            ...         request_id="019a83a9-f4c7-7c96-9139-2da2b2d934ef",
            ...         token_id="123...",
            ...         price=0.5,
            ...         side="SELL",
            ...         size=100.0,
            ...     )
            ... )
        """

        request_id = user_quote.request_id
        token_id = user_quote.token_id
        price = user_quote.price
        side = user_quote.side
        size = user_quote.size

        # Resolve tick size (from options or fetch from server)
        tick_size = self._parent._ClobClient__resolve_tick_size(
            token_id,
            options.tick_size if options else None,
        )

        # Get rounding configuration (ensure tick_size is a string for lookup)
        tick_size_str = str(tick_size) if not isinstance(tick_size, str) else tick_size
        round_config = ROUNDING_CONFIG[tick_size_str]

        # Round price and size
        rounded_price = round_normal(price, round_config.price)
        rounded_size = round_down(size, round_config.size)

        # Format with correct decimal places
        price_decimals = int(round_config.price)
        size_decimals = int(round_config.size)
        amount_decimals = int(round_config.amount)

        rounded_price_str = f"{rounded_price:.{price_decimals}f}"
        rounded_size_str = f"{rounded_size:.{size_decimals}f}"

        # Parse back to numbers for calculation
        size_num = float(rounded_size_str)
        price_num = float(rounded_price_str)

        # Get signature type from parent's order builder
        user_type = self._parent.builder.sig_type

        # Calculate amounts based on side
        if side == BUY:
            # Buying tokens: pay USDC, receive tokens
            # asset_in = tokens (what quoter receives)
            # asset_out = USDC (what quoter pays)
            amount_in = parse_units(rounded_size_str, COLLATERAL_TOKEN_DECIMALS)

            usdc_amount = size_num * price_num
            usdc_amount_str = f"{usdc_amount:.{amount_decimals}f}"
            amount_out = parse_units(usdc_amount_str, COLLATERAL_TOKEN_DECIMALS)

            asset_in = token_id
            asset_out = "0"  # USDC
        else:
            # Selling tokens: pay tokens, receive USDC
            # asset_in = USDC (what quoter receives)
            # asset_out = tokens (what quoter pays)
            usdc_amount = size_num * price_num
            usdc_amount_str = f"{usdc_amount:.{amount_decimals}f}"
            amount_in = parse_units(usdc_amount_str, COLLATERAL_TOKEN_DECIMALS)

            amount_out = parse_units(rounded_size_str, COLLATERAL_TOKEN_DECIMALS)

            asset_in = "0"  # USDC
            asset_out = token_id

        # Post directly to the server
        self._ensure_l2_auth()

        body = {
            "requestId": request_id,
            "assetIn": asset_in,
            "assetOut": asset_out,
            "amountIn": str(amount_in),
            "amountOut": str(amount_out),
            "userType": user_type,
        }
        serialized_body = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        headers = self._get_l2_headers("POST", CREATE_RFQ_QUOTE, body, serialized_body)
        return post(self._build_url(CREATE_RFQ_QUOTE), headers=headers, data=serialized_body)

    def get_rfq_requester_quotes(self, params: Optional[GetRfqQuotesParams] = None) -> dict:
        """
        Get quotes on requests created by the authenticated user (requester view).

        Returns quotes that others have made on your RFQ requests.

        Args:
            params: Optional filter parameters.

        Returns:
            Paginated response with RFQ quotes.
        """
        self._ensure_l2_auth()

        headers = self._get_l2_headers("GET", GET_RFQ_REQUESTER_QUOTES)
        query_params = parse_rfq_quotes_params(params)

        # Build URL with query params
        url = self._build_url(GET_RFQ_REQUESTER_QUOTES)
        if query_params:
            url = f"{url}?{urlencode(query_params, doseq=True)}"

        return get(url, headers=headers)

    def get_rfq_quoter_quotes(self, params: Optional[GetRfqQuotesParams] = None) -> dict:
        """
        Get quotes created by the authenticated user (quoter view).

        Returns quotes that you have made on others' RFQ requests.

        Args:
            params: Optional filter parameters.

        Returns:
            Paginated response with RFQ quotes.
        """
        self._ensure_l2_auth()

        headers = self._get_l2_headers("GET", GET_RFQ_QUOTER_QUOTES)
        query_params = parse_rfq_quotes_params(params)

        # Build URL with query params
        url = self._build_url(GET_RFQ_QUOTER_QUOTES)
        if query_params:
            url = f"{url}?{urlencode(query_params, doseq=True)}"

        return get(url, headers=headers)

    def get_rfq_best_quote(
        self, params: Optional[GetRfqBestQuoteParams] = None
    ) -> dict:
        """
        Get the best quote for an RFQ request.

        Args:
            params: Contains request_id.

        Returns:
            Single quote object representing the best quote.
        """
        self._ensure_l2_auth()

        headers = self._get_l2_headers("GET", GET_RFQ_BEST_QUOTE)

        url = self._build_url(GET_RFQ_BEST_QUOTE)
        if params and params.request_id:
            url = f"{url}?{urlencode({'requestId': params.request_id})}"

        return get(url, headers=headers)

    def cancel_rfq_quote(self, params: CancelRfqQuoteParams) -> str:
        """
        Cancel an RFQ quote.

        Args:
            params: Contains quote_id to cancel.

        Returns:
            "OK" on success.
        """
        self._ensure_l2_auth()

        body = {"quoteId": params.quote_id}
        serialized_body = json.dumps(body, separators=(",", ":"), ensure_ascii=False)
        headers = self._get_l2_headers("DELETE", CANCEL_RFQ_QUOTE, body, serialized_body)
        return delete(self._build_url(CANCEL_RFQ_QUOTE), headers=headers, data=serialized_body)

    # =========================================================================
    # Trade execution methods
    # =========================================================================

    def accept_rfq_quote(self, params: AcceptQuoteParams) -> str:
        """
        Accept an RFQ quote (requester side).

        This method:
        1. Fetches the RFQ quote details
        2. Creates a signed order matching the quote
        3. Submits the acceptance with the order

        Args:
            params: Contains request_id, quote_id, and expiration.

        Returns:
            "OK" on success.
        """
        self._ensure_l2_auth()

        resp = self.get_rfq_requester_quotes(
            GetRfqQuotesParams(quote_ids=[params.quote_id])
        )

        if not resp.get("data") or len(resp["data"]) == 0:
            raise Exception("RFQ quote not found")

        rfq_quote = resp["data"][0]
        order_creation_payload = self._get_request_order_creation_payload(rfq_quote)
        price = order_creation_payload.get("price")
        side = order_creation_payload["side"]
        size = float(order_creation_payload["size"])
        token = order_creation_payload["token"]

        order_args = OrderArgs(
            token_id=token,
            price=price,
            size=size,
            side=side,
            expiration=params.expiration,
        )

        order = self._parent.create_order(order_args)

        if not order:
            raise Exception("Error creating order")

        order_dict = order.dict()

        accept_payload = {
            "requestId": params.request_id,
            "quoteId": params.quote_id,
            "owner": self._parent.creds.api_key,
            # Order fields from dict
            "salt": int(order_dict["salt"]),
            "maker": order_dict["maker"],
            "signer": order_dict["signer"],
            "taker": order_dict["taker"],
            "tokenId": order_dict["tokenId"],
            "makerAmount": order_dict["makerAmount"],
            "takerAmount": order_dict["takerAmount"],
            "expiration": int(order_dict["expiration"]),
            "nonce": order_dict["nonce"],
            "feeRateBps": order_dict["feeRateBps"],
            "side": side,
            "signatureType": int(order_dict["signatureType"]),
            "signature": order_dict["signature"],
        }

        self.logger.debug(
            "Accept payload: requestId=%s, quoteId=%s, tokenId=%s, side=%s",
            accept_payload.get("requestId"),
            accept_payload.get("quoteId"),
            accept_payload.get("tokenId"),
            accept_payload.get("side"),
        )
        serialized_body = json.dumps(accept_payload, separators=(",", ":"), ensure_ascii=False)
        headers = self._get_l2_headers("POST", RFQ_REQUESTS_ACCEPT, accept_payload, serialized_body)
        return post(
            self._build_url(RFQ_REQUESTS_ACCEPT),
            headers=headers,
            data=serialized_body,
        )

    def approve_rfq_order(self, params: ApproveOrderParams) -> str:
        """
        Approve an RFQ order (quoter side).

        This method:
        1. Fetches the RFQ quote details
        2. Creates a signed order based on quote parameters
        3. Submits the approval with the order

        Args:
            params: Contains request_id, quote_id, and expiration.

        Returns:
            "OK" on success.
        """
        self._ensure_l2_auth()

        # Step 1: Fetch the RFQ quote
        rfq_quotes = self.get_rfq_quoter_quotes(
            GetRfqQuotesParams(quote_ids=[params.quote_id])
        )

        if not rfq_quotes.get("data") or len(rfq_quotes["data"]) == 0:
            raise Exception("RFQ quote not found")

        rfq_quote = rfq_quotes["data"][0]

        # Step 2: Create an order based on quote details
        # Quoter uses their own quote's side
        side = rfq_quote.get("side", BUY)

        # Determine size based on quote side
        if side == BUY:
            size = rfq_quote.get("sizeIn")
        else:
            size = rfq_quote.get("sizeOut")

        token_id = rfq_quote.get("token")
        price = rfq_quote.get("price")

        order_args = OrderArgs(
            token_id=token_id,
            price=float(price),
            size=float(size),
            side=side,
            expiration=params.expiration,
        )

        order = self._parent.create_order(order_args)

        if not order:
            raise Exception("Error creating order")

        # Step 3: Build approve payload
        order_dict = order.dict()

        approve_payload = {
            "requestId": params.request_id,
            "quoteId": params.quote_id,
            "owner": self._parent.creds.api_key,
            # Order fields from dict
            "salt": int(order_dict["salt"]),
            "maker": order_dict["maker"],
            "signer": order_dict["signer"],
            "taker": order_dict["taker"],
            "tokenId": order_dict["tokenId"],
            "makerAmount": order_dict["makerAmount"],
            "takerAmount": order_dict["takerAmount"],
            "expiration": int(order_dict["expiration"]),
            "nonce": order_dict["nonce"],
            "feeRateBps": order_dict["feeRateBps"],
            "side": side,
            "signatureType": int(order_dict["signatureType"]),
            "signature": order_dict["signature"],
        }
        serialized_body = json.dumps(approve_payload, separators=(",", ":"), ensure_ascii=False)
        headers = self._get_l2_headers("POST", RFQ_QUOTE_APPROVE, approve_payload, serialized_body)
        return post(
            self._build_url(RFQ_QUOTE_APPROVE),
            headers=headers,
            data=serialized_body,
        )

    # =========================================================================
    # Configuration
    # =========================================================================

    def rfq_config(self) -> dict:
        """
        Get RFQ configuration from the server.

        Returns:
            Configuration object with RFQ system parameters.
        """
        self._ensure_l2_auth()

        headers = self._get_l2_headers("GET", RFQ_CONFIG)
        return get(self._build_url(RFQ_CONFIG), headers=headers)

    def _get_request_order_creation_payload(self, quote: dict) -> dict:
        """
        Build the order creation payload for an RFQ request based on quote details.
        """
        raw_match_type = quote.get("matchType", MatchType.COMPLEMENTARY)
        match_type = (
            raw_match_type
            if isinstance(raw_match_type, MatchType)
            else MatchType(str(raw_match_type))
        )

        side = quote.get("side", BUY)

        if match_type == MatchType.COMPLEMENTARY:
            # For BUY <> SELL and SELL <> BUY
            # the order side is opposite the quote side
            token = quote.get("token")
            if not token:
                raise Exception("missing token for COMPLEMENTARY match")
            side = SELL if side == BUY else BUY
            size = quote.get("sizeOut") if side == BUY else quote.get("sizeIn")
            if size is None:
                raise Exception("missing sizeIn/sizeOut for COMPLEMENTARY match")
            price = quote.get("price")
            if price is None:
                raise Exception("missing price for COMPLEMENTARY match")
            price = float(price)
            return {
                "token": token,
                "side": side,
                "size": size,
                "price": price,
            }
        elif match_type in (MatchType.MINT, MatchType.MERGE):
            # BUY<> BUY, SELL <> SELL
            # the order side is the same as the quote side
            token = quote.get("complement")
            if not token:
                raise Exception("missing complement token for MINT/MERGE match")
            size = quote.get("sizeIn") if side == BUY else quote.get("sizeOut")
            if size is None:
                raise Exception("missing sizeIn/sizeOut for MINT/MERGE match")
            price = quote.get("price")
            if price is None:
                raise Exception("missing price for MINT/MERGE match")
            price = float(price)
            # For a MINT or a MERGE, the requester price is the inverse of the quote price
            # 95c Quote to BUY NO, implies that the Requester is buying YES at 5c
            # 45c Quote to SELL NO, implies the Requester is selling YES at 55c
            price = 1 - price
            return {
                "token": token,
                "side": side,
                "size": size,
                "price": price,
            }
        else:
            raise Exception(f"invalid match type: {raw_match_type}")



================================================
FILE: py_clob_client/rfq/rfq_helpers.py
================================================
"""
RFQ helper functions for the Polymarket CLOB API.

This module provides utility functions for RFQ operations including
query parameter parsing and unit conversion.
"""

from typing import Optional, Dict, Any

from .rfq_types import GetRfqRequestsParams, GetRfqQuotesParams


# Token decimals constants
COLLATERAL_TOKEN_DECIMALS = 6  # USDC has 6 decimals
CONDITIONAL_TOKEN_DECIMALS = 6


def parse_units(value: str, decimals: int) -> int:
    """
    Convert a decimal string to smallest units (like wei for ETH).

    Args:
        value: Decimal string (e.g., "1.5")
        decimals: Number of decimal places (e.g., 6 for USDC)

    Returns:
        Integer in smallest units (e.g., 1500000 for "1.5" with 6 decimals)

    Examples:
        >>> parse_units("1.5", 6)
        1500000
        >>> parse_units("100", 6)
        100000000
        >>> parse_units("0.000001", 6)
        1
    """
    if "." in value:
        integer_part, decimal_part = value.split(".")
        # Pad or truncate decimal part to match decimals
        decimal_part = decimal_part[:decimals].ljust(decimals, "0")
        return int(integer_part + decimal_part)
    else:
        return int(value) * (10**decimals)


def to_camel_case(snake_str: str) -> str:
    """
    Convert snake_case string to camelCase.

    Args:
        snake_str: String in snake_case format

    Returns:
        String in camelCase format

    Examples:
        >>> to_camel_case("user_address")
        'userAddress'
        >>> to_camel_case("request_id")
        'requestId'
        >>> to_camel_case("size_usdc_min")
        'sizeUsdcMin'
    """
    components = snake_str.split("_")
    return components[0] + "".join(x.title() for x in components[1:])


def parse_rfq_requests_params(params: Optional[GetRfqRequestsParams] = None) -> Dict[str, Any]:
    """
    Convert GetRfqRequestsParams to query string parameters.

    Arrays are kept as Python lists so the caller can serialize them with
    `urllib.parse.urlencode(..., doseq=True)` (repeated query params).

    Args:
        params: Optional filter parameters

    Returns:
        Dictionary of query parameters ready for HTTP request
    """
    if params is None:
        return {}

    result = {}

    # Single value fields (convert snake_case to camelCase)
    single_fields = [
        ("state", "state"),
        ("size_min", "sizeMin"),
        ("size_max", "sizeMax"),
        ("size_usdc_min", "sizeUsdcMin"),
        ("size_usdc_max", "sizeUsdcMax"),
        ("price_min", "priceMin"),
        ("price_max", "priceMax"),
        ("sort_by", "sortBy"),
        ("sort_dir", "sortDir"),
        ("limit", "limit"),
        ("offset", "offset"),
    ]

    for python_name, api_name in single_fields:
        value = getattr(params, python_name, None)
        if value is not None:
            result[api_name] = value

    # Array fields (keep as lists; let urlencode(doseq=True) expand them)
    if params.request_ids:
        result["requestIds"] = params.request_ids
    if params.markets:
        result["markets"] = params.markets

    return result


def parse_rfq_quotes_params(params: Optional[GetRfqQuotesParams] = None) -> Dict[str, Any]:
    """
    Convert GetRfqQuotesParams to query string parameters.

    Arrays are kept as Python lists so the caller can serialize them with
    `urllib.parse.urlencode(..., doseq=True)` (repeated query params).

    Args:
        params: Optional filter parameters

    Returns:
        Dictionary of query parameters ready for HTTP request
    """
    if params is None:
        return {}

    result = {}

    # Single value fields (convert snake_case to camelCase)
    single_fields = [
        ("state", "state"),
        ("size_min", "sizeMin"),
        ("size_max", "sizeMax"),
        ("size_usdc_min", "sizeUsdcMin"),
        ("size_usdc_max", "sizeUsdcMax"),
        ("price_min", "priceMin"),
        ("price_max", "priceMax"),
        ("sort_by", "sortBy"),
        ("sort_dir", "sortDir"),
        ("limit", "limit"),
        ("offset", "offset"),
    ]

    for python_name, api_name in single_fields:
        value = getattr(params, python_name, None)
        if value is not None:
            result[api_name] = value

    # Array fields (keep as lists; let urlencode(doseq=True) expand them)
    if params.quote_ids:
        result["quoteIds"] = params.quote_ids
    if params.request_ids:
        result["requestIds"] = params.request_ids
    if params.markets:
        result["markets"] = params.markets

    return result


================================================
FILE: py_clob_client/rfq/rfq_types.py
================================================
"""
RFQ (Request for Quote) data types for the Polymarket CLOB API.

This module defines all input and response types used by the RFQ client.
"""

from dataclasses import dataclass, field
from typing import List, Optional, Any, Literal
from enum import Enum


# =============================================================================
# Input Types
# =============================================================================


@dataclass
class RfqUserRequest:
    """
    Simplified user input for creating an RFQ request.

    This is the user-facing order format that gets converted to
    CreateRfqRequestParams for the API.
    """

    token_id: str
    """Token ID of the conditional token being traded."""

    price: float
    """Price per token (0 < price < 1)."""

    side: str
    """Order side: "BUY" or "SELL"."""

    size: float
    """Size in conditional tokens."""


@dataclass
class RfqUserQuote:
    """
    Simplified user input for creating an RFQ quote.

    This is the user-facing quote format that gets converted to
    CreateRfqQuoteParams for the API.
    """

    request_id: str
    """ID of the RFQ request being quoted."""

    token_id: str
    """Token ID of the conditional token being traded."""

    price: float
    """Price per token (0 < price < 1)."""

    side: str
    """Quoter's side: "BUY" or "SELL"."""

    size: float
    """Size in conditional tokens."""


@dataclass
class CreateRfqRequestParams:
    """
    Server payload for creating an RFQ request.

    This is the format sent to the API after converting from RfqUserRequest.
    """

    asset_in: str
    """Asset being received (token ID or "0" for USDC)."""

    asset_out: str
    """Asset being paid (token ID or "0" for USDC)."""

    amount_in: str
    """Amount being received (in smallest units, as string)."""

    amount_out: str
    """Amount being paid (in smallest units, as string)."""

    user_type: int
    """Signature type (0=EOA, 1=POLY_PROXY, 2=POLY_GNOSIS_SAFE)."""


@dataclass
class CreateRfqQuoteParams:
    """
    Parameters for creating a quote in response to an RFQ request.
    """

    request_id: str
    """ID of the RFQ request being quoted."""

    asset_in: str
    """Asset the quoter is paying."""

    asset_out: str
    """Asset the quoter is receiving."""

    amount_in: str
    """Amount quoter is paying (in smallest units)."""

    amount_out: str
    """Amount quoter is receiving (in smallest units)."""

    # Note: user_type is auto-filled by the client


@dataclass
class CancelRfqRequestParams:
    """
    Parameters for canceling an RFQ request.
    """

    request_id: str
    """ID of the request to cancel."""


@dataclass
class CancelRfqQuoteParams:
    """
    Parameters for canceling an RFQ quote.
    """

    quote_id: str
    """ID of the quote to cancel."""


@dataclass
class AcceptQuoteParams:
    """
    Parameters for accepting a quote (requester side).

    When a requester accepts a quote, they create a signed order
    and submit it with this payload.
    """

    request_id: str
    """ID of the RFQ request."""

    quote_id: str
    """ID of the quote being accepted."""

    expiration: int
    """Unix timestamp for order expiration."""


@dataclass
class ApproveOrderParams:
    """
    Parameters for approving an order (quoter side).

    When a quoter's quote is accepted, they approve by creating
    a signed order and submitting it with this payload.
    """

    request_id: str
    """ID of the RFQ request."""

    quote_id: str
    """ID of the quote being approved."""

    expiration: int
    """Unix timestamp for order expiration."""


@dataclass
class GetRfqRequestsParams:
    """
    Query parameters for fetching RFQ requests.

    All fields are optional filters.
    """

    request_ids: Optional[List[str]] = None
    """Filter by specific request IDs (query param: requestIds; repeatable)."""

    state: Optional[Literal["active", "inactive"]] = None
    """Single state filter ("active" or "inactive")."""

    markets: Optional[List[str]] = None
    """Filter by market condition IDs (query param: markets; repeatable)."""

    size_min: Optional[float] = None
    """Minimum size filter."""

    size_max: Optional[float] = None
    """Maximum size filter."""

    size_usdc_min: Optional[float] = None
    """Minimum USDC size filter."""

    size_usdc_max: Optional[float] = None
    """Maximum USDC size filter."""

    price_min: Optional[float] = None
    """Minimum price filter."""

    price_max: Optional[float] = None
    """Maximum price filter."""

    sort_by: Optional[Literal["price", "expiry", "size", "created"]] = None
    """Field to sort by."""

    sort_dir: Optional[Literal["asc", "desc"]] = None
    """Sort direction: "asc" or "desc"."""

    limit: Optional[int] = None
    """Pagination limit."""

    offset: Optional[str] = None
    """Pagination cursor (base64 encoded)."""


@dataclass
class GetRfqQuotesParams:
    """
    Query parameters for fetching RFQ quotes.

    All fields are optional filters.
    """

    quote_ids: Optional[List[str]] = None
    """Filter by specific quote IDs (query param: quoteIds; repeatable)."""

    request_ids: Optional[List[str]] = None
    """Filter by request IDs (query param: requestIds; repeatable)."""

    state: Optional[Literal["active", "inactive"]] = None
    """Single state filter ("active" or "inactive")."""

    markets: Optional[List[str]] = None
    """Filter by market condition IDs (query param: markets; repeatable)."""

    size_min: Optional[float] = None
    """Minimum size filter."""

    size_max: Optional[float] = None
    """Maximum size filter."""

    size_usdc_min: Optional[float] = None
    """Minimum USDC size filter."""

    size_usdc_max: Optional[float] = None
    """Maximum USDC size filter."""

    price_min: Optional[float] = None
    """Minimum price filter."""

    price_max: Optional[float] = None
    """Maximum price filter."""

    sort_by: Optional[Literal["price", "expiry", "created"]] = None
    """Field to sort by."""

    sort_dir: Optional[Literal["asc", "desc"]] = None
    """Sort direction: "asc" or "desc"."""

    limit: Optional[int] = None
    """Pagination limit."""

    offset: Optional[str] = None
    """Pagination cursor (base64 encoded)."""


@dataclass
class GetRfqBestQuoteParams:
    """
    Parameters for fetching the best quote for a request.
    """

    request_id: Optional[str] = None
    """Request ID to get best quote for."""


# =============================================================================
# Response Types
# =============================================================================


@dataclass
class RfqRequest:
    """
    Full RFQ request object returned by the API.
    """

    request_id: str
    """Unique request identifier."""

    user_address: str
    """Address of the requester."""

    proxy_address: Optional[str] = None
    """Proxy address if applicable."""

    token: Optional[str] = None
    """Token ID being traded."""

    complement: Optional[str] = None
    """Complement token ID."""

    condition: Optional[str] = None
    """Condition ID (market)."""

    side: Optional[str] = None
    """Order side: "BUY" or "SELL"."""

    size_in: Optional[str] = None
    """Size of asset_in."""

    size_out: Optional[str] = None
    """Size of asset_out."""

    price: Optional[float] = None
    """Price."""

    accepted_quote_id: Optional[str] = None
    """ID of accepted quote (if any)."""

    state: Optional[str] = None
    """Request state."""

    expiry: Optional[str] = None
    """Expiration timestamp."""

    created_at: Optional[str] = None
    """Creation timestamp."""

    updated_at: Optional[str] = None
    """Last update timestamp."""


@dataclass
class RfqQuote:
    """
    Full RFQ quote object returned by the API.
    """

    quote_id: str
    """Unique quote identifier."""

    request_id: str
    """Associated request ID."""

    user_address: str
    """Address of the quoter."""

    proxy_address: Optional[str] = None
    """Proxy address if applicable."""

    complement: Optional[str] = None
    """Complement token ID."""

    condition: Optional[str] = None
    """Condition ID (market)."""

    token: Optional[str] = None
    """Token ID."""

    side: Optional[str] = None
    """Quote side: "BUY" or "SELL"."""

    size_in: Optional[str] = None
    """Size of asset_in."""

    size_out: Optional[str] = None
    """Size of asset_out."""

    price: Optional[float] = None
    """Quote price."""

    state: Optional[str] = None
    """Quote state."""

    expiry: Optional[str] = None
    """Expiration timestamp."""

    created_at: Optional[str] = None
    """Creation timestamp."""

    updated_at: Optional[str] = None
    """Last update timestamp."""


@dataclass
class RfqRequestResponse:
    """
    Response from creating an RFQ request.
    """

    request_id: Optional[str] = None
    """Created request ID."""

    error: Optional[str] = None
    """Error message if failed."""


@dataclass
class RfqQuoteResponse:
    """
    Response from creating an RFQ quote.
    """

    quote_id: Optional[str] = None
    """Created quote ID."""

    error: Optional[str] = None
    """Error message if failed."""


@dataclass
class RfqPaginatedResponse:
    """
    Paginated response for list queries.
    """

    data: List[Any] = field(default_factory=list)
    """Array of results (RfqRequest or RfqQuote objects)."""

    next_cursor: Optional[str] = None
    """Cursor for next page."""

    limit: Optional[int] = None
    """Page limit."""

    count: Optional[int] = None
    """Number of results in this page."""

    total_count: Optional[int] = None
    """Total count (optional)."""


class MatchType(str, Enum):
    COMPLEMENTARY = "COMPLEMENTARY"
    MINT = "MINT"
    MERGE = "MERGE"


================================================
FILE: py_clob_client/signer.py
================================================
from eth_account import Account


class Signer:
    def __init__(self, private_key: str, chain_id: int):
        assert private_key is not None and chain_id is not None

        self.private_key = private_key
        self.account = Account.from_key(private_key)
        self.chain_id = chain_id

    def address(self):
        return self.account.address

    def get_chain_id(self):
        return self.chain_id

    def sign(self, message_hash):
        """
        Signs a message hash
        """
        return Account._sign_hash(message_hash, self.private_key).signature.hex()


================================================
FILE: py_clob_client/signing/__init__.py
================================================


================================================
FILE: py_clob_client/signing/eip712.py
================================================
from poly_eip712_structs import make_domain
from eth_utils import keccak
from py_order_utils.utils import prepend_zx

from .model import ClobAuth
from ..signer import Signer

CLOB_DOMAIN_NAME = "ClobAuthDomain"
CLOB_VERSION = "1"
MSG_TO_SIGN = "This message attests that I control the given wallet"


def get_clob_auth_domain(chain_id: int):
    return make_domain(name=CLOB_DOMAIN_NAME, version=CLOB_VERSION, chainId=chain_id)


def sign_clob_auth_message(signer: Signer, timestamp: int, nonce: int) -> str:
    clob_auth_msg = ClobAuth(
        address=signer.address(),
        timestamp=str(timestamp),
        nonce=nonce,
        message=MSG_TO_SIGN,
    )
    chain_id = signer.get_chain_id()
    auth_struct_hash = prepend_zx(
        keccak(clob_auth_msg.signable_bytes(get_clob_auth_domain(chain_id))).hex()
    )
    return prepend_zx(signer.sign(auth_struct_hash))


================================================
FILE: py_clob_client/signing/hmac.py
================================================
import hmac
import hashlib
import base64


def build_hmac_signature(
    secret: str, timestamp: str, method: str, requestPath: str, body=None
):
    """
    Creates an HMAC signature by signing a payload with the secret
    """
    base64_secret = base64.urlsafe_b64decode(secret)
    message = str(timestamp) + str(method) + str(requestPath)
    if body:
        # NOTE: Necessary to replace single quotes with double quotes
        # to generate the same hmac message as go and typescript
        message += str(body).replace("'", '"')

    h = hmac.new(base64_secret, bytes(message, "utf-8"), hashlib.sha256)

    # ensure base64 encoded
    return (base64.urlsafe_b64encode(h.digest())).decode("utf-8")


================================================
FILE: py_clob_client/signing/model.py
================================================
from poly_eip712_structs import EIP712Struct, Address, String, Uint


class ClobAuth(EIP712Struct):
    address = Address()
    timestamp = String()
    nonce = Uint()
    message = String()


================================================
FILE: py_clob_client/utilities.py
================================================
import hashlib
import json

from .clob_types import OrderBookSummary, OrderSummary, TickSize


def parse_raw_orderbook_summary(raw_obs: any) -> OrderBookSummary:
    bids = []
    for bid in raw_obs["bids"]:
        bids.append(OrderSummary(size=bid["size"], price=bid["price"]))

    asks = []
    for ask in raw_obs["asks"]:
        asks.append(OrderSummary(size=ask["size"], price=ask["price"]))

    orderbookSummary = OrderBookSummary(
        market=raw_obs["market"],
        asset_id=raw_obs["asset_id"],
        timestamp=raw_obs["timestamp"],
        last_trade_price=raw_obs["last_trade_price"],
        min_order_size=raw_obs["min_order_size"],
        neg_risk=raw_obs["neg_risk"],
        tick_size=raw_obs["tick_size"],
        bids=bids,
        asks=asks,
        hash=raw_obs["hash"],
    )

    return orderbookSummary


def generate_orderbook_summary_hash(orderbook: OrderBookSummary) -> str:
    """
    Server-compatible orderbook hash.

    The server computes SHA1 over a compact JSON payload with a specific key order,
    and with the "hash" field set to an empty string while hashing.
    """

    # Go server-side payload field order (struct order):
    # market, asset_id, timestamp, hash, bids, asks, min_order_size, tick_size, neg_risk, last_trade_price
    payload = {
        "market": orderbook.market,
        "asset_id": orderbook.asset_id,
        "timestamp": orderbook.timestamp,
        "hash": "",
        "bids": [{"price": o.price, "size": o.size} for o in (orderbook.bids or [])],
        "asks": [{"price": o.price, "size": o.size} for o in (orderbook.asks or [])],
        "min_order_size": orderbook.min_order_size,
        "tick_size": orderbook.tick_size,
        "neg_risk": orderbook.neg_risk,
        "last_trade_price": orderbook.last_trade_price,
    }

    serialized = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
    h = hashlib.sha1(serialized.encode("utf-8")).hexdigest()
    orderbook.hash = h
    return h


def order_to_json(order, owner, orderType, post_only: bool = False) -> dict:
    return {"order": order.dict(), "owner": owner, "orderType": orderType, "postOnly": post_only}


def is_tick_size_smaller(a: TickSize, b: TickSize) -> bool:
    return float(a) < float(b)


def price_valid(price: float, tick_size: TickSize) -> bool:
    return price >= float(tick_size) and price <= 1 - float(tick_size)


================================================
FILE: requirements.txt
================================================
black==24.4.2
eth-account===0.13.0
eth-utils===4.1.1
poly_eip712_structs==0.0.1
py_order_utils==0.3.2
pytest==8.2.2
python-dotenv==0.19.2
websockets==12.0
py-builder-signing-sdk==0.0.2
httpx[http2]==0.27.0


================================================
FILE: setup.py
================================================
import setuptools

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setuptools.setup(
    name="py_clob_client",
    version="0.34.6",
    author="Polymarket Engineering",
    author_email="engineering@polymarket.com",
    maintainer="Polymarket Engineering",
    maintainer_email="engineering@polymarket.com",
    description="Python client for the Polymarket CLOB",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/Polymarket/py-clob-client",
    install_requires=[
        "eth-account>=0.13.0",
        "eth-utils>=4.1.1",
        "poly_eip712_structs>=0.0.1",
        "py-order-utils>=0.3.2",
        "python-dotenv",
        "py-builder-signing-sdk>=0.0.2",
        "httpx[http2]>=0.27.0",
    ],
    project_urls={
        "Bug Tracker": "https://github.com/Polymarket/py-clob-client/issues",
    },
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    packages=setuptools.find_packages(),
    python_requires=">=3.9.10",
)


================================================
FILE: tests/__init__.py
================================================


================================================
FILE: tests/headers/__init__.py
================================================


================================================
FILE: tests/headers/test_headers.py
================================================
from datetime import datetime
from unittest import TestCase
from py_clob_client.clob_types import ApiCreds, RequestArgs
from py_clob_client.constants import AMOY
from py_clob_client.headers.headers import (
    POLY_ADDRESS,
    POLY_API_KEY,
    POLY_NONCE,
    POLY_PASSPHRASE,
    POLY_SIGNATURE,
    POLY_TIMESTAMP,
    create_level_1_headers,
    create_level_2_headers,
)
from py_clob_client.signer import Signer

# publicly known private key
private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
chain_id = AMOY
signer = Signer(private_key=private_key, chain_id=chain_id)

creds = ApiCreds(
    api_key="000000000-0000-0000-0000-000000000000",
    api_passphrase="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    api_secret="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
)


class TestHeaders(TestCase):
    def test_create_level_1_headers(self):
        # no nonce
        l1_headers = create_level_1_headers(signer)
        self.assertIsNotNone(l1_headers)
        self.assertEqual(l1_headers[POLY_ADDRESS], signer.address())
        self.assertIsNotNone(l1_headers[POLY_SIGNATURE])
        self.assertIsNotNone(l1_headers[POLY_TIMESTAMP])
        self.assertTrue(
            int(l1_headers[POLY_TIMESTAMP]) <= int(datetime.now().timestamp())
        )
        self.assertEqual(l1_headers[POLY_NONCE], "0")

        # nonce
        l1_headers = create_level_1_headers(signer, nonce=1012)
        self.assertIsNotNone(l1_headers)
        self.assertEqual(l1_headers[POLY_ADDRESS], signer.address())
        self.assertIsNotNone(l1_headers[POLY_SIGNATURE])
        self.assertIsNotNone(l1_headers[POLY_TIMESTAMP])
        self.asser
Download .txt
gitextract_cz0xsyu3/

├── .editorconfig
├── .github/
│   ├── CODEOWNERS
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── release.yaml
│       └── workflow.yaml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── examples/
│   ├── GTD_order.py
│   ├── are_orders_scoring.py
│   ├── cancel_all.py
│   ├── cancel_market_orders.py
│   ├── cancel_order.py
│   ├── cancel_orders.py
│   ├── create_api_key.py
│   ├── create_readonly_api_key.py
│   ├── delete_readonly_api_key.py
│   ├── derive_api_key.py
│   ├── drop_notifications.py
│   ├── get_api_keys.py
│   ├── get_balance_allowance.py
│   ├── get_builder_trades.py
│   ├── get_closed_only_mode.py
│   ├── get_last_trade_price.py
│   ├── get_last_trades_prices.py
│   ├── get_market_trades_events.py
│   ├── get_markets.py
│   ├── get_mid_market_price.py
│   ├── get_mid_markets_prices.py
│   ├── get_notifications.py
│   ├── get_ok.py
│   ├── get_open_orders_with_readonly_key.py
│   ├── get_order.py
│   ├── get_orderbook.py
│   ├── get_orderbooks.py
│   ├── get_orders.py
│   ├── get_price.py
│   ├── get_prices.py
│   ├── get_readonly_api_keys.py
│   ├── get_server_time.py
│   ├── get_spread.py
│   ├── get_spreads.py
│   ├── get_trades.py
│   ├── is_order_scoring.py
│   ├── market_buy_order.py
│   ├── market_sell_order.py
│   ├── order.py
│   ├── orders.py
│   ├── place_builder_order.py
│   ├── post_heartbeat.py
│   ├── post_only_order.py
│   ├── rfq_accept_quote.py
│   ├── rfq_approve_order.py
│   ├── rfq_cancel_quote.py
│   ├── rfq_cancel_request.py
│   ├── rfq_config.py
│   ├── rfq_create_quote.py
│   ├── rfq_create_request.py
│   ├── rfq_full_flow.py
│   ├── rfq_get_best_quote.py
│   ├── rfq_get_quotes.py
│   ├── rfq_get_requests.py
│   └── update_balance_allowance.py
├── py_clob_client/
│   ├── __init__.py
│   ├── client.py
│   ├── clob_types.py
│   ├── config.py
│   ├── constants.py
│   ├── endpoints.py
│   ├── exceptions.py
│   ├── headers/
│   │   ├── __init__.py
│   │   └── headers.py
│   ├── http_helpers/
│   │   ├── __init__.py
│   │   └── helpers.py
│   ├── order_builder/
│   │   ├── __init__.py
│   │   ├── builder.py
│   │   ├── constants.py
│   │   └── helpers.py
│   ├── rfq/
│   │   ├── __init__.py
│   │   ├── rfq_client.py
│   │   ├── rfq_helpers.py
│   │   └── rfq_types.py
│   ├── signer.py
│   ├── signing/
│   │   ├── __init__.py
│   │   ├── eip712.py
│   │   ├── hmac.py
│   │   └── model.py
│   └── utilities.py
├── requirements.txt
├── setup.py
└── tests/
    ├── __init__.py
    ├── headers/
    │   ├── __init__.py
    │   └── test_headers.py
    ├── http_helpers/
    │   ├── __init__.py
    │   └── test_helpers.py
    ├── order_builder/
    │   ├── __init__.py
    │   ├── test_builder.py
    │   └── test_helpers.py
    ├── rfq/
    │   ├── test_rfq_payload.py
    │   └── test_rfq_query_params.py
    ├── signing/
    │   ├── __init__.py
    │   ├── test_eip712.py
    │   └── test_hmac.py
    └── test_utilities.py
Download .txt
SYMBOL INDEX (356 symbols across 80 files)

FILE: examples/GTD_order.py
  function main (line 14) | def main():

FILE: examples/are_orders_scoring.py
  function main (line 11) | def main():

FILE: examples/cancel_all.py
  function main (line 13) | def main():

FILE: examples/cancel_market_orders.py
  function main (line 13) | def main():

FILE: examples/cancel_order.py
  function main (line 13) | def main():

FILE: examples/cancel_orders.py
  function main (line 13) | def main():

FILE: examples/create_api_key.py
  function main (line 10) | def main():

FILE: examples/create_readonly_api_key.py
  function main (line 13) | def main():

FILE: examples/delete_readonly_api_key.py
  function main (line 13) | def main():

FILE: examples/derive_api_key.py
  function main (line 10) | def main():

FILE: examples/drop_notifications.py
  function main (line 13) | def main():

FILE: examples/get_api_keys.py
  function main (line 13) | def main():

FILE: examples/get_balance_allowance.py
  function main (line 11) | def main():

FILE: examples/get_builder_trades.py
  function main (line 15) | def main():

FILE: examples/get_closed_only_mode.py
  function main (line 13) | def main():

FILE: examples/get_last_trade_price.py
  function main (line 13) | def main():

FILE: examples/get_last_trades_prices.py
  function main (line 14) | def main():

FILE: examples/get_market_trades_events.py
  function main (line 11) | def main():

FILE: examples/get_markets.py
  function main (line 11) | def main():

FILE: examples/get_mid_market_price.py
  function main (line 12) | def main():

FILE: examples/get_mid_markets_prices.py
  function main (line 12) | def main():

FILE: examples/get_notifications.py
  function main (line 13) | def main():

FILE: examples/get_ok.py
  function main (line 4) | def main():

FILE: examples/get_open_orders_with_readonly_key.py
  function main (line 12) | def main():

FILE: examples/get_order.py
  function main (line 11) | def main():

FILE: examples/get_orderbook.py
  function main (line 8) | def main():

FILE: examples/get_orderbooks.py
  function main (line 5) | def main():

FILE: examples/get_orders.py
  function main (line 11) | def main():

FILE: examples/get_price.py
  function main (line 5) | def main():

FILE: examples/get_prices.py
  function main (line 5) | def main():

FILE: examples/get_readonly_api_keys.py
  function main (line 13) | def main():

FILE: examples/get_server_time.py
  function main (line 4) | def main():

FILE: examples/get_spread.py
  function main (line 12) | def main():

FILE: examples/get_spreads.py
  function main (line 5) | def main():

FILE: examples/get_trades.py
  function main (line 13) | def main():

FILE: examples/is_order_scoring.py
  function main (line 11) | def main():

FILE: examples/market_buy_order.py
  function main (line 13) | def main():

FILE: examples/market_sell_order.py
  function main (line 13) | def main():

FILE: examples/order.py
  function main (line 14) | def main():

FILE: examples/orders.py
  function main (line 14) | def main():

FILE: examples/place_builder_order.py
  function main (line 14) | def main():

FILE: examples/post_heartbeat.py
  function main (line 14) | def main():

FILE: examples/post_only_order.py
  function main (line 14) | def main():

FILE: examples/rfq_accept_quote.py
  function main (line 13) | def main():

FILE: examples/rfq_approve_order.py
  function main (line 13) | def main():

FILE: examples/rfq_cancel_quote.py
  function main (line 12) | def main():

FILE: examples/rfq_cancel_request.py
  function main (line 12) | def main():

FILE: examples/rfq_config.py
  function main (line 11) | def main():

FILE: examples/rfq_create_quote.py
  function main (line 13) | def main():

FILE: examples/rfq_create_request.py
  function main (line 13) | def main():

FILE: examples/rfq_full_flow.py
  function main (line 71) | def main():

FILE: examples/rfq_get_best_quote.py
  function main (line 12) | def main():

FILE: examples/rfq_get_quotes.py
  function main (line 12) | def main():

FILE: examples/rfq_get_requests.py
  function main (line 12) | def main():

FILE: examples/update_balance_allowance.py
  function main (line 11) | def main():

FILE: py_clob_client/client.py
  class ClobClient (line 116) | class ClobClient:
    method __init__ (line 117) | def __init__(
    method get_address (line 167) | def get_address(self):
    method get_collateral_address (line 173) | def get_collateral_address(self):
    method get_conditional_address (line 181) | def get_conditional_address(self):
    method get_exchange_address (line 189) | def get_exchange_address(self, neg_risk=False):
    method get_ok (line 197) | def get_ok(self):
    method get_server_time (line 204) | def get_server_time(self):
    method create_api_key (line 211) | def create_api_key(self, nonce: int = None) -> ApiCreds:
    method derive_api_key (line 232) | def derive_api_key(self, nonce: int = None) -> ApiCreds:
    method create_or_derive_api_creds (line 253) | def create_or_derive_api_creds(self, nonce: int = None) -> ApiCreds:
    method set_api_creds (line 262) | def set_api_creds(self, creds: ApiCreds):
    method get_api_keys (line 269) | def get_api_keys(self):
    method get_closed_only_mode (line 280) | def get_closed_only_mode(self):
    method delete_api_key (line 291) | def delete_api_key(self):
    method create_readonly_api_key (line 302) | def create_readonly_api_key(self) -> ReadonlyApiKeyResponse:
    method get_readonly_api_keys (line 319) | def get_readonly_api_keys(self) -> list[str]:
    method delete_readonly_api_key (line 330) | def delete_readonly_api_key(self, key: str) -> bool:
    method validate_readonly_api_key (line 352) | def validate_readonly_api_key(self, address: str, key: str) -> str:
    method get_midpoint (line 363) | def get_midpoint(self, token_id):
    method get_midpoints (line 369) | def get_midpoints(self, params: list[BookParams]):
    method get_price (line 376) | def get_price(self, token_id, side):
    method get_prices (line 382) | def get_prices(self, params: list[BookParams]):
    method get_spread (line 389) | def get_spread(self, token_id):
    method get_spreads (line 395) | def get_spreads(self, params: list[BookParams]):
    method get_tick_size (line 402) | def get_tick_size(self, token_id: str) -> TickSize:
    method clear_tick_size_cache (line 418) | def clear_tick_size_cache(self, token_id: str = None):
    method _update_tick_size_from_order_book (line 433) | def _update_tick_size_from_order_book(self, book: OrderBookSummary):
    method get_neg_risk (line 441) | def get_neg_risk(self, token_id: str) -> bool:
    method get_fee_rate_bps (line 450) | def get_fee_rate_bps(self, token_id: str) -> int:
    method __resolve_tick_size (line 460) | def __resolve_tick_size(
    method __resolve_fee_rate (line 476) | def __resolve_fee_rate(self, token_id: str, user_fee_rate: int = None)...
    method create_order (line 492) | def create_order(
    method create_market_order (line 537) | def create_market_order(
    method post_orders (line 592) | def post_orders(self, args: list[PostOrdersArgs]):
    method post_order (line 623) | def post_order(self, order, orderType: OrderType = OrderType.GTC, post...
    method create_and_post_order (line 654) | def create_and_post_order(
    method cancel (line 663) | def cancel(self, order_id):
    method cancel_orders (line 684) | def cancel_orders(self, order_ids):
    method cancel_all (line 703) | def cancel_all(self):
    method post_heartbeat (line 713) | def post_heartbeat(self, heartbeat_id: Optional[str]):
    method cancel_market_orders (line 729) | def cancel_market_orders(self, market: str = "", asset_id: str = ""):
    method get_orders (line 750) | def get_orders(self, params: OpenOrderParams = None, next_cursor="MA=="):
    method get_order_book (line 771) | def get_order_book(self, token_id) -> OrderBookSummary:
    method get_order_books (line 780) | def get_order_books(self, params: list[BookParams]) -> list[OrderBookS...
    method get_order_book_hash (line 791) | def get_order_book_hash(self, orderbook: OrderBookSummary) -> str:
    method get_order (line 797) | def get_order(self, order_id):
    method get_trades (line 808) | def get_trades(self, params: TradeParams = None, next_cursor="MA=="):
    method get_last_trade_price (line 829) | def get_last_trade_price(self, token_id):
    method get_last_trades_prices (line 835) | def get_last_trades_prices(self, params: list[BookParams]):
    method assert_level_1_auth (line 842) | def assert_level_1_auth(self):
    method assert_level_2_auth (line 849) | def assert_level_2_auth(self):
    method assert_builder_auth (line 856) | def assert_builder_auth(self):
    method can_builder_auth (line 863) | def can_builder_auth(self) -> bool:
    method _get_client_mode (line 866) | def _get_client_mode(self):
    method _generate_builder_headers (line 873) | def _generate_builder_headers(self, request_args: RequestArgs, headers...
    method _get_builder_headers (line 888) | def _get_builder_headers(self, method: str, path: str, body: Optional[...
    method get_notifications (line 905) | def get_notifications(self):
    method drop_notifications (line 918) | def drop_notifications(self, params: DropNotificationParams = None):
    method get_balance_allowance (line 931) | def get_balance_allowance(self, params: BalanceAllowanceParams = None):
    method update_balance_allowance (line 946) | def update_balance_allowance(self, params: BalanceAllowanceParams = No...
    method is_order_scoring (line 961) | def is_order_scoring(self, params: OrderScoringParams):
    method are_orders_scoring (line 974) | def are_orders_scoring(self, params: OrdersScoringParams):
    method get_sampling_markets (line 995) | def get_sampling_markets(self, next_cursor="MA=="):
    method get_sampling_simplified_markets (line 1003) | def get_sampling_simplified_markets(self, next_cursor="MA=="):
    method get_markets (line 1013) | def get_markets(self, next_cursor="MA=="):
    method get_simplified_markets (line 1019) | def get_simplified_markets(self, next_cursor="MA=="):
    method get_market (line 1027) | def get_market(self, condition_id):
    method get_market_trades_events (line 1033) | def get_market_trades_events(self, condition_id):
    method get_builder_trades (line 1039) | def get_builder_trades(self, params: TradeParams = None, next_cursor="...
    method calculate_market_price (line 1062) | def calculate_market_price(

FILE: py_clob_client/clob_types.py
  class OrderType (line 12) | class OrderType(enumerate):
  class ApiCreds (line 20) | class ApiCreds:
  class ReadonlyApiKeyResponse (line 27) | class ReadonlyApiKeyResponse:
  class RequestArgs (line 32) | class RequestArgs:
  class BookParams (line 40) | class BookParams:
  class OrderArgs (line 46) | class OrderArgs:
  class MarketOrderArgs (line 89) | class MarketOrderArgs:
  class TradeParams (line 130) | class TradeParams:
  class OpenOrderParams (line 140) | class OpenOrderParams:
  class DropNotificationParams (line 147) | class DropNotificationParams:
  class OrderSummary (line 152) | class OrderSummary:
    method __dict__ (line 157) | def __dict__(self):
    method json (line 161) | def json(self):
  class OrderBookSummary (line 166) | class OrderBookSummary:
    method __dict__ (line 179) | def __dict__(self):
    method json (line 183) | def json(self):
  class AssetType (line 187) | class AssetType(enumerate):
  class BalanceAllowanceParams (line 193) | class BalanceAllowanceParams:
  class OrderScoringParams (line 200) | class OrderScoringParams:
  class OrdersScoringParams (line 205) | class OrdersScoringParams:
  class CreateOrderOptions (line 213) | class CreateOrderOptions:
  class PartialCreateOrderOptions (line 219) | class PartialCreateOrderOptions:
  class RoundConfig (line 225) | class RoundConfig:
  class ContractConfig (line 232) | class ContractConfig:
  class PostOrdersArgs (line 254) | class PostOrdersArgs:

FILE: py_clob_client/config.py
  function get_contract_config (line 4) | def get_contract_config(chainID: int, neg_risk: bool = False) -> Contrac...

FILE: py_clob_client/exceptions.py
  class PolyException (line 6) | class PolyException(Exception):
    method __init__ (line 7) | def __init__(self, msg):
  class PolyApiException (line 11) | class PolyApiException(PolyException):
    method __init__ (line 12) | def __init__(self, resp: Optional[httpx.Response] = None, error_msg=No...
    method _get_message (line 22) | def _get_message(self, resp: httpx.Response):
    method __repr__ (line 28) | def __repr__(self):
    method __str__ (line 31) | def __str__(self):

FILE: py_clob_client/headers/headers.py
  function create_level_1_headers (line 16) | def create_level_1_headers(signer: Signer, nonce: int = None):
  function create_level_2_headers (line 37) | def create_level_2_headers(signer: Signer, creds: ApiCreds, request_args...
  function enrich_l2_headers_with_builder_headers (line 65) | def enrich_l2_headers_with_builder_headers(

FILE: py_clob_client/http_helpers/helpers.py
  function overloadHeaders (line 22) | def overloadHeaders(method: str, headers: dict) -> dict:
  function request (line 37) | def request(endpoint: str, method: str, headers=None, data=None):
  function post (line 68) | def post(endpoint, headers=None, data=None):
  function get (line 72) | def get(endpoint, headers=None, data=None):
  function delete (line 76) | def delete(endpoint, headers=None, data=None):
  function put (line 80) | def put(endpoint, headers=None, data=None):
  function build_query_params (line 84) | def build_query_params(url: str, param: str, val: str) -> str:
  function add_query_trade_params (line 96) | def add_query_trade_params(
  function add_query_open_orders_params (line 137) | def add_query_open_orders_params(
  function drop_notifications_query_params (line 162) | def drop_notifications_query_params(
  function add_balance_allowance_params_to_url (line 176) | def add_balance_allowance_params_to_url(
  function add_order_scoring_params_to_url (line 194) | def add_order_scoring_params_to_url(
  function add_orders_scoring_params_to_url (line 208) | def add_orders_scoring_params_to_url(

FILE: py_clob_client/order_builder/builder.py
  class OrderBuilder (line 39) | class OrderBuilder:
    method __init__ (line 40) | def __init__(self, signer: Signer, sig_type=None, funder=None):
    method get_order_amounts (line 51) | def get_order_amounts(
    method get_market_order_amounts (line 85) | def get_market_order_amounts(
    method create_order (line 119) | def create_order(
    method create_market_order (line 158) | def create_market_order(
    method calculate_buy_market_price (line 197) | def calculate_buy_market_price(
    method calculate_sell_market_price (line 217) | def calculate_sell_market_price(

FILE: py_clob_client/order_builder/helpers.py
  function round_down (line 5) | def round_down(x: float, sig_digits: int) -> float:
  function round_normal (line 9) | def round_normal(x: float, sig_digits: int) -> float:
  function round_up (line 13) | def round_up(x: float, sig_digits: int) -> float:
  function to_token_decimals (line 17) | def to_token_decimals(x: float) -> int:
  function decimal_places (line 24) | def decimal_places(x: float) -> int:

FILE: py_clob_client/rfq/rfq_client.py
  class RfqClient (line 57) | class RfqClient:
    method __init__ (line 67) | def __init__(self, parent: "ClobClient"):
    method _ensure_l2_auth (line 77) | def _ensure_l2_auth(self) -> None:
    method _get_l2_headers (line 86) | def _get_l2_headers(self, method: str, endpoint: str, body: Any = None...
    method _build_url (line 108) | def _build_url(self, endpoint: str) -> str:
    method create_rfq_request (line 116) | def create_rfq_request(
    method cancel_rfq_request (line 221) | def cancel_rfq_request(self, params: CancelRfqRequestParams) -> str:
    method get_rfq_requests (line 238) | def get_rfq_requests(
    method create_rfq_quote (line 268) | def create_rfq_quote(
    method get_rfq_requester_quotes (line 378) | def get_rfq_requester_quotes(self, params: Optional[GetRfqQuotesParams...
    method get_rfq_quoter_quotes (line 402) | def get_rfq_quoter_quotes(self, params: Optional[GetRfqQuotesParams] =...
    method get_rfq_best_quote (line 426) | def get_rfq_best_quote(
    method cancel_rfq_quote (line 448) | def cancel_rfq_quote(self, params: CancelRfqQuoteParams) -> str:
    method accept_rfq_quote (line 469) | def accept_rfq_quote(self, params: AcceptQuoteParams) -> str:
    method approve_rfq_order (line 550) | def approve_rfq_order(self, params: ApproveOrderParams) -> str:
    method rfq_config (line 637) | def rfq_config(self) -> dict:
    method _get_request_order_creation_payload (line 649) | def _get_request_order_creation_payload(self, quote: dict) -> dict:

FILE: py_clob_client/rfq/rfq_helpers.py
  function parse_units (line 18) | def parse_units(value: str, decimals: int) -> int:
  function to_camel_case (line 46) | def to_camel_case(snake_str: str) -> str:
  function parse_rfq_requests_params (line 68) | def parse_rfq_requests_params(params: Optional[GetRfqRequestsParams] = N...
  function parse_rfq_quotes_params (line 115) | def parse_rfq_quotes_params(params: Optional[GetRfqQuotesParams] = None)...

FILE: py_clob_client/rfq/rfq_types.py
  class RfqUserRequest (line 18) | class RfqUserRequest:
  class RfqUserQuote (line 40) | class RfqUserQuote:
  class CreateRfqRequestParams (line 65) | class CreateRfqRequestParams:
  class CreateRfqQuoteParams (line 89) | class CreateRfqQuoteParams:
  class CancelRfqRequestParams (line 113) | class CancelRfqRequestParams:
  class CancelRfqQuoteParams (line 123) | class CancelRfqQuoteParams:
  class AcceptQuoteParams (line 133) | class AcceptQuoteParams:
  class ApproveOrderParams (line 152) | class ApproveOrderParams:
  class GetRfqRequestsParams (line 171) | class GetRfqRequestsParams:
  class GetRfqQuotesParams (line 219) | class GetRfqQuotesParams:
  class GetRfqBestQuoteParams (line 270) | class GetRfqBestQuoteParams:
  class RfqRequest (line 285) | class RfqRequest:
  class RfqQuote (line 337) | class RfqQuote:
  class RfqRequestResponse (line 389) | class RfqRequestResponse:
  class RfqQuoteResponse (line 402) | class RfqQuoteResponse:
  class RfqPaginatedResponse (line 415) | class RfqPaginatedResponse:
  class MatchType (line 436) | class MatchType(str, Enum):

FILE: py_clob_client/signer.py
  class Signer (line 4) | class Signer:
    method __init__ (line 5) | def __init__(self, private_key: str, chain_id: int):
    method address (line 12) | def address(self):
    method get_chain_id (line 15) | def get_chain_id(self):
    method sign (line 18) | def sign(self, message_hash):

FILE: py_clob_client/signing/eip712.py
  function get_clob_auth_domain (line 13) | def get_clob_auth_domain(chain_id: int):
  function sign_clob_auth_message (line 17) | def sign_clob_auth_message(signer: Signer, timestamp: int, nonce: int) -...

FILE: py_clob_client/signing/hmac.py
  function build_hmac_signature (line 6) | def build_hmac_signature(

FILE: py_clob_client/signing/model.py
  class ClobAuth (line 4) | class ClobAuth(EIP712Struct):

FILE: py_clob_client/utilities.py
  function parse_raw_orderbook_summary (line 7) | def parse_raw_orderbook_summary(raw_obs: any) -> OrderBookSummary:
  function generate_orderbook_summary_hash (line 32) | def generate_orderbook_summary_hash(orderbook: OrderBookSummary) -> str:
  function order_to_json (line 61) | def order_to_json(order, owner, orderType, post_only: bool = False) -> d...
  function is_tick_size_smaller (line 65) | def is_tick_size_smaller(a: TickSize, b: TickSize) -> bool:
  function price_valid (line 69) | def price_valid(price: float, tick_size: TickSize) -> bool:

FILE: tests/headers/test_headers.py
  class TestHeaders (line 29) | class TestHeaders(TestCase):
    method test_create_level_1_headers (line 30) | def test_create_level_1_headers(self):
    method test_create_level_2_headers (line 53) | def test_create_level_2_headers(self):

FILE: tests/http_helpers/test_helpers.py
  class TestHelpers (line 23) | class TestHelpers(TestCase):
    method test_build_query_params (line 24) | def test_build_query_params(self):
    method test_add_query_trade_params (line 35) | def test_add_query_trade_params(self):
    method test_add_query_trade_params_no_params_includes_next_cursor (line 54) | def test_add_query_trade_params_no_params_includes_next_cursor(self):
    method test_add_query_open_orders_params (line 59) | def test_add_query_open_orders_params(self):
    method test_add_query_open_orders_params_no_params_includes_next_cursor (line 70) | def test_add_query_open_orders_params_no_params_includes_next_cursor(s...
    method test_drop_notifications_query_params (line 75) | def test_drop_notifications_query_params(self):
    method test_add_balance_allowance_params_to_url (line 83) | def test_add_balance_allowance_params_to_url(self):
    method test_add_order_scoring_params_to_url (line 102) | def test_add_order_scoring_params_to_url(self):
    method test_add_orders_scoring_params_to_urll (line 110) | def test_add_orders_scoring_params_to_urll(self):

FILE: tests/order_builder/test_builder.py
  class TestOrderBuilder (line 29) | class TestOrderBuilder(TestCase):
    method test_calculate_buy_market_price_FOK (line 30) | def test_calculate_buy_market_price_FOK(self):
    method test_calculate_sell_market_price_FOK (line 86) | def test_calculate_sell_market_price_FOK(self):
    method test_calculate_buy_market_price_FAK (line 152) | def test_calculate_buy_market_price_FAK(self):
    method test_calculate_sell_market_price_FAK (line 209) | def test_calculate_sell_market_price_FAK(self):
    method test_get_market_order_amounts_buy_0_1 (line 276) | def test_get_market_order_amounts_buy_0_1(self):
    method test_get_market_order_amounts_buy_0_01 (line 298) | def test_get_market_order_amounts_buy_0_01(self):
    method test_get_market_order_amounts_buy_0_001 (line 320) | def test_get_market_order_amounts_buy_0_001(self):
    method test_get_market_order_amounts_buy_0_0001 (line 342) | def test_get_market_order_amounts_buy_0_0001(self):
    method test_get_market_order_amounts_sell_0_1 (line 364) | def test_get_market_order_amounts_sell_0_1(self):
    method test_get_market_order_amounts_sell_0_01 (line 386) | def test_get_market_order_amounts_sell_0_01(self):
    method test_get_market_order_amounts_sell_0_001 (line 408) | def test_get_market_order_amounts_sell_0_001(self):
    method test_get_market_order_amounts_sell_0_0001 (line 430) | def test_get_market_order_amounts_sell_0_0001(self):
    method test_get_order_amounts_buy_0_1 (line 452) | def test_get_order_amounts_buy_0_1(self):
    method test_get_order_amounts_buy_0_01 (line 474) | def test_get_order_amounts_buy_0_01(self):
    method test_get_order_amounts_buy_0_001 (line 496) | def test_get_order_amounts_buy_0_001(self):
    method test_get_order_amounts_buy_0_0001 (line 518) | def test_get_order_amounts_buy_0_0001(self):
    method test_get_order_amounts_sell_0_1 (line 540) | def test_get_order_amounts_sell_0_1(self):
    method test_get_order_amounts_sell_0_01 (line 562) | def test_get_order_amounts_sell_0_01(self):
    method test_get_order_amounts_sell_0_001 (line 584) | def test_get_order_amounts_sell_0_001(self):
    method test_get_order_amounts_sell_0_0001 (line 606) | def test_get_order_amounts_sell_0_0001(self):
    method test_create_order_decimal_accuracy (line 628) | def test_create_order_decimal_accuracy(self):
    method test_create_order_buy_0_1 (line 805) | def test_create_order_buy_0_1(self):
    method test_create_order_buy_0_01 (line 874) | def test_create_order_buy_0_01(self):
    method test_create_order_buy_0_001 (line 943) | def test_create_order_buy_0_001(self):
    method test_create_order_buy_0_0001 (line 1012) | def test_create_order_buy_0_0001(self):
    method test_create_order_sell_0_1 (line 1081) | def test_create_order_sell_0_1(self):
    method test_create_order_sell_0_01 (line 1150) | def test_create_order_sell_0_01(self):
    method test_create_order_sell_0_001 (line 1219) | def test_create_order_sell_0_001(self):
    method test_create_order_sell_0_0001 (line 1288) | def test_create_order_sell_0_0001(self):
    method test_create_order_decimal_accuracy_neg_risk (line 1357) | def test_create_order_decimal_accuracy_neg_risk(self):
    method test_create_order_buy_0_1_neg_risk (line 1534) | def test_create_order_buy_0_1_neg_risk(self):
    method test_create_order_buy_0_01_neg_risk (line 1603) | def test_create_order_buy_0_01_neg_risk(self):
    method test_create_order_buy_0_001_neg_risk (line 1672) | def test_create_order_buy_0_001_neg_risk(self):
    method test_create_order_buy_0_0001_neg_risk (line 1741) | def test_create_order_buy_0_0001_neg_risk(self):
    method test_create_order_sell_0_1_neg_risk (line 1810) | def test_create_order_sell_0_1_neg_risk(self):
    method test_create_order_sell_0_01_neg_risk (line 1879) | def test_create_order_sell_0_01_neg_risk(self):
    method test_create_order_sell_0_001_neg_risk (line 1948) | def test_create_order_sell_0_001_neg_risk(self):
    method test_create_order_sell_0_0001_neg_risk (line 2017) | def test_create_order_sell_0_0001_neg_risk(self):
    method test_dict_order_buy_neg_risk (line 2086) | def test_dict_order_buy_neg_risk(self):
    method test_dict_order_sell_neg_risk (line 2154) | def test_dict_order_sell_neg_risk(self):
    method test_dict_order_buy (line 2222) | def test_dict_order_buy(self):
    method test_dict_order_sell (line 2290) | def test_dict_order_sell(self):
    method test_create_market_order_buy_0_1 (line 2358) | def test_create_market_order_buy_0_1(self):
    method test_create_market_order_buy_0_01 (line 2426) | def test_create_market_order_buy_0_01(self):
    method test_create_market_order_buy_0_001 (line 2494) | def test_create_market_order_buy_0_001(self):
    method test_create_market_order_buy_0_0001 (line 2562) | def test_create_market_order_buy_0_0001(self):
    method test_create_market_order_buy_0_1_neg_risk (line 2630) | def test_create_market_order_buy_0_1_neg_risk(self):
    method test_create_market_order_buy_0_01_neg_risk (line 2698) | def test_create_market_order_buy_0_01_neg_risk(self):
    method test_create_market_order_buy_0_001_neg_risk (line 2766) | def test_create_market_order_buy_0_001_neg_risk(self):
    method test_create_market_order_buy_0_0001_neg_risk (line 2834) | def test_create_market_order_buy_0_0001_neg_risk(self):
    method test_create_market_order_sell_0_1 (line 2902) | def test_create_market_order_sell_0_1(self):
    method test_create_market_order_sell_0_01 (line 2970) | def test_create_market_order_sell_0_01(self):
    method test_create_market_order_sell_0_001 (line 3038) | def test_create_market_order_sell_0_001(self):
    method test_create_market_order_sell_0_0001 (line 3106) | def test_create_market_order_sell_0_0001(self):
    method test_create_market_order_sell_0_1_neg_risk (line 3174) | def test_create_market_order_sell_0_1_neg_risk(self):
    method test_create_market_order_sell_0_01_neg_risk (line 3242) | def test_create_market_order_sell_0_01_neg_risk(self):
    method test_create_market_order_sell_0_001_neg_risk (line 3310) | def test_create_market_order_sell_0_001_neg_risk(self):
    method test_create_market_order_sell_0_0001_neg_risk (line 3378) | def test_create_market_order_sell_0_0001_neg_risk(self):

FILE: tests/order_builder/test_helpers.py
  class TestHelpers (line 6) | class TestHelpers(TestCase):
    method test_decimal_places (line 7) | def test_decimal_places(self):

FILE: tests/rfq/test_rfq_payload.py
  class TestCreateAcceptQuotePayload (line 7) | class TestCreateAcceptQuotePayload(TestCase):
    method test_complementary (line 9) | def test_complementary(self):
    method test_mint_merge (line 42) | def test_mint_merge(self):

FILE: tests/rfq/test_rfq_query_params.py
  class TestRfqQueryParams (line 11) | class TestRfqQueryParams(TestCase):
    method test_get_rfq_requests_request_ids_are_repeated_query_params (line 12) | def test_get_rfq_requests_request_ids_are_repeated_query_params(self):
    method test_get_rfq_quotes_quote_ids_are_repeated_query_params (line 22) | def test_get_rfq_quotes_quote_ids_are_repeated_query_params(self):

FILE: tests/signing/test_eip712.py
  class TestEIP712 (line 13) | class TestEIP712(TestCase):
    method test_sign_clob_auth_message (line 14) | def test_sign_clob_auth_message(self):

FILE: tests/signing/test_hmac.py
  class TestHMAC (line 7) | class TestHMAC(TestCase):
    method setUp (line 8) | def setUp(self):
    method test_build_hmac_signature_matches_expected (line 19) | def test_build_hmac_signature_matches_expected(self):
    method test_dict_body_same_as_equivalent_string_body (line 27) | def test_dict_body_same_as_equivalent_string_body(self):
    method test_different_method_changes_signature (line 35) | def test_different_method_changes_signature(self):
    method test_different_timestamp_changes_signature (line 45) | def test_different_timestamp_changes_signature(self):
    method test_different_path_changes_signature (line 51) | def test_different_path_changes_signature(self):
    method test_extra_key_and_ordering_affect_signature (line 61) | def test_extra_key_and_ordering_affect_signature(self):
    method test_array_body_signature (line 79) | def test_array_body_signature(self):
    method test_unicode_body_signature (line 86) | def test_unicode_body_signature(self):
    method test_none_body_omitted_from_message (line 93) | def test_none_body_omitted_from_message(self):
    method test_empty_string_body_omitted (line 99) | def test_empty_string_body_omitted(self):
    method test_none_and_empty_body_same_signature (line 105) | def test_none_and_empty_body_same_signature(self):
    method test_idempotent_same_inputs (line 114) | def test_idempotent_same_inputs(self):
    method test_different_secret_changes_signature (line 123) | def test_different_secret_changes_signature(self):
    method test_invalid_secret_raises (line 133) | def test_invalid_secret_raises(self):
    method test_boolean_body_signature (line 143) | def test_boolean_body_signature(self):
    method test_numeric_body_signature (line 154) | def test_numeric_body_signature(self):
    method test_nested_object_no_sorting_means_signatures_can_differ (line 165) | def test_nested_object_no_sorting_means_signatures_can_differ(self):
    method test_list_of_dicts_key_order_affects_signature (line 176) | def test_list_of_dicts_key_order_affects_signature(self):
    method test_string_body_preserved_verbatim (line 187) | def test_string_body_preserved_verbatim(self):

FILE: tests/test_utilities.py
  class TestUtilities (line 21) | class TestUtilities(TestCase):
    method test_parse_raw_orderbook_summary (line 22) | def test_parse_raw_orderbook_summary(self):
    method test_generate_orderbook_summary_hash (line 114) | def test_generate_orderbook_summary_hash(self):
    method test_order_to_json_0_1 (line 196) | def test_order_to_json_0_1(self):
    method test_order_to_json_0_01 (line 364) | def test_order_to_json_0_01(self):
    method test_order_to_json_0_001 (line 530) | def test_order_to_json_0_001(self):
    method test_order_to_json_0_0001 (line 696) | def test_order_to_json_0_0001(self):
    method test_order_to_json_0_1_neg_risk (line 862) | def test_order_to_json_0_1_neg_risk(self):
    method test_order_to_json_0_01_neg_risk (line 1028) | def test_order_to_json_0_01_neg_risk(self):
    method test_order_to_json_0_001_neg_risk (line 1194) | def test_order_to_json_0_001_neg_risk(self):
    method test_order_to_json_0_0001_neg_risk (line 1360) | def test_order_to_json_0_0001_neg_risk(self):
    method test_is_tick_size_smaller (line 1526) | def test_is_tick_size_smaller(self):
    method test_price_valid (line 1551) | def test_price_valid(self):
Condensed preview — 110 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (402K chars).
[
  {
    "path": ".editorconfig",
    "chars": 293,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_style = space\ntrim_trailing_whitespace = true\ninsert_final_newl"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 132,
    "preview": "# All PRs on any file must be reviewed by one of the following team members\n# Wildcard (*) for all files\n* @Polymarket/e"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 3401,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 1275,
    "preview": "## python-order-utils Monorepo Contribution Guide\n\nAll contributions to the python-order-utils are welcome and greatly a"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 584,
    "preview": "<!-- Please use tags to indicate weather this issue is a bug, new feature, or discussion. -->\n<!-- Omit and add sections"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1458,
    "preview": "<!-- Delete any sub-sections not used rather than leaving them empty. -->\n\n## Overview\n\n<!-- Provide a brief (1-3 senten"
  },
  {
    "path": ".github/workflows/release.yaml",
    "chars": 1058,
    "preview": "name: Release\n\non:\n  release:\n    types: [published]\n\npermissions:\n  id-token: write\n\njobs:\n  release:\n    name: release"
  },
  {
    "path": ".github/workflows/workflow.yaml",
    "chars": 568,
    "preview": "name: Test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n\npermissions:\n  contents: read\n  pull-requests: read\n\njobs:"
  },
  {
    "path": ".gitignore",
    "chars": 1806,
    "preview": ".idea/\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / "
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 346,
    "preview": "### Contributing\n\nThanks for helping improve `py-clob-client`.\n\n- Fork the repo and create a feature branch.\n- Create fo"
  },
  {
    "path": "LICENSE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) 2022 Polymarket\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
  },
  {
    "path": "Makefile",
    "chars": 73,
    "preview": "init:\n\tpip install -r requirements.txt\n\ntest:\n\tpytest -s\n\nfmt:\n\tblack ./."
  },
  {
    "path": "README.md",
    "chars": 9544,
    "preview": "# Polymarket Python CLOB Client\n\n<a href='https://pypi.org/project/py-clob-client'>\n    <img src='https://img.shields.io"
  },
  {
    "path": "SECURITY.md",
    "chars": 369,
    "preview": "### Security\n\nIf you believe you’ve found a security vulnerability, please email security@polymarket.com. Do not open a "
  },
  {
    "path": "examples/GTD_order.py",
    "chars": 1092,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, OrderArgs, Order"
  },
  {
    "path": "examples/are_orders_scoring.py",
    "chars": 837,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, OrdersScoringPar"
  },
  {
    "path": "examples/cancel_all.py",
    "chars": 638,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/cancel_market_orders.py",
    "chars": 678,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/cancel_order.py",
    "chars": 651,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/cancel_orders.py",
    "chars": 651,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/create_api_key.py",
    "chars": 375,
    "preview": "from dotenv import load_dotenv\nimport os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.constants imp"
  },
  {
    "path": "examples/create_readonly_api_key.py",
    "chars": 651,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/delete_readonly_api_key.py",
    "chars": 789,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/derive_api_key.py",
    "chars": 375,
    "preview": "from dotenv import load_dotenv\nimport os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.constants imp"
  },
  {
    "path": "examples/drop_notifications.py",
    "chars": 680,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\n\nfrom py_clob_client.clob_types import ApiCreds, DropNotificatio"
  },
  {
    "path": "examples/get_api_keys.py",
    "chars": 605,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/get_balance_allowance.py",
    "chars": 1267,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, BalanceAllowance"
  },
  {
    "path": "examples/get_builder_trades.py",
    "chars": 1059,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, TradeParams\nfrom"
  },
  {
    "path": "examples/get_closed_only_mode.py",
    "chars": 613,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/get_last_trade_price.py",
    "chars": 538,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom dotenv import load_dotenv\nfrom pprint import pprint\n\nfrom p"
  },
  {
    "path": "examples/get_last_trades_prices.py",
    "chars": 816,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import BookParams\nfrom dotenv imp"
  },
  {
    "path": "examples/get_market_trades_events.py",
    "chars": 649,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/get_markets.py",
    "chars": 803,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/get_mid_market_price.py",
    "chars": 754,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/get_mid_markets_prices.py",
    "chars": 972,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, BookParams\nfrom "
  },
  {
    "path": "examples/get_notifications.py",
    "chars": 610,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\n\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impo"
  },
  {
    "path": "examples/get_ok.py",
    "chars": 204,
    "preview": "from py_clob_client.client import ClobClient\n\n\ndef main():\n    host = os.getenv(\"CLOB_API_URL\", \"https://clob.polymarket"
  },
  {
    "path": "examples/get_open_orders_with_readonly_key.py",
    "chars": 973,
    "preview": "import os\n\nimport httpx\nfrom dotenv import load_dotenv\n\nfrom py_clob_client.endpoints import ORDERS\n\n\nload_dotenv()\n\n\nde"
  },
  {
    "path": "examples/get_order.py",
    "chars": 717,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/get_orderbook.py",
    "chars": 474,
    "preview": "from py_clob_client.client import ClobClient\nimport os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n\ndef main():\n    h"
  },
  {
    "path": "examples/get_orderbooks.py",
    "chars": 607,
    "preview": "from py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import BookParams\n\n\ndef main():\n    host = o"
  },
  {
    "path": "examples/get_orders.py",
    "chars": 782,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, OpenOrderParams\n"
  },
  {
    "path": "examples/get_price.py",
    "chars": 390,
    "preview": "from py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import BookParams\n\n\ndef main():\n    host = o"
  },
  {
    "path": "examples/get_prices.py",
    "chars": 1008,
    "preview": "from py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import BookParams\n\n\ndef main():\n    host = o"
  },
  {
    "path": "examples/get_readonly_api_keys.py",
    "chars": 649,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/get_server_time.py",
    "chars": 203,
    "preview": "from py_clob_client.client import ClobClient\n\n\ndef main():\n    host = os.getenv(\"CLOB_API_URL\", \"https://clob.polymarket"
  },
  {
    "path": "examples/get_spread.py",
    "chars": 730,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom dotenv impor"
  },
  {
    "path": "examples/get_spreads.py",
    "chars": 603,
    "preview": "from py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import BookParams\n\n\ndef main():\n    host = o"
  },
  {
    "path": "examples/get_trades.py",
    "chars": 850,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, TradeParams\nfrom"
  },
  {
    "path": "examples/is_order_scoring.py",
    "chars": 800,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, OrderScoringPara"
  },
  {
    "path": "examples/market_buy_order.py",
    "chars": 1081,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, MarketOrderArgs,"
  },
  {
    "path": "examples/market_sell_order.py",
    "chars": 1086,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, MarketOrderArgs,"
  },
  {
    "path": "examples/order.py",
    "chars": 1058,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, OrderArgs\nfrom d"
  },
  {
    "path": "examples/orders.py",
    "chars": 1941,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, OrderArgs, PostO"
  },
  {
    "path": "examples/place_builder_order.py",
    "chars": 1332,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, OrderArgs\nfrom d"
  },
  {
    "path": "examples/post_heartbeat.py",
    "chars": 1033,
    "preview": "import os\nimport time\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom "
  },
  {
    "path": "examples/post_only_order.py",
    "chars": 1034,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, OrderArgs\nfrom d"
  },
  {
    "path": "examples/rfq_accept_quote.py",
    "chars": 1133,
    "preview": "import os\nimport time\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom "
  },
  {
    "path": "examples/rfq_approve_order.py",
    "chars": 1133,
    "preview": "import os\nimport time\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom "
  },
  {
    "path": "examples/rfq_cancel_quote.py",
    "chars": 818,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom py_clob_clie"
  },
  {
    "path": "examples/rfq_cancel_request.py",
    "chars": 826,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom py_clob_clie"
  },
  {
    "path": "examples/rfq_config.py",
    "chars": 677,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom py_clob_clie"
  },
  {
    "path": "examples/rfq_create_quote.py",
    "chars": 1118,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom py_clob_clie"
  },
  {
    "path": "examples/rfq_create_request.py",
    "chars": 1133,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, PartialCreateOrd"
  },
  {
    "path": "examples/rfq_full_flow.py",
    "chars": 7525,
    "preview": "\"\"\"\nRFQ Full Flow Example\n\nThis script demonstrates the complete RFQ (Request for Quote) flow between two parties.\nFor a"
  },
  {
    "path": "examples/rfq_get_best_quote.py",
    "chars": 875,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom py_clob_clie"
  },
  {
    "path": "examples/rfq_get_quotes.py",
    "chars": 1564,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom py_clob_clie"
  },
  {
    "path": "examples/rfq_get_requests.py",
    "chars": 1160,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds\nfrom py_clob_clie"
  },
  {
    "path": "examples/update_balance_allowance.py",
    "chars": 1231,
    "preview": "import os\n\nfrom py_clob_client.client import ClobClient\nfrom py_clob_client.clob_types import ApiCreds, BalanceAllowance"
  },
  {
    "path": "py_clob_client/__init__.py",
    "chars": 1521,
    "preview": "from .client import ClobClient\nfrom .clob_types import (\n    ApiCreds,\n    OrderArgs,\n    MarketOrderArgs,\n    OrderType"
  },
  {
    "path": "py_clob_client/client.py",
    "chars": 36483,
    "preview": "import logging\nimport json\nimport time\nfrom typing import Optional\n\nfrom py_builder_signing_sdk.config import BuilderCon"
  },
  {
    "path": "py_clob_client/clob_types.py",
    "chars": 4338,
    "preview": "from typing import Any\nfrom dataclasses import dataclass, asdict\nfrom json import dumps\nfrom typing import Literal, Opti"
  },
  {
    "path": "py_clob_client/config.py",
    "chars": 1473,
    "preview": "from .clob_types import ContractConfig\n\n\ndef get_contract_config(chainID: int, neg_risk: bool = False) -> ContractConfig"
  },
  {
    "path": "py_clob_client/constants.py",
    "chars": 555,
    "preview": "# Access levels\nL0 = 0\nL1 = 1\nL2 = 2\n\n\nCREDENTIAL_CREATION_WARNING = \"\"\"🚨🚨🚨\nYour credentials CANNOT be recovered after t"
  },
  {
    "path": "py_clob_client/endpoints.py",
    "chars": 2080,
    "preview": "TIME = \"/time\"\nCREATE_API_KEY = \"/auth/api-key\"\nGET_API_KEYS = \"/auth/api-keys\"\nDELETE_API_KEY = \"/auth/api-key\"\nDERIVE_"
  },
  {
    "path": "py_clob_client/exceptions.py",
    "chars": 854,
    "preview": "from typing import Optional\n\nimport httpx\n\n\nclass PolyException(Exception):\n    def __init__(self, msg):\n        self.ms"
  },
  {
    "path": "py_clob_client/headers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "py_clob_client/headers/headers.py",
    "chars": 1868,
    "preview": "from ..clob_types import ApiCreds, RequestArgs\nfrom ..signing.hmac import build_hmac_signature\nfrom ..signer import Sign"
  },
  {
    "path": "py_clob_client/http_helpers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "py_clob_client/http_helpers/helpers.py",
    "chars": 6094,
    "preview": "import httpx\n\nfrom py_clob_client.clob_types import (\n    DropNotificationParams,\n    BalanceAllowanceParams,\n    OrderS"
  },
  {
    "path": "py_clob_client/order_builder/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "py_clob_client/order_builder/builder.py",
    "chars": 7944,
    "preview": "from py_order_utils.builders import OrderBuilder as UtilsOrderBuilder\nfrom py_order_utils.signer import Signer as UtilsS"
  },
  {
    "path": "py_clob_client/order_builder/constants.py",
    "chars": 26,
    "preview": "BUY = \"BUY\"\nSELL = \"SELL\"\n"
  },
  {
    "path": "py_clob_client/order_builder/helpers.py",
    "chars": 629,
    "preview": "from math import floor, ceil\nfrom decimal import Decimal\n\n\ndef round_down(x: float, sig_digits: int) -> float:\n    retur"
  },
  {
    "path": "py_clob_client/rfq/__init__.py",
    "chars": 1335,
    "preview": "from .rfq_types import (\n    # Input types\n    RfqUserRequest,\n    RfqUserQuote,\n    CreateRfqRequestParams,\n    CreateR"
  },
  {
    "path": "py_clob_client/rfq/rfq_client.py",
    "chars": 24510,
    "preview": "\"\"\"\nRFQ (Request for Quote) client for the Polymarket CLOB API.\n\nThis module provides the RfqClient class which handles "
  },
  {
    "path": "py_clob_client/rfq/rfq_helpers.py",
    "chars": 4532,
    "preview": "\"\"\"\nRFQ helper functions for the Polymarket CLOB API.\n\nThis module provides utility functions for RFQ operations includi"
  },
  {
    "path": "py_clob_client/rfq/rfq_types.py",
    "chars": 9880,
    "preview": "\"\"\"\nRFQ (Request for Quote) data types for the Polymarket CLOB API.\n\nThis module defines all input and response types us"
  },
  {
    "path": "py_clob_client/signer.py",
    "chars": 583,
    "preview": "from eth_account import Account\n\n\nclass Signer:\n    def __init__(self, private_key: str, chain_id: int):\n        assert "
  },
  {
    "path": "py_clob_client/signing/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "py_clob_client/signing/eip712.py",
    "chars": 877,
    "preview": "from poly_eip712_structs import make_domain\nfrom eth_utils import keccak\nfrom py_order_utils.utils import prepend_zx\n\nfr"
  },
  {
    "path": "py_clob_client/signing/hmac.py",
    "chars": 708,
    "preview": "import hmac\nimport hashlib\nimport base64\n\n\ndef build_hmac_signature(\n    secret: str, timestamp: str, method: str, reque"
  },
  {
    "path": "py_clob_client/signing/model.py",
    "chars": 191,
    "preview": "from poly_eip712_structs import EIP712Struct, Address, String, Uint\n\n\nclass ClobAuth(EIP712Struct):\n    address = Addres"
  },
  {
    "path": "py_clob_client/utilities.py",
    "chars": 2388,
    "preview": "import hashlib\nimport json\n\nfrom .clob_types import OrderBookSummary, OrderSummary, TickSize\n\n\ndef parse_raw_orderbook_s"
  },
  {
    "path": "requirements.txt",
    "chars": 206,
    "preview": "black==24.4.2\neth-account===0.13.0\neth-utils===4.1.1\npoly_eip712_structs==0.0.1\npy_order_utils==0.3.2\npytest==8.2.2\npyth"
  },
  {
    "path": "setup.py",
    "chars": 1157,
    "preview": "import setuptools\n\nwith open(\"README.md\", \"r\", encoding=\"utf-8\") as fh:\n    long_description = fh.read()\n\nsetuptools.set"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/headers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/headers/test_headers.py",
    "chars": 3252,
    "preview": "from datetime import datetime\nfrom unittest import TestCase\nfrom py_clob_client.clob_types import ApiCreds, RequestArgs\n"
  },
  {
    "path": "tests/http_helpers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/http_helpers/test_helpers.py",
    "chars": 4080,
    "preview": "from unittest import TestCase\nfrom py_clob_client.clob_types import (\n    TradeParams,\n    OpenOrderParams,\n    DropNoti"
  },
  {
    "path": "tests/order_builder/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/order_builder/test_builder.py",
    "chars": 103650,
    "preview": "from unittest import TestCase\n\nfrom py_clob_client.clob_types import (\n    OrderArgs,\n    MarketOrderArgs,\n    CreateOrd"
  },
  {
    "path": "tests/order_builder/test_helpers.py",
    "chars": 274,
    "preview": "from unittest import TestCase\n\nfrom py_clob_client.order_builder.helpers import decimal_places\n\n\nclass TestHelpers(TestC"
  },
  {
    "path": "tests/rfq/test_rfq_payload.py",
    "chars": 2322,
    "preview": "from unittest import TestCase\n\nfrom py_clob_client.rfq.rfq_client import RfqClient\nfrom py_clob_client.rfq.rfq_types imp"
  },
  {
    "path": "tests/rfq/test_rfq_query_params.py",
    "chars": 1076,
    "preview": "from unittest import TestCase\nfrom urllib.parse import urlencode\n\nfrom py_clob_client.rfq import GetRfqRequestsParams, G"
  },
  {
    "path": "tests/signing/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/signing/test_eip712.py",
    "chars": 753,
    "preview": "from unittest import TestCase\nfrom py_clob_client.constants import AMOY\n\nfrom py_clob_client.signer import Signer\nfrom p"
  },
  {
    "path": "tests/signing/test_hmac.py",
    "chars": 7642,
    "preview": "from unittest import TestCase\nimport binascii\n\nfrom py_clob_client.signing.hmac import build_hmac_signature\n\n\nclass Test"
  },
  {
    "path": "tests/test_utilities.py",
    "chars": 66190,
    "preview": "from unittest import TestCase\n\nfrom py_clob_client.clob_types import (\n    OrderArgs,\n    OrderType,\n    CreateOrderOpti"
  }
]

About this extraction

This page contains the full source code of the Polymarket/py-clob-client GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 110 files (365.5 KB), approximately 92.2k tokens, and a symbol index with 356 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!