← Back to All Tutorials

Tutorial 04: Polymarket 5-Minute Bot (Easy Hyper Gambler) Beginner-Friendly

Table of Contents
  1. What Does This Bot Do?
  2. Key Concepts
  3. Keyboard Controls
  4. Code Walkthrough
  5. The Math Behind It
  6. Dependencies
  7. Glossary

1. What Does This Bot Do?

This is the simplest Polymarket BTC 5-minute market bot. It's keyboard-driven - you press a key to bet, and the bot handles the rest. There are 288 five-minute markets every day (24 hours x 12 per hour), giving you tons of betting opportunities.

Simple Analogy: Think of this as a casino roulette table for crypto. Every 5 minutes, a new "spin" happens - BTC either goes up or down. You press B to bet on UP, S to bet on DOWN, and X to cash out early. The bot automatically places your bet at a discount below market price, tracks your wins/losses, and shows you a colorful dashboard.

2. Key Concepts

Bid Discount

The bot doesn't buy at the market price - it places orders at a discount (default 10%) below the current best bid. This is like asking for a deal: "The market says UP costs $0.55, but I'll only pay $0.495."

Why? Because in binary options, every cent of entry price matters. If you can consistently buy cheaper than the "fair" price, even a small edge compounds over hundreds of bets.

5-Minute Market Window

Each market lasts exactly 5 minutes. At the end, BTC's price is compared to where it was at the start. If BTC's closing price is higher than the opening price, UP wins. Otherwise, DOWN wins.

P&L Tracking

The bot tracks your session performance: total wins, losses, net P&L in dollars, and shows motivational messages.

3. Keyboard Controls

KeyActionWhat Happens
BBet UPPlaces a discounted BUY order for the UP (NO) token
SBet DOWNPlaces a discounted BUY order for the DOWN (YES) token
XClose EarlySells your position at current market price before market ends
Ctrl+CExit BotCancels all orders and shuts down

4. Code Walkthrough

1 Terminal Setup - Raw Input Mode

import tty, termios

# Set terminal to raw mode for instant keypress detection
old_settings = termios.tcgetattr(sys.stdin)
tty.setcbreak(sys.stdin.fileno())

What it does: Switches your terminal into "raw" mode so it detects each keypress immediately, without waiting for you to press Enter. This makes the bot feel responsive - press B and it instantly places your bet.

2 State Machine

state = {
    'order_side': None,       # "UP" or "DOWN" if pending order
    'order_price': 0,
    'order_token_id': None,
    'position_side': None,    # "UP" or "DOWN" if holding
    'position_shares': 0,
    'entry_price': 0,
    'current_market_ts': None,
    'price_to_beat': None,    # BTC price at market open
}

What it does: Tracks everything about your current situation: whether you have an open order, whether you hold a position, what price you entered at, and what BTC price needs to be beaten for you to win.

3 Market Lifecycle

# When a new 5-minute market starts:
if market_ts != state['current_market_ts']:
    # 1. Count P&L from previous position
    if state['position_side']:
        pnl = (final_bid - state['entry_price']) * state['position_shares']
        SESSION_PNL += pnl

    # 2. Cancel any unfilled orders from previous market
    cancel_token_orders(state['order_token_id'])

    # 3. Reset state for new market
    state = {k: None for k in state}

    # 4. Record BTC opening price
    state['price_to_beat'] = get_btc_price()

    # 5. Find the new market
    state['market_info'] = get_market_info(market_ts)

What it does: When the 5-minute timer resets, the bot: calculates if your last bet won or lost, cancels any leftover orders, resets everything, grabs the new BTC price (this is the "price to beat"), and finds the new Polymarket market.

5. The Math Behind It

Payout Structure:

The bid discount (10%) helps: instead of buying at $0.54, you might buy at $0.486. This lowers your breakeven to ~48.6%, giving you more room for error.

6. Dependencies

pip install py-clob-client web3 termcolor python-dotenv
Note: This bot also uses nice_funcs, which is Moon Dev's custom library. You'll need that installed too. The bot only works on Mac/Linux due to the tty module (terminal raw mode).

7. Glossary

TermMeaning
Token IDA unique identifier for each outcome (UP/DOWN) on Polymarket
Best BidThe highest price someone is currently willing to pay
Best AskThe lowest price someone is currently willing to sell at
Limit OrderAn order that only executes at your specified price or better
Market OrderAn order that executes immediately at the best available price
P&LProfit and Loss - your net gain or loss
Session StatsPerformance tracking for the current run of the bot

8. Full Code: Python to Pseudo-Code Translation

Configuration & State Setup

# --- PYTHON ---
BET_SIZE_USD = 10.0
MARKET_DURATION = 300
BID_DISCOUNT_PCT = 10
SESSION_WINS = 0; SESSION_LOSSES = 0; SESSION_PNL = 0.0; SESSION_TRADES = 0
state = {
    'order_side': None, 'order_price': 0, 'order_token_id': None,
    'position_side': None, 'position_shares': 0, 'position_token_id': None,
    'entry_price': 0, 'current_market_ts': None, 'market_info': None,
    'price_to_beat': None,
}

# --- PSEUDO-CODE ---
SET bet size to $10 per bet
SET market duration to 300 seconds (5 minutes)
SET bid discount to 10% below current bid
INITIALIZE session counters: 0 wins, 0 losses, $0 P&L, 0 trades

