DEV Community

Cover image for Polymarket Trading Bot: Build a Market Maker Bot in Python- Project Setup and API Integration
Benjamin-Cup
Benjamin-Cup

Posted on

Polymarket Trading Bot: Build a Market Maker Bot in Python- Project Setup and API Integration

Day 3 of 7: Building a Polymarket Market Maker Bot From Zero to Complete

In the first two parts of this series, we explored what a Polymarket market maker bot is and how the Polymarket order book works.

We covered YES and NO outcome tokens, bid-ask spreads, market depth, limit orders, and the risks of providing liquidity.

Now, let's start building.

In this tutorial, we'll set up a Python project, connect to Polymarket's public market data API, discover available markets, retrieve outcome asset identifiers, and inspect an order book.

By the end, you'll have the foundation of a Python market maker bot that can read market data and calculate basic pricing metrics.

Important: This tutorial is read-only. It does not submit orders, spend funds, or require a private key.

What We'll Build

Our first version will perform five tasks:

  1. Connect to Polymarket's public API.
  2. Discover available markets.
  3. Identify a market's YES and NO assets.
  4. Retrieve the order book for a selected asset.
  5. Calculate the best bid, best ask, spread, and midpoint.

The data flow looks like this:

Polymarket Market Data
        |
        v
   Market Discovery
        |
        v
    Select a Market
        |
        v
  Identify YES / NO Assets
        |
        v
    Fetch Order Book
        |
        v
 Calculate Spread & Midpoint
        |
        v
      Print Results
Enter fullscreen mode Exit fullscreen mode

This is a small program, but it establishes an important component of every trading bot: the market data layer.

polymarket trading bot

A production market maker would extend this foundation with real-time data subscriptions, strategy logic, order management, inventory tracking, risk controls, and monitoring.

1. Choose the Python SDK

Polymarket provides developer interfaces for market discovery, order books, pricing, and trading.

For this tutorial, we'll use the unified Python SDK identified in the project references:

Official repository: Polymarket Python SDK

The package used in the examples is polymarket-client.

The SDK is intended to provide access to market information and other Polymarket workflows. Public market-data access does not require you to configure trading credentials for this read-only exercise.

Before installing it, check the repository's current installation instructions and examples. SDK interfaces can change, and code written for an older client may not work with the current version.

2. Set Up Your Development Environment

You can follow this tutorial on Windows, macOS, or Linux.

Check Python

python --version
Enter fullscreen mode Exit fullscreen mode

On some systems, use:

python3 --version
Enter fullscreen mode Exit fullscreen mode

Create a project directory

mkdir polymarket-market-maker
cd polymarket-market-maker
Enter fullscreen mode Exit fullscreen mode

Create a virtual environment

python -m venv .venv
Enter fullscreen mode Exit fullscreen mode

Activate it on Windows PowerShell:

.venv\Scripts\Activate.ps1
Enter fullscreen mode Exit fullscreen mode

On macOS or Linux:

source .venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Install the SDK

python -m pip install --upgrade pip
pip install polymarket-client
Enter fullscreen mode Exit fullscreen mode

Verify the import:

python -c "from polymarket import PublicClient; print('SDK import successful')"
Enter fullscreen mode Exit fullscreen mode

If the import fails, check the SDK's current Python requirements and installation instructions before proceeding.

3. Create a Clean Project Structure

A common beginner mistake is putting market discovery, pricing calculations, and trading logic into one large Python file.

Let's separate responsibilities from the beginning.

polymarket-market-maker/
├── src/
│   └── polymarket_mm/
│       ├── __init__.py
│       ├── market_data.py
│       ├── orderbook.py
│       ├── pricing.py
│       └── main.py
├── tests/
├── .gitignore
└── README.md
Enter fullscreen mode Exit fullscreen mode

Each module has a specific responsibility:

  • market_data.py — discovers markets and retrieves market information.
  • orderbook.py — retrieves and inspects order book levels.
  • pricing.py — calculates spread and midpoint.
  • main.py — connects the modules and runs the example.
  • tests/ — contains tests for pricing calculations and other logic.

This structure is intentionally simple. We don't need a complex architecture before our first API request works.

4. Discover Markets Through the SDK

Before inspecting an order book, the bot needs to discover which markets are available.

Create src/polymarket_mm/market_data.py:

from polymarket import PublicClient


def list_markets(limit: int = 10):
    """Return a page of markets from Polymarket."""

    with PublicClient() as client:
        page = client.list_markets(
            closed=False,
            page_size=limit,
        ).first_page()

        return page.items


