← Trading methods

Trading operations on Windows

This procedure prepares the observer and private execution worker without changing the existing Wikipedia, learning, Browser Lab or chess services. Live execution is activated separately by the operator; check the current worker heartbeat before treating it as available. ETH and USDG funding and both direct Uniswap v3 quote routes were verified read-only on September 23, 2026; these observations are not completed trades or permanent readiness guarantees. The public trading-feed hostname below is an example; it has not been configured by this release.

Processes and private state

ComponentRoleCredentialsDefault data path
experiments/trading/server.mjsCollect quotes, record decisions, serve read-only HTTP/WSSPublic wallet address onlyexperiments/trading/data
experiments/trading/executor.mjsValidate new decisions, route orders, reconcile receiptsPrivate key; ZEROX_API_KEY only for the optional 0x venueexperiments/trading/execution-data
Vercel /trading/Public static viewerNoneNo private state

The observer does not need the execution environment file. Its connection to execution is a sanitized public snapshot, not an order API. The worker listens on no HTTP port. Keep every data path outside static web roots.

Use a dedicated limited Windows account for trading, separate from Administrator and existing service identities. Run observer and worker as separate tasks and load the private environment only in the worker. For separate observer and worker accounts, grant the observer access only to the required public projection through a deployment arrangement that preserves permissions on atomic file replacement; do not grant it inherited read access to the private execution directory. Validate this before enabling that arrangement.

Install and run the observer

Use a pinned, reviewed repository revision. The following assumes C:\ZebrafishNeuralTrading\app is that checkout and Node 24 is installed. Adjust the explicit paths to your host.

Set-Location -LiteralPath 'C:\ZebrafishNeuralTrading\app'
node --version
npm ci
npm run build:trading
npm run test:trading
npm run check

Start the observer with public settings only:

$env:ZNEURO_TRADING_WALLET_ADDRESS = '0xb3D6a92E7FfA565944375F344b55B31F7B9FD7de'
$env:ZNEURO_TRADING_DAILY_LOSS_USD = '50'
$env:ZNEURO_TRADING_DATA_DIR = 'C:/ZebrafishNeuralTrading/data'
$env:ZNEURO_TRADING_EXECUTION_DATA_DIR = 'C:/ZebrafishNeuralTrading/execution-data'
$env:ZNEURO_TRADING_ALLOWED_ORIGINS = 'https://zebraneural.com'
node --use-system-ca experiments/trading/server.mjs

The listener must be 127.0.0.1:4410. Check http://127.0.0.1:4410/healthz and http://127.0.0.1:4410/trading/. Quotes normally refresh every 15 seconds; a fresh start needs at least five minutes of observations. Decisions are saved on UTC quarter-hour boundaries. Do not interpret a HOLD or an empty execution record as a failed browser connection.

Keep certificate verification enabled. --use-system-ca uses the Windows trust configuration where needed; do not use NODE_TLS_REJECT_UNAUTHORIZED=0.

Create the private environment

Copy the template once, without overwriting an existing private file:

if (Test-Path -LiteralPath '.env.trading') { throw 'Private environment already exists; inspect it locally instead of replacing it.' }
Copy-Item -LiteralPath 'experiments/trading/.env.example' -Destination '.env.trading'

Restrict the file ACL before entering secrets. In an elevated local administration shell, replace ZebraTradingService with the actual limited account and inspect the resulting ACL:

$executionIdentity = "$env:COMPUTERNAME\ZebraTradingService"
icacls '.env.trading' /inheritance:r /grant:r "${executionIdentity}:(R)" '*S-1-5-18:(F)' '*S-1-5-32-544:(F)'
icacls '.env.trading'

The file should be readable only by the intended execution identity and trusted system/administration identities. Remove any unintended explicit grants if present; disabling inheritance alone does not remove pre-existing explicit grants. Keep private state, logs and backups similarly restricted. Do not display or paste the private key while checking permissions.

Edit .env.trading locally as the operator. Set the signing key; its address must match 0xb3D6a92E7FfA565944375F344b55B31F7B9FD7de. Keep the default uniswap-v3 venue for the pinned direct pools. It needs no exchange API key. Set these data paths to match the observer:

ZNEURO_TRADING_DATA_DIR=C:/ZebrafishNeuralTrading/data
ZNEURO_TRADING_EXECUTION_DATA_DIR=C:/ZebrafishNeuralTrading/execution-data
ZNEURO_TRADING_VENUE=uniswap-v3
ZNEURO_TRADING_LIVE_ENABLED=0
ZNEURO_TRADING_MAX_ORDER_USD=10
ZNEURO_TRADING_DAILY_LOSS_USD=50
ZNEURO_TRADING_SLIPPAGE_BPS=50
ZNEURO_TRADING_MAX_GAS_USD=0.25
ZNEURO_TRADING_MAX_GAS_WEI=50000000000000

