Setup guide

From a blank server
to a running bot

Six steps to a paper bot, about ten minutes. Three more only if you decide to trade real money — nothing moves until you do.

What you need
A VPS with Ubuntu, and your seat token
What it costs
Any $5/month box — it uses almost nothing
Where the keys live
In a file on that server. Never with us
Jump to a step+
  1. 1Pick a server, and pick where it is
  2. 2Install Node
  3. 3Download the runner with your seat token
  4. 4Put the token in a file, not in a command
  5. 5Run it in paper mode
  6. 6Keep it running after you log out
  7. 7Fund a deposit wallet
  8. 8Go live, deliberately
  9. 9Rest below the ask, knowing it lost
1

Pick a server, and pick where it is

Any provider works — Hetzner, DigitalOcean, Vultr. The smallest box is enough.

Choose the region deliberately. Polymarket refuses orders from some countries and says so before it looks at the order — a server in a restricted region cannot trade live, whatever else is correct. Paper mode works anywhere.

Two regions we have measured ourselves, by sending real orders rather than by reading a list: Singapore is refused and Indonesia is accepted. Singapore is a common default on Asian providers, so it is worth checking rather than accepting.

We do not publish a full list. Ours would be the two above plus guesses, and a wrong entry costs you a server. Polymarket's own terms are the source; if your region turns out to be refused, the runner says so by name on the first live order and paper keeps working meanwhile.

your machine
ssh root@your-server-ip
2

Install Node

Ubuntu's own package is too old. This is the official installer.

~/ramalogy
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs
node --version
Expected output
v22or higher — the runner uses the standard library's own SQLite rather than adding a dependency to a process that reads your key.
3

Download the runner with your seat token

The same token that reads the signal fetches the code: no registry account, no deploy key, and one place to revoke. Paste yours in place of $TOKEN.

~/ramalogy
export TOKEN='eyJ...'
 
mkdir -p ~/ramalogy && cd ~/ramalogy
curl -fsSL -H "Authorization: Bearer $TOKEN" \
https://api.ramalogy.xyz/v1/runner/download -o runner.tgz
tar xzf runner.tgz --strip-components=1
npm install && npm run build
Expected output
a dist/ folder after the build. If the download returns 403, the seat is console-only — the runner needs full access.
4

Put the token in a file, not in a command

ps shows command arguments to every user on the box, and your shell keeps them in history. The runner refuses to start if either file is readable by anyone but you.

~/ramalogy
mkdir -p ~/.ramalogy
printf '%s' "$TOKEN" > ~/.ramalogy/token
chmod 600 ~/.ramalogy/token
unset TOKEN
5

Run it in paper mode

Paper is the default and cannot be configured away. Nothing reaches the exchange; the gate runs and every window is recorded.

Ctrl-C when you have seen a few. It all lands in ~/.ramalogy/history.db, and npm start -- --history reads it back — declined windows included, because a record of only the trades taken cannot say whether the skips would have won.

~/ramalogy paper
cd ~/ramalogy && npm start -- --windows 5
Expected output
one JSON line per event, starting with window.open. Most windows will say the gate refused — that is the product working, not a fault.
6

Keep it running after you log out

An SSH session ends when you close the laptop. systemd does not.

Do not add Nice= or IOSchedulingClass=idle to that file. Throttling this process delays its heartbeat enough that the feed stops streaming while the process stays up and every health check still passes.

/etc/systemd/system/ramalogy.service
cat >/etc/systemd/system/ramalogy.service <<'EOF'
[Unit]
Description=ramalogy runner
After=network-online.target
 
[Service]
ExecStart=/usr/bin/node /root/ramalogy/dist/index.js
Restart=always
RestartSec=10
WorkingDirectory=/root/ramalogy
 
[Install]
WantedBy=multi-user.target
EOF
 
systemctl enable --now ramalogy
journalctl -u ramalogy -f
7

Fund a deposit wallet

optional

Only when you want real orders. Deposit into Polymarket once through their own flow — that creates the deposit wallet, which is the only kind of maker the exchange accepts.

Check the printed address against the one Polymarket shows you before sending anything to it. It is derived from your key, not read from your account — if the two disagree, stop and ask rather than editing the file to match.

~/ramalogy
cp credentials.example.json ~/.ramalogy/credentials.json
chmod 600 ~/.ramalogy/credentials.json
nano ~/.ramalogy/credentials.json # your signing key goes here
 
npm start -- --setup-deposit-wallet
Expected output
the wallet your key owns, what it holds, and the API credentials that belong to it. Nothing is written until you add --write.
8

Go live, deliberately

optional

Pick a level, check the size it produces, copy the command. The confirmation phrase is required on every run, not once.

Live mode reads your wallet balance at startup and refuses to start if the bankroll you claim is larger than what you hold. --bankroll auto takes the balance instead of a number that stops being true.

Read the measured results before this step. The strategy did not beat the market in testing: the out-of-sample log score is worse than the market's, its confidence interval excludes zero, and the pre-registered evaluation expects a stop. Start with an amount you would not mind losing entirely.

Risk level

The runner's default, and what the backtest was measured at.

Bankroll
Each order$10.002% of $500

Clears the exchange's $1 minimum. The gate still declines most windows — this is the size when it does not.

npm start -- --live \
--confirm "I ACCEPT THE MEASURED EDGE IS NOT SIGNIFICANT" \
--bankroll 500 --max-fraction 0.02
9

Rest below the ask, knowing it lost

optional

By default the runner takes the ask the moment the gate fires. --limit-offset rests a buy below it instead, in dollars. It was measured against taking the ask on 372 windows before it was built, and it lost: net −0.046 per dollar staked, CI [−0.062, −0.032]. At 30c below the ask the fills win 39%.

Waiting does not buy a better price on the same bet — it sorts the bets. You get filled at a discount on the windows turning against you, and are left chasing the ones turning your way. The flag exists because it was asked for, not because the evidence supports it; the runner logs limit-offset.measured-worse on every run that sets it. A resting order is also cancelled when its window closes, and needs 5 shares rather than $1 — the share floor binds a resting order even though it does not bind a taker.

~/ramalogy
npm start -- --live \
--confirm "I ACCEPT THE MEASURED EDGE IS NOT SIGNIFICANT" \
--bankroll 500 --max-fraction 0.02 \
--limit-offset 0.01
BranchWin ratePrice paid
Took the ask76.9%the ask
Rested, filled72.3%1c below
Rested, unfilled, crossed late86.7%12.9c above

If something is wrong

Pick what your terminal said.

Tap a code for what it means and what to do.

The full reference ships inside the download, as README.md — every flag, what each log line means, and the reasoning behind the execution rules.