if __name__ == "__main__":
    markets = list_markets()

    for market in markets:
        print(
            {
                "question": market.question,
                "slug": market.slug,
                "market_id": market.id,
            }
        )
Enter fullscreen mode Exit fullscreen mode

Run the module from your project root.

On macOS or Linux:

PYTHONPATH=src python -m polymarket_mm.market_data
Enter fullscreen mode Exit fullscreen mode

On Windows PowerShell:

$env:PYTHONPATH = "src"
python -m polymarket_mm.market_data
Enter fullscreen mode Exit fullscreen mode

The program requests a page of markets and prints basic information about each result.

One important detail: closed=False does not necessarily mean a market is currently accepting orders. A production bot should separately validate market status, trading permissions, and the availability of a usable order book.

For the current implementation, consult the official market discovery example.

5. Understand Market IDs and Asset IDs

A market and a tradable asset are not the same thing.

A market represents an event or question.

An asset identifier identifies a tradable outcome or position associated with that market.

For a conventional binary market, there are typically YES and NO outcomes. However, market infrastructure can evolve, so avoid assuming every market exposes its tradable identifiers in exactly the same format.

The SDK's market model may expose a token ID, position ID, or another identifier depending on the market representation.

The key principle is simple:

Use the current SDK's market model and official examples instead of extracting identifiers from URLs or relying on undocumented raw JSON structures.

You can review the SDK's market helper examples to understand how outcome identifiers are accessed.

6. Retrieve an Order Book

Now let's inspect an actual market.

The following example searches a limited page of open markets until it finds one with a usable YES asset identifier. It then requests the corresponding order book.

Create src/polymarket_mm/orderbook.py:

from polymarket import PublicClient


def inspect_first_available_market():
    with PublicClient() as client:
        markets = client.list_markets(
            closed=False,
            page_size=100,
        ).first_page().items

        for market in markets:
            # Conventional binary-market example.
            # Check the SDK model for your market version.
            yes = market.outcomes.yes

            asset_id = yes.token_id or yes.position_id

            if asset_id is None:
                continue

            try:
                book = client.get_order_book(
                    asset_id=asset_id
                )
            except Exception as exc:
                print(
                    f"Skipping market {market.id}: "
                    f"could not retrieve its book: {exc}"
                )
                continue

            if not book.bids and not book.asks:
                continue

            return market, asset_id, book

    raise RuntimeError(
        "No market with a usable YES order book was found. "
        "Try again or refine market discovery."
    )


def main():
    market, asset_id, book = inspect_first_available_market()

    print("Market:", market.question)
    print("Market ID:", market.id)
    print("Asset ID:", asset_id)
    print("Number of bids:", len(book.bids))
    print("Number of asks:", len(book.asks))

    print("\nTop bids:")
    for level in sorted(
        book.bids,
        key=lambda item: item.price,
        reverse=True,
    )[:5]:
        print("Price:", level.price, "Size:", level.size)

    print("\nTop asks:")
    for level in sorted(
        book.asks,
        key=lambda item: item.price,
    )[:5]:
        print("Price:", level.price, "Size:", level.size)


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Run it:

PYTHONPATH=src python -m polymarket_mm.orderbook
Enter fullscreen mode Exit fullscreen mode

On Windows PowerShell, use the PYTHONPATH command from the previous section.

The program prints the market question, asset identifier, number of bid and ask levels, and the five best levels on each side.

A few things to remember:

  • The best bid is the highest available buy price.
  • The best ask is the lowest available sell price.
  • The size indicates the quantity available at a price level.
  • An empty order book does not provide enough information to calculate both sides of the spread.

The code is an educational starting point, not a guarantee that every market returned by the API will expose these exact fields. Verify the current SDK model before running it.

For another reference, see the official market prices example.

7. Calculate the Spread and Midpoint

Once we have the order book, we can calculate two basic market metrics.

Bid-ask spread

The spread measures the difference between the best ask and the best bid.

[
\text{Spread} = \text{Best Ask} - \text{Best Bid}
]

For example, suppose the best bid is $0.48 and the best ask is $0.52.

The spread is:

0.52 - 0.48 = 0.04
Enter fullscreen mode Exit fullscreen mode

The spread is four cents per share.

Midpoint

The midpoint is the average of the best bid and best ask.

[
\text{Midpoint} =
\frac{\text{Best Bid}+\text{Best Ask}}{2}
]

Using the same example:

(0.48 + 0.52) / 2 = 0.50
Enter fullscreen mode Exit fullscreen mode

The midpoint is $0.50.

Important: The midpoint is a reference price, not a guaranteed execution price. A market order or limit order may execute at a different price depending on liquidity and market conditions.

Implement the calculations

Create src/polymarket_mm/pricing.py:

def calculate_book_metrics(bids, asks):
    """Calculate basic metrics from order book levels."""

    best_bid = max(
        (float(level.price) for level in bids),
        default=None,
    )

    best_ask = min(
        (float(level.price) for level in asks),
        default=None,
    )

    if best_bid is None or best_ask is None:
        return {
            "best_bid": best_bid,
            "best_ask": best_ask,
            "spread": None,
            "midpoint": None,
        }

    if best_bid > best_ask:
        raise ValueError(
            "The book appears crossed; inspect the data "
            "and API response before using it."
        )

    return {
        "best_bid": best_bid,
        "best_ask": best_ask,
        "spread": best_ask - best_bid,
        "midpoint": (best_bid + best_ask) / 2,
    }
Enter fullscreen mode Exit fullscreen mode

You can import this function into the order book example:

from polymarket_mm.pricing import calculate_book_metrics

metrics = calculate_book_metrics(
    book.bids,
    book.asks,
)

print("\nBook metrics:")
for name, value in metrics.items():
    print(f"{name}: {value}")
Enter fullscreen mode Exit fullscreen mode

For financial calculations and order construction, prefer Decimal or the SDK's decimal-compatible types over binary floating-point arithmetic.

The example uses float for simplicity and educational display.

8. Add Error Handling and Logging

Real-world API requests can fail.

Common causes include:

  • Network timeouts.
  • Rate limits.
  • Invalid asset identifiers.
  • Temporary service errors.
  • Disconnected WebSocket connections.
  • Missing or stale market data.

Different errors require different responses.

A timeout may justify a controlled retry. A rate-limit response should trigger appropriate backoff. An invalid identifier may indicate a market-selection problem.

Stale data is particularly important for a market maker. If the order book is outdated, the bot should not continue generating quotes as if the information were current.

Start with Python's standard logging module:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
)

logger = logging.getLogger(__name__)

logger.info("Market data module started")
Enter fullscreen mode Exit fullscreen mode

As the project grows, structured logs will help investigate market data updates, order submissions, fills, cancellations, and risk-limit events.

Avoid infinite retry loops. A robust bot should stop making trading decisions when its data cannot be trusted.

9. Why We Are Not Placing Orders Yet

At this stage, our program can retrieve market information, but it is not yet a market maker.

It does not know:

  • What prices it should quote.
  • How large its orders should be.
  • When it should cancel or replace orders.
  • How to manage inventory.
  • How to control exposure and capital usage.
  • How to reconcile partial fills and failed requests.

Adding live order placement before implementing these components would be premature.

A safer development sequence is:

  1. Retrieve market data reliably.
  2. Calculate candidate quotes.
  3. Validate quotes against price and risk limits.
  4. Simulate decisions using recorded or live market data.
  5. Implement order lifecycle management.
  6. Test failures, cancellations, and partial fills.
  7. Consider controlled live testing only after sufficient validation.

Separating market data from order execution makes the bot easier to test and reduces the risk of an early coding mistake spending real funds.

10. What We Built Today

By completing Day 3, we have established the initial market data layer.

We now have a foundation for:

  • Installing and importing the Python SDK.
  • Discovering open markets.
  • Identifying a usable YES asset identifier.
  • Retrieving an order book.
  • Inspecting bid and ask levels.
  • Calculating spread and midpoint metrics.
  • Recognizing the importance of API errors and stale data.

This is the first step toward a more complete market maker bot.

Day 4: Design the Market-Making Strategy

In Day 4, we'll move from observing the market to generating candidate quotes.

We'll explore:

  • How to select a reference price.
  • How to calculate bid and ask quotes.
  • How to set a target spread.
  • How to choose order sizes.
  • How to adjust quotes based on inventory.
  • Why fixed-distance-from-midpoint strategies can perform poorly during rapid price movements.

The objective is to build a strategy layer that can be tested before connecting it to live order execution.

Follow the Project

GitHub repository: Polymarket Trading Bot in Python

YouTube tutorial: Watch the video

Contact and discussion: Telegram — BenjaminCup

Follow the series as we build a Polymarket market maker bot from the fundamentals toward a complete Python implementation.

Python #Polymarket #TradingBot #AlgoTrading #Crypto

Top comments (0)