The template fixes Robinhood Chain and the expected experiment wallet. Order and loss limits may be lowered, not raised above $10/$50. The default per-transaction fee bounds are 0.00005 ETH and $0.25; both must pass. The chain adapter also requires a 0.001 ETH gas reserve beyond the bounded fee. These values describe this deployment's constraints, not expected results.

The optional ZNEURO_TRADING_VENUE=0x path additionally requires ZEROX_API_KEY and 0x authorization for the requested Stock Tokens. The default direct route does not use that API key. Do not switch venues to treat an issuer eligibility restriction as resolved: Robinhood's Stock Token restrictions apply independently. The 0x route has its own RWA access requirements.

Keep this file on the private host. Never place signing or provider credentials in Git, a frontend build, Vercel settings, chat, browser storage or a public support log. Do not run the observer with --env-file=.env.trading.

Read-only preflight

ETH covers network gas. BUY uses USDG, so an ETH-only wallet cannot buy Stock Tokens through this worker. No automatic wrapping, bridging or ETH-to-USDG conversion is implemented. Confirm the actual on-chain USDG balance rather than a planned funding amount.

node --use-system-ca --env-file=.env.trading experiments/trading/executor.mjs --check

The command validates wallet/chain identity, venue-specific configuration, balances, official marks, saved evidence and unsigned routes for both assets. For direct v3 it calls the public QuoterV2 and validates the pinned router, factory, pools and canonical tokens. It cannot sign, approve or broadcast; its report includes transactionsSubmitted: 0. Exit code 0 means all checks passed at that moment; exit code 1 means readiness is incomplete. A quote may expire or liquidity may change after the check. Keep the environment gate at 0 while resolving missing configuration, USDG or routing.

A successful QuoterV2 call is a pool calculation, not a complete wallet simulation. The direct quote records that distinction. Before an actual swap, the worker separately checks balances and token restrictions, exact allowance and recipient, a fresh minimum output and deadline, then simulates the wallet transaction and estimates fees. Quotes are usable for at most 20 seconds; the encoded swap deadline is 30 seconds. Approvals and receipt waits must be followed by a fresh quote. Contract addresses and preparation evidence are documented in Trading methods.

Operator activation and stopping

After inspecting a passing preflight, only the operator changes the private file to ZNEURO_TRADING_LIVE_ENABLED=1 and runs:

node --use-system-ca --env-file=.env.trading experiments/trading/executor.mjs --live

Both --live and the environment gate are required. This starts real transaction capability; it is not another preview. The worker consumes fresh saved decisions after its startup and does not execute historical catch-up signals. The observer must continue producing those decisions.

Environment changes made with CMD set or PowerShell $env: belong to that terminal. A new terminal loads the private file's value again. From the repository directory, an operator can explicitly enable execution for one CMD child process (also callable from PowerShell) without editing the private file:

cmd /d /c "set ZNEURO_TRADING_LIVE_ENABLED=1&& npm run trading:execute"

This is a live start command, not a diagnostic. It must be run by the operator. A missing gate is separate from a bad signing key, an RPC outage or damaged state; none of those conditions should be addressed by deleting a checkpoint.

The terminal reports execution-state and decision changes, including HOLD, missing prices and skipped decisions. If the final price read fails before signing, the unsigned decision may be checked again on a later tick, up to three failed checks and only within its original 90-second validity window. Every attempt obtains a fresh wallet snapshot, prices and quote and repeats all limits. An expired decision, restarted unsigned intent or saved signed transaction is never replayed through this retry path. A confirmed token approval authorizes spending; it is not itself a stock purchase.

For initial validation, keep the worker in an operator-controlled shell. Ctrl+C requests a graceful stop. A submitted transaction can still confirm after the process stops; preserve state and reconcile it on restart. Setting the environment file back to 0 only affects subsequent starts, so also stop any running worker when disabling execution.

For unattended operation, create separate scheduled tasks under the limited account, with the explicit Node executable, working directory and arguments above. Choose "Run whether user is logged on or not," disallow parallel task instances, and disable the default task time limit. Start the observer independently. Leave the worker task disabled until the operator authorizes activation. Configure bounded failure recovery and review repeated failures; do not repeatedly clear checkpoints or ownership files to force a restart. Test RDP disconnection and reboot behavior before relying on unattended operation.

Stops, accounting and recovery

The $10 order cap is trade notional; gas is additional. SELL sizing uses a fresh ask mark. The $50 daily stop compares realized plus unrealized P&L minus fees with the first fresh valuation obtained on that UTC date. Once triggered it remains latched for that date, blocking all new buys, sells and approvals. Pending transactions continue receipt reconciliation. The worker does not liquidate open positions automatically, and market moves or pending fills can exceed the threshold.

