📦 FTSeller Wholesale Market Log in · Register

Deploying under your domain

The whole platform is one Python process (FastAPI + SQLite + the market engine and settlement loop).

git clone <this repo> && cd prop-sim
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env         # official values (t5market.com); set XGM_DOMAIN=localhost:8000 XGM_SCHEME=http for a local copy
set -a; . ./.env; set +a
xgames serve --port 8000     # or: uvicorn xgames.main:app --port 8000

Open http://localhost:8000.

Configuration (environment)

Variable Default Meaning
XGM_DOMAIN localhost:8000 Public host[:port]; used in purchase links, manifest, docs. The official deployment is t5market.com (.env.example); a local copy keeps its own host — nothing redirects
XGM_SCHEME http https behind TLS
XGM_OFFICIAL_URL https://t5market.com The official site, shown in the footer, manifest and docs
XGM_OPERATOR_LEGAL_NAME, XGM_OPERATOR_ENTITY, XGM_OPERATOR_JURISDICTION, XGM_OPERATOR_ADDRESS First Autonomous Trade LLC, a Wyoming limited liability company, the State of Wyoming, United States of America, – The legal entity operating the desk: named in the footer, the terms (and their governing law), the privacy policy, the manifest and llms.txt; the address is shown once set
XGM_FIRM_NAME T5MARKET Games Prop Desk Branding
XGM_MARKET_NAME T5MARKET Games Market The market's name on the landing page, the boards, the meta description and llms.txt (the wholesale engine's own landing copy uses it too)
XGM_DB_PATH ./xgames.db SQLite file
XGM_SECRET_KEY dev value Session cookie signing key — change it
XGM_GENESIS 2025-01-01T00:00:00Z Tick 0 of every market (keep it earlier than launch minus the history)
XGM_HISTORY_DAYS 182 Pre-launch history of the desk's own book, loaded at first start
XGM_ORACLE 0 1 reveals outcomes of rejected deals too (research oracle)
XGM_KERNEL core_v1 Market engine name from the registry: core_v1, core_v2 (sales ranks and rank drops instead of a quoted velocity — sales_velocity is not served there —, competing sellers, a sale price that slides — see Market mechanics), wholesale (an Amazon-style wholesale marketplace: category sales ranks behind a delay gate, competing sellers and repricers, lead times, stock on hand and outstanding — see The wholesale market) or the toy random_walk
XGM_WHOLESALE_CONFIG – The wholesale engine's configuration: a JSON file overriding any field of xgames/wholesale/config.py; part of the market identity like the engine itself
XGM_SEED 42 Master engine seed (each market adds its own offset)
XGM_PRICING walk Price process over the core's reference prices: walk (mean-reverting market and family walks, a bridge within the session, per-listing scatter) or none — see Prices move in Market mechanics
XGM_PRICE_SCATTER uniform Per-listing scatter distribution: uniform, normal, triangular, none
XGM_PRICE_NOISE 0.3 Half-width of that scatter in price units (the landed cost scatters 2/3 of it)
XGM_PRICE_VOL 0.3 Innovation per session of the market walk in price units (the family walk moves 3/4 of it)
XGM_ENGINE_POLL_SECONDS 5 How often the loop checks for tick closes
XGM_PAYMENTS_MODE local live, or the in-process stand-ins for local development while no provider key is configured; they switch themselves off as soon as a Stripe or Coinbase key is set — see Payments
XGM_STRIPE_ENABLED, XGM_STRIPE_SECRET_KEY, XGM_STRIPE_WEBHOOK_SECRET 1, –, whsec_local Card connector (Stripe Checkout + off-session charges); webhook at /payments/stripe/webhook
XGM_COINBASE_ENABLED, XGM_COINBASE_API_KEY, XGM_COINBASE_WEBHOOK_SECRET 1, –, cc_local Crypto connector (Coinbase Commerce); webhook at /payments/coinbase/webhook
XGM_MONTHLY_FEE, XGM_CURRENCY 15, USD Monthly participation fee, charged with the first real-money checkout of each month
XGM_TERMS_VERSION 2026-09-05 Terms & privacy version; bump to re-collect acceptance
XGM_KEY_MASTER_SECRET – Shared secret with which the platform recognises the agent keys that get trade carts (hex, or a plain string of 16+ characters); every other agent buys goods to own. Empty = no trade carts = no real-money trading on the deployment
XGM_GOLD_COINS_FREE, XGM_GOLD_COIN_PRICE, XGM_GOLD_COIN_PACKS 5, 5, 5,10,25,50,100 Gold Coins: free at registration, price per coin, packs offered
XGM_EXCHANGE_COMMISSION 0 Commission (fraction) the exchange keeps on sales between holders
XGM_PAYOUT_DELAY_DAYS, XGM_PAYOUT_COMMISSION, XGM_PAYOUT_MIN 3, 0.06, 10 Payouts are scheduled, paid after the delay minus the commission, never below the minimum
XGM_AUTONOMY_MIN_SETTLED 3 Settled trades (with a positive combined result) an agent's hand-funded proposals need before the account holder can switch on autonomous funding for it
XGM_ADMIN_PASSWORD, XGM_ADMIN_PORT –, 8100 Password (8+ characters) and port of the admin interface; the admin app refuses to start without a password
XGM_COMPETITIONS 0 Competitions are disabled by default (see Competitions)

