Metadata-Version: 2.4
Name: potd-trader
Version: 0.4.0
Summary: Buy the 0xinsider Pick of the Day on your own Polymarket account. Your keys stay on your machine. Kalshi uses reviewed mappings.
Keywords: polymarket,0xinsider,pick-of-the-day,sports,prediction-markets
Author: 0xinsider
Author-email: 0xinsider <support@0xinsider.com>
License-Expression: MIT
Requires-Dist: polymarket-client==0.10.0
Requires-Dist: httpx>=0.27,<1
Requires-Dist: cryptography==50.0.2
Requires-Dist: pydantic>=2,<3
Requires-Dist: pydantic-settings>=2,<3
Requires-Dist: python-dotenv>=1.2,<2
Requires-Dist: portalocker[win32]==4.4.0
Requires-Dist: tzdata>=2024.1 ; sys_platform == 'win32'
Requires-Python: >=3.12.4
Project-URL: API reference, https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day
Project-URL: Guide, https://docs.0xinsider.com/guides/auto-buy-the-pick
Project-URL: Homepage, https://0xinsider.com/pick-of-the-day
Project-URL: Repository, https://github.com/0xinsider/potd-trader
Description-Content-Type: text/markdown

# potd-trader

Buy the [0xinsider Pick of the Day](https://0xinsider.com/pick-of-the-day) on your own
Polymarket account, with a small stake and explicit spending limits. Dry-run is the default.
Your wallet key signs locally through the official Polymarket SDK.

[Setup guide](https://docs.0xinsider.com/guides/auto-buy-the-pick) ·
[API contract](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day) ·
[Release notes](CHANGELOG.md)

## Install a version with locked dependencies

**v0.4.0** adds separate Kalshi commands using reviewed equivalent contracts. The existing
Polymarket commands retain their behavior. It also verifies your authenticated Pro or Max allowance before considering a buy. Pro
includes five daily picks; Max includes every eligible published pick, up to fifteen. Earlier
releases do not enforce this additional daily pick limit. Historical wire ranks through twenty
remain readable.

Python 3.12.4+ and [uv](https://docs.astral.sh/uv/) are required. Supported systems: Windows
with x64 Python, macOS and Linux, including WSL. Keep the configuration and ledger on a local
filesystem, outside OneDrive, other synced folders and network drives.

```bash
git clone --branch v0.4.0 --depth 1 https://github.com/0xinsider/potd-trader potd-trader-src
cd potd-trader-src
uv sync --locked
uv run --locked potd-trader init
```

On Windows, install Git and uv from PowerShell, then reopen your terminal:

```powershell
winget install --id Git.Git --exact
winget install --id astral-sh.uv --exact
```

In the new PowerShell window, use a local folder under your Windows profile:

```powershell
Set-Location $env:USERPROFILE
git clone --branch v0.4.0 --depth 1 https://github.com/0xinsider/potd-trader potd-trader-src
Set-Location potd-trader-src
uv sync --locked --python ">=3.12.4,<3.13"
uv run --locked potd-trader init
```

uv installs Python if needed. WSL is optional; native Windows uses the same commands and ledger
format. Use x64 Python on Windows; native ARM64 Python is not covered by this release.

Use the [v0.4.0 release page](https://github.com/0xinsider/potd-trader/releases/tag/v0.4.0)
to verify the source commit and package checksums. For an immutable source pin, check out that
full commit instead of a moving branch. `uv sync --locked` installs the
versions and artifact hashes in the checked-in `uv.lock` and refuses a stale lockfile. Installing
an unversioned Git URL or a package without its lockfile does not reproduce that environment.

`init` creates a new `potd-trader/` folder inside the checkout. It asks five questions, hides both
keys as you type, creates a private folder, and runs account checks and a forced dry run.
On macOS/Linux the folder is mode 700 and `.env` is mode 600. On Windows, the new folder limits
access to your user and administrators; files inherit its permissions. An inherited `LIVE=yes`
cannot make this setup run buy. Only the final confirmation
can enable subsequent live commands. An existing folder is never overwritten. The last two
questions set your unit size per pick and daily cap; the cap prompt shows the cost of up to
15 picks. Pro opens 5 daily picks in total, including the free pick; Max opens every available
published pick up to 15. The existing init suggestion remains 10 stakes, and your chosen cap
remains authoritative.

From that new folder, `uv` finds the project in its parent directory:

```bash
cd potd-trader
uv run --locked potd-trader run --dry-run
uv run --locked potd-trader live on     # type "spend real money" yourself
uv run --locked potd-trader watch
```

## Buy reviewed contracts on Kalshi

Use the separate [Kalshi guide](https://docs.0xinsider.com/guides/auto-buy-the-pick-on-kalshi).
The signal still comes from Polymarket wallet analytics. Kalshi has its own account, contracts,
settlement rules, prices and fees. No Polymarket expected return is represented as a Kalshi return.

```bash
uv run --locked potd-trader kalshi init
cd kalshi-potd-demo
```

Fill in `.env.kalshi` privately: your Pro or Max `OXINSIDER_API_KEY`, demo
`KALSHI_API_KEY_ID`, and `KALSHI_PRIVATE_KEY_PATH` pointing at your local RSA (at least 2,048 bits)
or Ed25519 PEM. Keep the key file private (`chmod 600 kalshi-key.pem` on macOS/Linux).
Get a separate demo account and API key at [demo.kalshi.co](https://demo.kalshi.co).
The private signing key stays local. Do not paste any key in a chat.

```bash
uv run --locked potd-trader kalshi status
uv run --locked potd-trader kalshi picks
uv run --locked potd-trader kalshi markets --series KXNFLGAME
uv run --locked potd-trader kalshi map --rank <slot> --ticker <ticker> --outcome yes --max-price <price>
uv run --locked potd-trader kalshi run --dry-run
```

`map` shows the source outcome/rules and Kalshi rules/linked contract terms. Review game versus
map, overtime, tie, postponement, cancellation and settlement behavior. Type `the same event,
outcome and settlement rules` only if they agree. Every unmapped, changed, unavailable, expired
or ambiguous contract skips. This release does not infer mappings from team names. Source
kickoff owns the cutoff; a Kalshi close time is not treated as kickoff. Reviewed identity/rules,
linked contract PDF hashes and the source kickoff are checked again before submitting.

Only binary $1 default-settlement contracts are supported. Demo fixtures without real event
settlement and combinations are refused. Demo markets can differ from production, and a matching
current POTD may not exist in demo. An empty book or no equivalent demo contract is an honest skip.

Kalshi `STAKE_USD` and `DAILY_CAP_USD` include principal plus a **conservative exchange fee
reservation**, not an estimated fee. Each contract reserves `0.0175 * maximum_fee_multiplier
+ 1.0001` dollars for fees, covering worst-case fractional fill rounding without assuming rebates.
This can buy substantially fewer contracts than principal-only sizing. The ledger retains this
full reserve for accepted and unresolved orders, and displays confirmed exchange debit separately.
Unknown fee models skip. External broker/FCM commissions are unsupported. Each order also obeys
its reviewed price cap, `MAX_PRICE`, the source authorization ceiling and `MAX_SLIPPAGE_PCT`.

```bash
uv run --locked potd-trader kalshi live on  # user types "place demo orders"
uv run --locked potd-trader kalshi watch
uv run --locked potd-trader kalshi live off
uv run --locked potd-trader kalshi reconcile
uv run --locked potd-trader kalshi ledger
```

Only you enable orders. `run --dry-run` and `watch --dry-run` force no submission even with
`LIVE=yes`. Kalshi buys integer quantities with Immediate-or-Cancel: no new resting order is
intended. A zero-fill acknowledgement remains reserved until terminal provider readback confirms
it; ambiguous transport/results never cause an automatic repost. The user must not hand-edit the ledger.

For real funds, create a **new** folder with `kalshi init kalshi-potd-live --environment production`.
Use production credentials and new mappings there. Its enabling phrase is `spend real money on
Kalshi`. Use a dedicated Kalshi account with default subaccount 0 and no concurrent manual trades or
other trading tools. Position/resting-order reads are delayed projections and cannot atomically
prevent another app racing a submission. Eligibility is your account's responsibility. Configuration is read only from the current
folder's `.env.kalshi`; process environment values and the Polymarket `.env` are ignored. The
ledger is bound to that environment/key ID and paths. All instances for the same Kalshi account
must share one local configuration/ledger folder. Separate copies or machines do not coordinate.

Kalshi runtime destinations are `api.0xinsider.com` (only the feed key),
`gamma-api.polymarket.com` (source token identity/rules through the existing SDK),
`external-api.demo.kalshi.co` or `external-api.kalshi.com` (market/account reads and signed orders),
and `assets.kalshi.com` (bounded official contract PDFs). Redirects and custom API destinations
are refused. The PEM never leaves your machine. The table below describes the existing
Polymarket commands.

## What leaves your machine

| Goes to | What | Why |
| --- | --- | --- |
| `api.0xinsider.com` | your 0xinsider API key | read today's entitled Pro or Max picks |
| `clob.polymarket.com` | signed authentication messages, derived API credentials, signed orders, and token IDs | authenticate, check balance, quote, and buy |
| `gamma-api.polymarket.com` | token IDs | read market identity, kickoff, tick size, and status |
| `polymarket.com/api/geoblock` | your IP, as with any request | check trading eligibility for your location |
| `polygon.drpc.org` | public wallet, token, and contract addresses in RPC calls | SDK wallet and approval checks |
| `relayer-v2.polymarket.com` | wallet addresses; relayer credentials and signed approval requests when setup is requested | SDK wallet and relayer operations |

These are runtime destinations, not package-installation hosts. The reviewed SDK is pinned to
`polymarket-client==0.10.0`. The private key remains inside the signing process; there is no
0xinsider endpoint accepting it. `OXINSIDER_API_BASE` only accepts `https://api.0xinsider.com`;
redirects are refused. The optional builder code stays optional. No withdrawal command exists.

Keeping the key local does not make a recommendation feed infallible. A compromised feed could
recommend unwanted purchases within your limits. Use a dedicated wallet and a small budget.

## Set up by hand or with an agent

Inside the versioned checkout:

```bash
cp .env.example .env
chmod 600 .env
```

On Windows, prefer `init` so it creates the private folder. If setting up by hand, use
`Copy-Item .env.example .env` in a folder whose Windows Security permissions allow only your
account and administrators. `chmod` does not set Windows access permissions.

Fill in these values locally; never paste keys into a chat:

- `OXINSIDER_API_KEY`: a live Pro or Max key from [0xinsider.com/developers](https://0xinsider.com/developers).
- `POLYMARKET_PRIVATE_KEY`: the signer key. Email/Google login: Polymarket Settings, Export
  private key ([official help](https://help.polymarket.com/en/articles/13364258-how-do-i-export-my-key)).
  Wallet login: export from that wallet app.
- `POLYMARKET_WALLET_ADDRESS`: the account address in your Polymarket profile menu, which holds
  the pUSD. This can differ from the signer address.

```bash
uv run --locked potd-trader status
uv run --locked potd-trader run --dry-run
```

For an existing setup, stop its watcher, run `potd-trader live off`, then run
`uv run --locked potd-trader size` inside the configuration folder. Type your unit size and daily
cap; the command preserves keys and other settings and removes obsolete rank settings. Run
`status` and a dry run, then restart the watcher.
Only you can turn live trading back on. A one-shot `run` sees picks already released; `watch`
wakes for later releases.

An agent must read [AGENTS.md](AGENTS.md), keep secrets out of output, run only these read/dry-run
checks, and leave enabling live orders to you. `status` reports region, account, approvals,
balance, reserved UTC budget, and unresolved orders. Missing approvals require `setup` with a
Relayer API key configured in `.env`; that command submits approval transactions, not orders.

## Stop and resume

```bash
uv run --locked potd-trader live off
uv run --locked potd-trader live status
```

`live off` writes a persistent `HALT` file beside the active `.env`, then waits for any submission
already in progress before acknowledging the stop. Running watchers using that folder cannot
start another submission after that acknowledgment. An order already sent may fill; this is not
an order-cancellation command. If a network call is stuck, the acknowledgment can wait for it.
Creating `HALT` yourself signals the stop before the next post too.

`live on` requires your confirmation, writes `LIVE=yes`, and clears `HALT`. A watcher that started
with live control enabled can resume on its next cycle. One started in dry-run stays dry until
restarted. `run --dry-run` and `watch --dry-run` always prohibit orders. Live mode requires both
process settings and the active file to say exactly `yes`, with no `HALT`; an inherited variable
cannot override a file saying `no`, and a missing control file fails closed. Restart after changing
other settings. A local `.env` is used alone; the home file is a fallback, not merged into it.

## What happens before an order

1. Verify the same response's authenticated account allowance, then validate current New York product dates and unique pick slots/tokens. Refuse a duplicate or
   inconsistent slate. Skip settled, unreleased, started, out-of-rank, or incomplete picks.
2. Require a positive published price and a current entry authorization for the selected token.
3. Check the independent Polymarket market identity and backed outcome, an explicitly open
   market, known kickoff, tick size, and minimum order size. Keep the kickoff buffer.
4. Quote the book for your stake. Enforce `MAX_PRICE`, `MAX_SLIPPAGE_PCT`, and the authorization's
   `max_entry_price` together. The authorization is a drift limit, not a promise of profit.
5. Recheck the market and book before signing. After signing, recheck live control and the
   quote deadline (30 seconds at most, shorter near kickoff/authorization expiry).
6. Under an exclusive file lock, reload the ledger, reject a repeated pick/slot/token, enforce
   the account's New York daily pick limit, and reserve the stake against the local UTC
   submission day. Persist and sync the reservation before posting.
7. Post one Fill-and-Kill BUY through the SDK's separate `post_order`. Record its result without
   automatic approval transactions or an application retry of an ambiguous submission.

Live preflight also checks geoblocking, wallet approvals, and the available balance. FAK orders
can fill partially; their full requested principal remains reserved for that UTC day. Exchange
fees are additional: `STAKE_USD` and `DAILY_CAP_USD` bound order principal, not fee-inclusive debits.

Identity-free `locked_picks` are access information, not trades. The CLI shows how many
additional picks require Max and an upgrade link. A successful `state: "none"` response has no released trade candidates,
and replaces the previous slate; its missing game identity or schedule is never guessed.

`watch` honors `Retry-After`, release times, and `proof_pending_picks[].retry_at`. Read transport
failures receive bounded backoff with a visible warning; an ambiguous order does not get retried.

Every released, otherwise eligible pick uses the same unit size. There is no rank-based selection,
stake, or priority in this trader. If the remaining cap funds only some picks, the
trader considers the earliest released eligible picks first, breaking simultaneous-release ties
by token ID. It prints each cap skip.

The default 25 pUSD cap funds five 5 pUSD picks, up to the 5 daily picks Pro includes. Funding
15 Max picks at that size would require 75 pUSD of principal, but a day may publish fewer picks.
Existing default and configured caps are not raised automatically.

`MIN_RANKS` and `MAX_RANKS` from older setups are ignored; `status`, `run`, and `watch` warn
about them. The feed read reports your Pro or Max allowance and warns only when your chosen spending
cap cannot cover that plan's possible daily picks. The current
API still supplies a slot number for durable duplicate protection. Removing that contract across
the product and historical proofs is tracked in
[0xinsider/0xinsider#19968](https://github.com/0xinsider/0xinsider/issues/19968).

## Price protection and fill price

The trader checks the current book for your full `STAKE_USD` before submitting a buy. The
quote is the highest price level that stake would reach, not just the best ask or an average
fill price. It must pass all three independent guards:

| Guard | Maximum permitted quote |
| --- | --- |
| API `entry_authorization.max_entry_price` | The ceiling returned for that exact token and authorization |
| `MAX_PRICE` | Your configured absolute ceiling; default `0.925` (92.5 cents per share) |
| `MAX_SLIPPAGE_PCT` | Published `backed_price` multiplied by `1 + MAX_SLIPPAGE_PCT / 100`; default 3% |

The API allowance is versioned. New policy-8 authorizations allow **5 cents** above the first
reference ask, capped at 85 cents and rounded down to the provider's tick. Policy-7 authorizations
keep their original 2-cent allowance and frozen price/expiry. The reference ask can predate the
pick's release. Always read the returned `max_entry_price`; the trader does not reconstruct it.
The five-cent allowance does not mean 5%, and does not change your `.env` settings.

For example, a 64-cent reference ask gives a policy-8 ceiling of 69 cents with a 1-cent tick.
If the published pick price is 66.5 cents, the default 3% guard allows a quote up to 68.495 cents.
A 68-cent quote passes both guards; a 69-cent quote still fails the 3% guard.

```text
stake-sized book quote -> all three guards -> signed order limit -> confirmed fills
```

After a fresh quote passes, the trader rounds that quote down to the market tick and signs it
as the order's maximum buy price. The order limit can therefore be lower than the API ceiling.
Polymarket's Fill-and-Kill order may fill partially or not at all if the book moves; it cannot
buy above the signed limit. See the [official order contract](https://docs.polymarket.com/trading/place-orders).

Price guards use the quote **before** submission. Your actual average fill price comes **after**
execution: confirmed pUSD spent divided by confirmed shares received. The ledger records those
amounts; an accepted order alone does not prove a fill. This average excludes additional fees.
The pick's published `backed_price` is its reference price, not your personal fill price.

## Pro and Max access

Your API key determines access; there is no local tier setting. The trader recognizes the
verified feed response's `X-Monthly-Quota-Limit` header: the current included allowance of
500,000 requests identifies Pro, and 2,000,000 identifies Max. This is not the optional
pay-as-you-go ceiling. Missing or unrecognized allowances stop trading instead of guessing a
plan. A future quota or pick-limit change requires a compatible trader release.

Pro can reserve at most five distinct picks per New York product day, including the designated
free pick. It is not a rank filter: the free selection may have a later presentation slot.
Max can reserve up to fifteen. The server's filtered feed still decides which selections your
account may see; locked selections are never enriched or bought. Existing accepted, submitting
and unknown reservations count toward the daily allowance, including entries from an earlier
release. Confirmed rejections release their pick reservation.

A watcher checks the account allowance on every successful response, including `304`. If it
changes, the old slate is discarded and a fresh authenticated read is required. A lapsed key
cannot keep trading from a cached slate. Stop older watchers before upgrading; separate local
folders or machines do not share these limits.

## Ledger and recovery

`ledger.json` beside `.env` records local submission timestamps, pick identities, reserved stakes,
and exchange answers. The adjacent `.lock` file coordinates concurrent processes. All instances
for one wallet must share this ledger and configuration folder on the same local filesystem;
separate copies or machines do not coordinate. Never delete the ledger or lock files while running.

A `submitting`, `accepted`, or `unknown` entry blocks the pick, the same date/rank slot even if its
token changes, and the token even if the feed relabels its date. Known rejections release their
reservation. `submitting` and `unknown` entries continue consuming budget across midnight until
reconciled. Corrupt state fails closed. Existing v1 ledgers remain readable without a migration.

```bash
uv run --locked potd-trader ledger
```

For an unresolved order: stop all instances, inspect Polymarket Activity and the order/fill state,
and preserve a backup. Only after proving no order can still execute should you remove its local
entry. If the order exists, preserve its identity and record the confirmed result instead. Never
clear an entry just to make the bot try again. There is no automatic unknown-order reconciliation.

## Upgrade from 0.2.0

Stop every old `watch` process first: the old `live off` command cannot stop an already-running
old binary. Back up your private configuration folder and ledger, install the new release in a
separate checkout, and use the same ledger path. Run `--dry-run` before restarting live.

Behavior changes: `DAILY_CAP_USD=0` is rejected; set a positive cap. Dates are checked against New
York, but spending is capped by locally recorded UTC submissions. Missing safety data now skips
trading. Alternate API origins are refused. Environment-only live deployments must mount an
active `.env` control file containing `LIVE=yes`; keys may still come from the environment.

## Settings

| Variable | Default | Meaning |
| --- | --- | --- |
| `LIVE` | `no` | exact `yes` in process settings and active `.env`, with no `HALT` |
| `STAKE_USD` | `5` | order principal per pick; `0` means no buys |
| `MAX_PRICE` | `0.925` | absolute ceiling on the stake-sized book quote, in pUSD per share |
| `MAX_SLIPPAGE_PCT` | `3` | maximum percentage increase of the quote over the published backed price; `3` means 3%, not 3 cents |
| `DAILY_CAP_USD` | `25` | positive ceiling on UTC order principal plus unresolved prior intents |
| `KICKOFF_BUFFER_MINUTES` | `5` | stop buying this far before kickoff or authorization expiry |
| `LEDGER_PATH` | `ledger.json` beside `.env` | shared durable order state |
| `POTD_TRADER_HOME` | `~/.potd-trader` | fallback configuration folder |
| `WATCH_IDLE_MINUTES` | `30` | idle recheck cadence |

## Docker

Build from the release checkout. Mount the configuration folder so `live off` on the host and
container see the same control file and ledger. Never bake secrets into the image.

```bash
docker build -t potd-trader:0.3.6 .
docker run --rm -v "$PWD/potd-trader:/app/data" potd-trader:0.3.6
```

## Verification and limits

The Check workflow installs locked dependencies, runs lint, formatting and strict source typing,
compiles the source, builds packages, and exercises CLI help, version, local setup, `live off`,
`live status`, an empty ledger and New York timezone loading on Windows, macOS and Linux.
These checks use no wallet credentials and submit no orders. Historical fake-exchange cases
remain in the repository; they are not run by this workflow or claimed as Windows evidence.

Atomic writes flush file content before replacement. macOS/Linux also flush the parent directory;
Windows does not, so sudden power loss can lose a recent replacement. After a power failure,
stop trading and compare the ledger with Polymarket Activity before resuming. Never run the same
wallet from Windows and WSL, separate machines, containers or separate ledger copies at once.

Stop every old watcher before upgrading, including any WSL or container process. Preserve the
configuration folder and ledger, then check `live status` and `run --dry-run` with the new version.
Only you enable live orders. Platform checks do not prove future feed integrity or profit.

Orders spend real money and cannot be undone. Polymarket regional restrictions apply. This tool
is MIT licensed, with no warranty; 0xinsider does not operate or custody your wallet.