The ledger assumes USDG equals one USD, uses token bids for positions, and uses the observed ETH/USD ask for gas conversion. Its first fresh daily valuation is the actual recorded baseline, not an invented midnight quote. A new day's baseline is established only when fresh marks are available.

Pending, confirmed and reverted transactions remain distinct. Confirmed receipt token deltas create fills; reverted transactions can charge gas without creating a position. Unknown receipts or nonce changes require review. Do not resubmit manually simply because a provider response was lost. An ownership claim left by an interrupted startup must be inspected against the actual process state before any operator recovery; never blindly delete a live lock.

Back up both data and execution-data, the pinned code revision and the private environment through protected host backups. Prefer stopping both tasks cleanly before taking a consistent backup, or use an application-consistent host backup process. The execution checkpoint can contain signed raw transaction bytes and belongs in private storage even though it does not store the private key. Preserve pending intents, receipts, ledger, processed decision IDs and observer hashes together.

For migration, stop the original worker, validate a consistent backup on the replacement host, and establish exactly one owner before activation. Never start two hosts against copies of the same execution wallet state. Reconcile preserved pending transactions before new orders. Do not replace corrupt evidence with an empty data directory. Keep existing project services and their data separate throughout the migration.

A saved USDG approval without a receipt

npm run trading:recover -- --check is read-only and needs no private environment file. It checks the checkpoint, exact signature identity, current nonce, receipt, allowance, gas and deployment restrictions. A reported fee-stage failure means the saved gas price is below the observed block base fee; it is not evidence of a completed approval or trade.

Only the operator may run the following recovery actions. Stop the execution worker with Ctrl+C first; keep the observer running. Recovery refuses a live owner, takes a verified private checkpoint backup, and preserves the original approval and decision. Do not delete the state or locks to bypass refusal.

For a still-valid signature, exact-byte rebroadcast is available with npm run trading:recover -- --rebroadcast ORIGINAL_HASH. If its gas price is insufficient, an explicit same-nonce fee replacement is available instead:

set "ZNEURO_TRADING_LIVE_ENABLED=1"
node --use-system-ca --env-file=.env.trading experiments/trading/recover-approval.mjs --replace-approval ORIGINAL_HASH

Replace ORIGINAL_HASH with the exact saved approval hash. This is a real financial action: it signs and submits only the same USDG approval, changing its gas price while retaining its nonce, spender, amount and gas limit. It cannot place a stock order. Fresh checks retain the $10 approval amount limit, $0.25 maximum gas valuation and ETH reserve. It allows only one saved replacement candidate, stores its signed bytes before submission and leaves any unknown result for reconciliation. Neither candidate is blindly resent by a restart.

After a successful submission or a report that a receipt is available, the operator can run npm run trading:execute in the same terminal. The updated worker checks both hashes, accounts only the verified winning receipt and marks the other signature replaced. It clears only the matching transaction uncertainty; other review stops stay in force. An expired decision is skipped after approval confirmation, so a fresh eligible model decision is required for the next purchase. A fifteen-minute decision interval does not guarantee a trade every fifteen minutes.

Publish the read-only feed

Use a stable subdomain such as trading-feed.zebraneural.com pointed to the host's stable address. Add only the new proxy block; preserve existing feed, learning, chess and play routes. The following is an example, not an applied configuration:

trading-feed.zebraneural.com {
    @readOnly {
        method GET HEAD
        path /healthz /api/state /api/decisions /api/decisions/* /ws
    }
    handle @readOnly {
        reverse_proxy 127.0.0.1:4410
    }
    handle {
        respond "Not found" 404
    }
}

Caddy preserves the WebSocket upgrade and Origin header. The observer separately permits only its configured website origins; keep ZNEURO_TRADING_ALLOWED_ORIGINS=https://zebraneural.com unless another exact origin is intentionally added. Do not rewrite all Origins to bypass the check.

Expose HTTPS on 443, and HTTP 80 only as needed for the supervised proxy's certificate/redirect configuration. Keep 4410 private in both Windows Firewall and the provider firewall. Do not add an execution port: the worker has none. Keep administrative access separate. Publish no directory listings, environment files, checkpoints, raw transactions or order routes.

After trusted HTTPS and WSS work, set the static frontend's dist/trading/trading-config.json to:

{"endpoint":"https://trading-feed.zebraneural.com"}

npm run build:trading preserves an existing valid endpoint configuration. Deploy only the static frontend to Vercel; no keys or execution data belong there. Verify the new endpoint before public cutover. Until then, the viewer must show an unavailable or prepared state rather than claim live execution.

Check /healthz, /api/state, paginated /api/decisions, a full decision JSON and wss://trading-feed.zebraneural.com/ws from outside the host. Verify forbidden paths and raw port 4410 are inaccessible, market age and worker heartbeat update independently, and pending transactions are not shown as fills. Confirm the existing site and its other feeds still work. A fresh price feed does not establish a fresh execution heartbeat.