XGM_GENESIS, XGM_KERNEL, XGM_SEED, the engine config and the price process (XGM_PRICING and its parameters) are frozen into the database at first start (market_meta). Starting with different values against the same database is refused, because the recorded history would no longer match its menus. Use a fresh XGM_DB_PATH instead.

The admin interface

The operator's admin interface is a separate process, bound to loopback only, with its own password:

XGM_ADMIN_PASSWORD='a long secret' xgames admin --port 8100     # http://127.0.0.1:8100, user "admin"

It refuses to bind to anything but 127.0.0.1 / ::1, refuses requests that do not arrive from a loopback address, and refuses to start without a password of at least 8 characters. Reach it over an SSH tunnel (ssh -L 8100:127.0.0.1:8100 host). It shows:

  • Scheduled payouts — every payout with holder, account, amount, commission, net, destination, status, request and due dates; mark a payout paid (with the transfer reference) or cancel it (the holder is refunded). While no provider key is configured the settlement loop marks due payouts paid itself.
  • Trade rounds & fees — who paid what to trade (cart, agent, orders, amount, fee, source, status, units handed back) and every participation fee with what collected it.
  • Purchases & exchange — ownership purchases and exchange trades.
  • Emit goods — mint a stack of a family and grade into the USD or Gold Coin book at an asking price (also xgames emit --cluster 3 --grade Mythic --qty 4 --economy usd --price 12).

xgames payouts list|due|pay --id N --reference R|cancel --id N does the payout work from the command line; xgames issue-key [--tenant N] issues an agent key that this deployment recognises (with the configured secret), and xgames issue-key --verify KEY checks one. The scheme itself, for the parties that issue keys, is the standalone, documented client/agent_keys.py in the repository.

First start and the history load

On first start the platform serves immediately and a background thread loads the pre-launch history into the record, newest sessions first: for every session it generates the menu, executes the desk's legacy policy, realises each trade's demand path and stores the trades and statistics. Six months across the four markets is roughly 26,000 sessions and 4 million trades; expect 5–15 minutes (the platform stays responsive meanwhile) and a database of roughly 1 GB. Progress is shown at /console/timeline and /api/v1/markets/{id}/coverage. xgames backfill runs the same job synchronously (useful before packaging a demo).

Local development without provider keys

With no Stripe or Coinbase Commerce key configured the connectors run as in-process stand-ins: no provider is contacted, a card is entered on the desk's own form (only brand, last four digits and expiry are kept), hosted payments complete on /pay/<token> with a Pay / Decline choice, due payouts are marked paid by the settlement loop, and webhooks are still signature-checked with the configured secrets, so a signed event can be posted by hand:

BODY='{"event":{"type":"charge:confirmed","data":{"code":"X","metadata":{"payment_token":"<token>"}}}}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac cc_local | sed 's/^.* //')
curl -s -X POST https://ftseller.com/payments/coinbase/webhook -H "X-CC-Webhook-Signature: $SIG" -H 'content-type: application/json' -d "$BODY"

Setting a provider key turns the stand-ins off; nothing on the served pages says which way the deployment runs. The test-suite (tests/test_payments.py) covers the fee once per month, both hosted flows, signed webhooks, late confirmation after a session close, and the acceptance of terms.

Docker

docker build -t xgames .
docker run -p 8000:8000 -e XGM_DOMAIN=t5market.com -e XGM_SCHEME=https \
  -e XGM_SECRET_KEY=$(openssl rand -hex 32) -v xgames-data:/data xgames

Put any TLS-terminating proxy (Caddy, nginx, Cloudflare) in front and point it at port 8000.

Building the docs as a static site

The same Markdown in docs/ is rendered in-app at /docs (this is the published documentation). It is also a ready MkDocs project if you ever want a standalone static copy:

pip install mkdocs mkdocs-material
XGM_DOMAIN=t5market.com XGM_SCHEME=https mkdocs build   # -> site/

https://ftseller.com placeholders are replaced by setting XGM_DOMAIN before building (mkdocs.yml uses a hook); in-app rendering substitutes them itself.

Updates and restarts

All state — accounts, cards and charges, open and funded orders, positions, carts, agents and API keys, the market record — lives in the SQLite database, so a restart never loses anything. Orders that were awaiting funding stay payable until their session closes; sessions that closed while the platform was down are processed in order on the next start (funded orders fill, unfunded ones expire, positions advance).

Code updates that change the schema are applied automatically at startup (xgames migrate runs the same step by hand): new tables and columns are added in place with defaults, new indexes are created, and the few changes SQLite cannot make in place are done as table rebuilds that copy the data. Applied steps are listed in schema_migrations and summarised in /api/v1/manifest under schema. Renames of the market engine are recognised (the recorded identity is migrated); a genuinely different engine, seed or genesis against an existing database is still refused.

Login sessions survive restarts too: if XGM_SECRET_KEY is not set, a key is generated once and stored next to the database (xgames.secret). Back up that file together with the database.

Recommended update procedure: stop the service, deploy the new code, start it. Take a copy of the database file first if you want a rollback point.

Operations

  • Single process, single settlement thread: do not run more than one instance against the same database.
  • xgames markets prints the market clocks; xgames menu m15 --tick N dumps a menu.
  • Backups: copy the SQLite file (and xgames.secret) while the service is stopped, or use sqlite3 xgames.db ".backup copy.db" while it runs.