Hi thereRunbook
How-to & procedures
Step-by-step procedures for operating the SPX trading system, server, and website. Click any section to expand it.
Key facts & addresses▸
- VPS IP: 165.227.84.78 (DigitalOcean droplet "spx-server", Ubuntu 24.04)
- SSH user: victor
- Trading server path: /home/victor/spx-server/
- Trading service: spx-server.service (port 5005)
- Dashboard service: spx-dashboard.service (port 5006)
- Journal file: /home/victor/spx-server/journal/trades_journal_2026.csv
- URLs: hub = victorngoma.com · dashboard = dashboard.victorngoma.com · webhook = webhook.victorngoma.com · strategies = pine.victorngoma.com · runbook = runbook.victorngoma.com
- Cloudflare account: vngoma78@gmail.com
- Secrets location: your password manager (SSH passphrase, dashboard password, webhook secret)
SSH into the VPS▸
Run this on your Mac (Terminal app), not on the server:
ssh victor@165.227.84.78
Enter your SSH passphrase when prompted (it's in your password manager). You'll land at a prompt that reads victor@spx-server:~$ — that confirms you're on the VPS.
If the prompt still says victorngoma@Victors-MacBook-Pro, you're still on your Mac — the SSH didn't connect. Re-run the command.
Download the full journal CSV to your Mac▸
Run this on your Mac (NOT on the VPS — open a fresh Mac terminal, do not SSH in first):
scp victor@165.227.84.78:/home/victor/spx-server/journal/trades_journal_2026.csv ~/Downloads/
Enter your SSH passphrase. The file lands in your Mac's ~/Downloads/ folder.
Check if the trading server is running▸
sudo systemctl status spx-server
Look for Active: active (running) in green. Press q to exit the view.
Restart the trading server▸
sudo systemctl restart spx-server
Then confirm it came back up:
sudo systemctl status spx-server
View the trading server log (live)▸
Tail the log to watch incoming webhooks in real time:
sudo tail -f /var/log/spx-server.log
Press Ctrl + C to stop watching.
To see just the last 50 lines:
sudo tail -50 /var/log/spx-server.log
Check / restart the dashboard▸
sudo systemctl status spx-dashboard
Restart if needed:
sudo systemctl restart spx-dashboard
Weekly watchlist update (fool.com → strategy)▸
The Stocks Strategy trades the Stock Advisor Aggressive + Moderate "Buy" lists. Do this check about once a week:
- Open the Stock Advisor scorecard page on fool.com (Active tab).
- Select the whole page with Cmd+A, copy with Cmd+C. (Copy-paste beats screenshots — nothing gets misread or cut off.)
- Paste it to Claude with the message: "Weekly watchlist update — here is the fool.com paste."
- Claude runs stocks_strategy/update_universe.py against it and reports three lists: ADD (new recommendations), REMOVE (dropped ones), UNCHANGED.
- You approve. Rules Claude follows: a stock with an open position is never removed until it closes; multi-symbol rows (spinoffs, share classes) are flagged for your call, never auto-added.
- Claude updates the universe file, the trading server (deploy + restart), and regenerates the three TradingView scripts if tickers changed.
- Your only manual step: if scripts changed, re-paste them into TradingView (see the TradingView procedure below). New stocks only trade once their script group is updated there.
Master file: Local_Server/stocks_strategy/stocks_universe.json — every ticker with its sector fund, strategy (S1/S3/S6), and script group (A/B/C).
TradingView scripts & alerts (Stocks A / B / C)▸
TradingView limits one script to 40 watched symbols, so the stocks universe is split into three generated scripts: A = technology, B = consumer/financial/industrial, C = everything else. Each needs exactly one alert.
To update a script (after a watchlist change):
- Find the files in Finder → Dropbox → Mac → Downloads → Local_Server → pine: stocks_multi_strategy_A.pine, _B, _C.
- Open the changed file in TextEdit, Cmd+A, Cmd+C.
- In TradingView: Pine Editor (bottom of window) → open your saved script of the same letter → Cmd+A, Cmd+V → Save. The chart updates itself; the existing alert keeps working.
Creating an alert from scratch: alarm-clock icon → Create alert → Condition = the script → "Any alert() function call" → Notifications tab → tick Webhook URL and paste the webhook address (ends in ?secret=… — it's in your password manager, never in the script).
Each script also draws a small table on the chart's right edge showing its open positions. Settings → Inputs has checkboxes to hide it or show all stocks.
Restart a trading brain or panel (the safe way)▸
All four brains and the panels restart automatically only if they die "by accident". A polite stop looks intentional, so the supervisor leaves them dead.
After SSH'ing in (prompt must say victor@spx-server), restart the stocks brain like this:
kill -9 $(pgrep -f "stocks-server/.venv/bin/python stocks_server.py" | head -1)
Then confirm it came back (should print "ok":true):
sleep 8; curl -s http://127.0.0.1:5109/health
Ports: SPX brain 5005 · ETF brain 5107 · stocks brain 5109 · panels 5106/5108/5110/5111 · dashboard 5006.
Change position size or max open positions▸
Each brain reads a config file at startup. Current settings (2026-07-27): stocks = $1,000/position, max 15 open; ETF = $1,000/position, max 5 open.
- Stocks brain: /home/victor/stocks-server/stocks_config.json
- ETF brain: /home/victor/etf-server/etf_config.json
Edit the number (position_value or max_concurrent_positions), then restart that brain with the kill -9 procedure above. Changes affect new positions only — anything already open keeps its original size until it exits.
Easiest: just ask Claude — "set the stocks brain to $2,000 per position" — and it will edit, restart, and verify.
Manage member plans (tiers) in /admin▸
Members have a plan: Observer (Home, Strategies, Advisory) → Member (adds Live Dashboard, the four strategy pages, Backtests, Documentation, live positions) → Pro (adds Runbook). Owner and admin accounts always see everything.
- Go to victorngoma.com/admin.
- In the user table, use the Plan dropdown next to a member — it saves instantly.
- When inviting someone, pick their plan in the invite form.
The same page shows Recent visits: who opened the site, when (Chicago time), roughly how long, and how many pages.
Add a new user to a locked tool▸
- Go to Cloudflare dashboard → Zero Trust
- Access controls → Policies
- Click the policy for the tool (e.g. pine-allowed-users) — pick the one with Used by applications = 1
- Click Configure
- Under Include → Value, type the new email and add it
- Click Save policy
The new person can now log in to that tool via email PIN code. Each tool has its own policy/allowlist — add someone to one tool without affecting others.
To remove someone: same steps, click the × on their email chip, then Save.
Update a hub or subdomain page▸
- Edit the index.html file in the matching Mac folder (e.g. victorngoma-hub, victorngoma-pine)
- Cloudflare dashboard → Workers & Pages → click the project (e.g. victorngoma-pine)
- Create deployment (or drag-and-drop the updated folder)
- Upload the new index.html → Deploy
Keep the filename index.html — Cloudflare serves that as the page automatically. Keep each project's files in its own folder.
Add a brand-new locked subdomain▸
Part 1 — deploy the page:
- Make a new Mac folder with an index.html inside
- Cloudflare → Workers & Pages → Create application → Pages → "Get started" next to "Drag and drop your files"
- Project name (e.g. victorngoma-docs) → Create project → upload folder → Deploy
- Custom domains tab → Set up a custom domain → enter the subdomain (e.g. docs.victorngoma.com) → Activate
Part 2 — lock it:
- Zero Trust → Access controls → Applications → Add an application
- Self-hosted and private → Public DNS → Continue
- Destinations: Subdomain = the subdomain, Domain = victorngoma.com, Path = empty
- Access policies → Create new policy → name it, Action = Allow, Include → Selector = Emails → add emails → Save policy
- Authentication: "Accept all available identity providers" = ON
- Details: Name it, Session Duration 24h → Create
Test in a fresh Incognito window (or phone) — you should hit the Cloudflare Access email login. If it loads straight through, the lock isn't active.
Verify a locked page is actually protected▸
- Open a fresh Incognito window, or use your phone
- Visit the subdomain
- You should see the Cloudflare Access email login page
- An email NOT on the allowlist should be denied; an allowed email gets a PIN code by email
If the page loads with no login prompt, the lock isn't working — check the Application exists and its DNS record is orange-clouded (proxied).