CREATE a state tracking dictionary:
    "order_side": currently null (no pending order)
    "order_price": 0 (no price set)
    "position_side": currently null (no held position)
    "entry_price": 0 (no entry recorded)
    "current_market_ts": which 5-minute window we're in
    "price_to_beat": the BTC price at market open

Terminal Setup for Instant Keypress Detection

# --- PYTHON ---
old_settings = termios.tcgetattr(sys.stdin)
tty.setcbreak(sys.stdin.fileno())

# --- PSEUDO-CODE ---
SAVE the current terminal settings (so we can restore them later)
SWITCH terminal to "cbreak" mode:
    This means: detect each keypress IMMEDIATELY without waiting for Enter
    The bot can react the instant you press B, S, or X

Market Lifecycle (New 5-Min Window)

# --- PYTHON ---
if market_ts != state['current_market_ts']:
    if state['position_side'] and state['position_token_id']:
        current_book = up_book if state['position_side'] == "UP" else down_book
        if current_book and state['entry_price'] > 0:
            final_bid = current_book['best_bid']
            pnl = (final_bid - state['entry_price']) * state['position_shares']
            SESSION_PNL += pnl; SESSION_TRADES += 1
            if pnl >= 0: SESSION_WINS += 1
            else: SESSION_LOSSES += 1
    if state['order_side'] and state['order_token_id'] and not state['position_side']:
        cancel_token_orders(state['order_token_id'])
    state = {k: None if k != 'current_market_ts' else market_ts for k in state}
    state['price_to_beat'] = get_btc_price()
    for attempt in range(8):
        state['market_info'] = get_market_info(market_ts)
        if state['market_info']: break
        time.sleep(2)

# --- PSEUDO-CODE ---
WHEN a new 5-minute market window starts:

    IF we held a position from the previous market:
        GET the final bid price for our token
        CALCULATE P&L = (final price - our entry price) x number of shares
        ADD to session total P&L
        ADD 1 to session trade count
        IF profit >= 0: ADD 1 to session wins
        ELSE: ADD 1 to session losses

    IF we had an unfilled order from the previous market:
        CANCEL that order (it's too late now)

    RESET all state variables for the new market

    GET the current BTC price (this is the "price to beat")

    TRY up to 8 times to find the new Polymarket market:
        SEARCH for the market with slug "btc-updown-5m-{timestamp}"
        IF found: use it
        ELSE: wait 2 seconds and try again

Key Press: B = Bet UP

# --- PYTHON ---
if char == 'B':
    token_id = market_info['up_token_id']
    if state['order_token_id']:
        cancel_token_orders(state['order_token_id'])
        time.sleep(0.5)
    book = get_order_book(token_id)
    bid_price = round(book['best_bid'] * (1 - BID_DISCOUNT_PCT / 100), 4)
    shares = calculate_shares(BET_SIZE_USD, bid_price)
    resp = place_limit_order(token_id, "BUY", bid_price, shares)
    if resp and resp.get('orderID'):
        state['order_side'] = "UP"
        state['order_price'] = bid_price
        state['order_token_id'] = token_id

# --- PSEUDO-CODE ---
WHEN user presses 'B' (Bet UP):

    GET the token ID for the UP outcome
    IF we already have a pending order: CANCEL it first

    GET the current order book for this token
    CALCULATE our bid price = best current bid MINUS 10% discount
        (e.g., if best bid is $0.55, we bid $0.495)
    CALCULATE how many shares we get for $10 at our bid price

    PLACE a BUY limit order on Polymarket at our discounted price
    IF the order was accepted:
        RECORD that we have a pending UP order
        RECORD our bid price
        RECORD which token we ordered

Key Press: X = Close Early

# --- PYTHON ---
elif char == 'X':
    if not state['position_side'] or not state['position_token_id']:
        continue
    token_id = state['position_token_id']
    shares = state['position_shares']
    book = get_order_book(token_id)
    sell_price = book['best_bid']
    close_pnl = (sell_price - state['entry_price']) * shares
    SESSION_PNL += close_pnl; SESSION_TRADES += 1
    if close_pnl >= 0: SESSION_WINS += 1
    else: SESSION_LOSSES += 1
    cancel_token_orders(token_id)
    place_limit_order(token_id, "SELL", sell_price, shares)

# --- PSEUDO-CODE ---
WHEN user presses 'X' (Close position early):

    IF we don't have a position: do nothing
    GET the token ID and number of shares we hold
    GET the current order book
    SET the sell price to the current best bid (sell immediately)

    CALCULATE P&L = (sell price - entry price) x shares
    ADD to session P&L and trade count
    UPDATE wins or losses counter

    CANCEL any remaining orders
    PLACE a SELL order at the current bid price (sell into the market)

get_btc_price() - Get Current BTC Price

# --- PYTHON ---
def get_btc_price():
    resp = requests.get("https://api.binance.com/api/v3/ticker/price",
                        params={"symbol": "BTCUSDT"}, timeout=5)
    if resp.status_code == 200:
        return float(resp.json()['price'])
    return None

# --- PSEUDO-CODE ---
FUNCTION get_btc_price():
    CALL Binance's public API to get the current BTC/USDT price
    WAIT up to 5 seconds for a response
    IF successful: RETURN the price as a number
    IF failed: RETURN nothing (None)