There was no way to tell "running, nothing to pull" from "not running". The no-change path logs nothing by design, and PM2 reports a cron-restart process as `stopped` with ↺ 0 even while firing on schedule — I verified that against PM2 7.0.4: a one-minute cron fired four times without the counter moving once. scripts/.last-check is now rewritten on every run with the outcome. It is a single overwritten line, so it needs no trimming. The outcome is set by each exit path and written once in `finally`, rather than calling record at each return — the first version of this did the latter and already missed the build-failure path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
250 lines
8.7 KiB
Markdown
250 lines
8.7 KiB
Markdown
# AutoFirmer
|
|
|
|
Automated futures trading dashboard for prop firm accounts via Tradovate.
|
|
|
|
---
|
|
|
|
## Setup
|
|
|
|
### Windows
|
|
|
|
In **PowerShell**, from wherever you want the instance to live:
|
|
|
|
```powershell
|
|
irm https://git.juicerroom.com/senofy/autofirmer-expanded/raw/branch/master/setup-windows.bat -OutFile setup-windows.bat
|
|
```
|
|
|
|
```powershell
|
|
.\setup-windows.bat
|
|
```
|
|
|
|
That is the whole install. It clones into an `autofirmer` folder beside itself,
|
|
installs anything missing (Git, Node, Python) via winget, installs dependencies,
|
|
builds, asks for the master dashboard URL and an instance name, and offers to
|
|
set up auto-start and auto-update.
|
|
|
|
Re-running it updates an existing checkout rather than cloning again, so it
|
|
doubles as a repair tool.
|
|
|
|
Two things it deliberately does, worth knowing:
|
|
|
|
- **Node LTS, not latest.** `better-sqlite3` ships prebuilt binaries for LTS
|
|
only. On a newer major it falls back to compiling with node-gyp and fails
|
|
without Visual Studio Build Tools, so the script warns before that happens.
|
|
- **It looks for tools off `PATH`.** A tool installed in the same session it is
|
|
needed — or installed without its "add to PATH" option — is invisible to a
|
|
running shell, so the script checks the standard install directories too.
|
|
|
|
### Raspberry Pi 5 / Debian / Ubuntu
|
|
|
|
```bash
|
|
curl -fsSLO https://git.juicerroom.com/senofy/autofirmer-expanded/raw/branch/master/setup-linux.sh
|
|
```
|
|
|
|
```bash
|
|
bash setup-linux.sh
|
|
```
|
|
|
|
Same idea, apt instead of winget. It installs Node 22 from NodeSource (Pi OS
|
|
ships one too old for Next 16), `xdotool` and `scrot` for the clicker, and puts
|
|
the Python dependencies in a virtualenv — Pi OS Bookworm enforces PEP 668, so a
|
|
system-wide `pip install` fails outright.
|
|
|
|
**The clicker needs X11.** Pi OS on a Pi 5 defaults to Wayland, which does not
|
|
let one client synthesise input into another or read the focused window, so
|
|
neither `xdotool` nor pyautogui can work there. The dashboard is unaffected, but
|
|
for the AutoBuyer:
|
|
|
|
```bash
|
|
sudo raspi-config # Advanced Options -> Wayland -> X11, then reboot
|
|
```
|
|
|
|
`python clicker/diagnose.py` reports the session type, `DISPLAY` and whether
|
|
`xdotool` is present — run it before trusting a new machine.
|
|
|
|
The dashboard runs on port **3000**: `http://localhost:3000`.
|
|
|
|
---
|
|
|
|
## First-time setup
|
|
|
|
On first run, the SQLite database (`autotrader.sqlite`) is created automatically in the project root. No migrations need to be run manually.
|
|
|
|
Open the app in your browser and add your firms through the UI:
|
|
|
|
1. Click **+ Add Firm** on the main page
|
|
2. Enter the firm name, Tradovate username, and password
|
|
3. Go to the firm's **Settings** page to configure account types (prefix, profit target, consistency %, etc.)
|
|
4. Return to the main page — accounts will populate once the firm connects to Tradovate
|
|
|
|
---
|
|
|
|
## AutoBuyer (browser automation)
|
|
|
|
The AutoBuyer page drives a real browser to buy and reset prop-firm accounts. It
|
|
is three pieces, and all three must be running:
|
|
|
|
| Piece | What it does |
|
|
|---|---|
|
|
| the dashboard | Defines automations, queues runs, shows progress |
|
|
| `extension/` | A Chromium extension that reads the broker page and measures elements |
|
|
| `clicker/runner.py` | A desktop process that moves the real mouse and keyboard |
|
|
|
|
Automations are declared in `lib/automations.ts` — one entry per firm, with the
|
|
steps inside. Adding a button means editing that file; the page and the runner
|
|
pick it up from the server.
|
|
|
|
### 1. Load the extension
|
|
|
|
`chrome://extensions` → enable **Developer mode** → **Load unpacked** →
|
|
select the `extension` folder.
|
|
|
|
Chrome does **not** reload an unpacked extension when its files change. After
|
|
pulling updates, click the reload icon on its card — the dashboard shows the
|
|
version it sees, and a mismatch means the reload did not take.
|
|
|
|
Adding a new firm also means adding its host to `host_permissions` in
|
|
`extension/manifest.json` and reloading. Without it the extension cannot read
|
|
that site, and every step fails to locate.
|
|
|
|
### 2. The clicker
|
|
|
|
`setup-windows.bat` installs its Python dependencies, and re-applies them
|
|
whenever `clicker/requirements.txt` changes. To do it by hand:
|
|
|
|
```powershell
|
|
python -m pip install -r clicker\requirements.txt
|
|
```
|
|
|
|
See `clicker/README.md` for the per-platform notes — display scaling and
|
|
foreground lock both matter on Windows — and for the verification sequence to
|
|
run before letting it click anything on a new machine.
|
|
|
|
### 3. Start the runner
|
|
|
|
Auto-start runs it under PM2, so normally there is nothing to do. To run it by
|
|
hand instead:
|
|
|
|
```powershell
|
|
python clicker\runner.py
|
|
```
|
|
|
|
It reports in every two seconds, and the dashboard greys out
|
|
the automation buttons when it is not there. A running Python process does not
|
|
reload when the source changes, so restart it after pulling updates; the
|
|
dashboard warns when its version is behind.
|
|
|
|
### Running it
|
|
|
|
Turn **Page capture** on from the AutoBuyer page, then press an automation's
|
|
button. Progress appears per step, and **Stop** halts a run between steps.
|
|
|
|
The browser must be visible and frontmost while a run is in flight — the clicks
|
|
are real OS-level input, so the machine cannot be used for anything else, and a
|
|
dialog stealing focus fails the step. That makes an RDP session a poor host:
|
|
disconnecting can suspend the desktop and break clicks in ways that are hard to
|
|
diagnose.
|
|
|
|
## Keeping it running
|
|
|
|
`setup-windows.bat` offers to set this up for you at step 6. To enable it later,
|
|
or after declining:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install-autostart.ps1
|
|
```
|
|
|
|
That puts the dashboard, the clicker and an update checker under PM2, adds a
|
|
Startup-folder entry so PM2 comes back at logon, and lets PM2's cron restart run
|
|
the update check every 5 minutes. It needs no administrator rights, changes
|
|
nothing machine-wide, and is safe to re-run.
|
|
|
|
```powershell
|
|
pm2 list # what is running
|
|
pm2 logs autofirmer # dashboard output
|
|
pm2 logs clicker # runner output
|
|
```
|
|
|
|
**Why not a Windows service.** The clicker sends real mouse and keyboard input
|
|
and has to own a desktop. A service runs in session 0, which has none, so the
|
|
clicks would go nowhere. Everything therefore runs in your logged-in session —
|
|
which also means an unattended reboot leaves the instance down until somebody
|
|
logs in.
|
|
|
|
To stop it starting at logon, delete the `AutoFirmer.cmd` shortcut from your
|
|
Startup folder (`shell:startup` in the Run dialog).
|
|
|
|
To start everything by hand without waiting for a logon:
|
|
|
|
```powershell
|
|
node scripts\start-all.mjs
|
|
```
|
|
|
|
`start-autofirmer.bat` still runs the dashboard in a visible window without PM2,
|
|
which is the easier thing to watch when a build is misbehaving.
|
|
|
|
|
|
## Firewall
|
|
|
|
To restrict port 3000 to a specific trusted IP only:
|
|
|
|
```powershell
|
|
New-NetFirewallRule -DisplayName "AutoFirmer" -Direction Inbound -Protocol TCP -LocalPort 3000 -RemoteAddress <your-ip> -Action Allow
|
|
```
|
|
|
|
Or open it to all inbound (less secure):
|
|
|
|
```powershell
|
|
New-NetFirewallRule -DisplayName "AutoFirmer" -Direction Inbound -Protocol TCP -LocalPort 3000 -Action Allow
|
|
```
|
|
|
|
---
|
|
|
|
## Updating
|
|
|
|
Once auto-start is installed, nothing here is manual. Every 5 minutes the update
|
|
task fetches `master`, and when it has moved it pulls, reinstalls dependencies if
|
|
`package-lock.json` or `clicker/requirements.txt` changed, rebuilds, restarts
|
|
AutoFirmer, and restarts the clicker if anything under `clicker/` changed.
|
|
|
|
**A failed build is never deployed.** The build runs before anything restarts, so
|
|
a broken push leaves the previous build serving and logs the failure instead.
|
|
|
|
### Checking it is alive
|
|
|
|
`scripts/.last-check` is rewritten on every check, whether or not anything came
|
|
down:
|
|
|
|
```
|
|
2026-08-31T00:43:27.155Z up to date at 27a61fd2
|
|
```
|
|
|
|
This exists because the alternatives mislead. `update.log` only records real
|
|
events, so it stays empty for days when nothing is pushed — and PM2 reports a
|
|
cron-restart process as `stopped` with a restart count of `0` even while it is
|
|
firing on schedule. Neither is evidence of a problem; `.last-check` is the
|
|
signal to trust.
|
|
|
|
Everything that actually happens is appended to `scripts/update.log`. To see
|
|
what it would do without touching anything:
|
|
|
|
```powershell
|
|
node scripts\update-check.mjs --dry-run
|
|
```
|
|
|
|
To apply an update immediately rather than waiting for the next check:
|
|
|
|
```powershell
|
|
node scripts\update-check.mjs
|
|
```
|
|
|
|
### The two things that still do not reload themselves
|
|
|
|
- **The extension** — click reload on its card in `chrome://extensions`.
|
|
- **The scheduler is fine now.** It persists to the settings table and resumes
|
|
after a restart, so an update no longer silently stops automated trading.
|
|
|
|
The dashboard reports the version it sees from the extension and the runner, and
|
|
warns when either is behind. Most AutoBuyer bugs that look mysterious are the
|
|
extension still running the previous code.
|