CompoundFox: live algo trading with a public, defensible performance record.
Autowealth is a SaaS platform that distributes algorithmic trading strategies to followers across Bybit, Binance, and Hyperliquid. CompoundFox is the flagship strategy, live since November 2025 with a logic revision shipped in February 2026 and a risk-per-trade increase currently rolling out. Live performance is published at stats.autowealth.trade: equity curve, Sharpe, deposit-adjusted drawdown, and TWR calculated in real time from actual Bybit trade history. No backtest, no screenshot. Per-community ECS containers, an on-chain fee Splitter, and an AUM circuit breaker make the platform multi-tenant and production-grade.
Most algo strategies are sold on backtests or cherry-picked screenshots. Autowealth publishes live performance: full equity curve, Sharpe, drawdown, and TWR pulled from real Bybit trade history and updated daily on a public dashboard. Anyone can check it before committing capital. CompoundFox is the flagship strategy. Autowealth is the platform behind it: copy trading across three exchanges, per-community deployment, and on-chain fee invoicing.
┌─ Turborepo monorepo
│
├── apps/
│ ├── backend [ Bun · TypeScript · Drizzle · PostgreSQL ]
│ │ Bybit API integration · AUM snapshotting · performance calculation
│ │ on-chain fee invoicing · position deduplication
│ │ notification router (Expo push + Telegram fallback)
│ │ community auto-provisioning (HD wallet + splitter approval worker)
│ │
│ ├── telegram-bot [ Telegraf · TypeScript ]
│ │ strategy signals · performance alerts · follower management
│ │
│ ├── telegram-mini-app [ Next.js 16 · React 19 · Recharts ]
│ │ in-Telegram dashboard · deep-linked from bot commands
│ │
│ ├── performance-page [ Next.js · React Query ]
│ │ live equity curve · Sharpe · deposit-adjusted drawdown · TWR
│ │ startBalanceReliable flag: hides metrics it can't defend
│ │
│ └── mobile [ Expo · React Native · Clerk · MMKV ]
│ strategy overview · performance charts · push notifications
│ App Store build via EAS
│
├── packages/
│ ├── database [ Drizzle · PostgreSQL · 7 migrations ]
│ ├── wallet-service [ ethers v6 · HD derivation · async-mutex ]
│ │ gas station per chain · refill below threshold
│ │ deterministic community wallets via keccak256(id + secret)
│ └── sweep-service [ Solidity Splitter · Hardhat · BSC + Arbitrum ]
│ atomic distribution to provider / platform / referral
│
└── cdk/ [ TypeScript · AWS CDK ]
ExchangeBotStack per community · shared VPC/cluster via CF exports
512MB / 0.25 CPU per bot · VPS Docker alternative
Raw AUM drawdown inflates whenever a deposit lands: AUM spikes, then the strategy looks like it immediately lost capital. The performance engine walks a 3-day attribution window to match transaction amounts to AUM jumps before computing peak-to-trough. The window exists because of a production failure: late-day deposits landing after the daily snapshot produced phantom -100% drawdowns until the attribution logic was added.
The performance API returns a startBalanceReliable flag when there is no AUM snapshot before the first trade. When the flag is false, the UI hides annualized return and average monthly return entirely. The dashboard doesn't fill in a guess; it shows fewer numbers. Accounts that connected mid-strategy get a smaller metrics set until a full pre-trade snapshot exists.
A snapshot job that retries every failing account wastes cycles on expired keys and slows down active ones. The AUM snapshot worker tracks consecutive failures per account in a Map<string, number>, skips accounts with 3+ consecutive failures, and resets the count hourly. An isRunning guard prevents concurrent execution without a lock library. One expired API key has no effect on the snapshot cadence of live accounts. The job runs every 5 minutes (288 snapshots/day per active account).
Not all exchange APIs return stable order IDs across paginated history endpoints. Bybit's position history in particular doesn't guarantee idempotent IDs. The closed-position worker uses a 5-field composite key: exchange + account UID + symbol + side + timestamp + PnL formatted to 8 decimal places via formatToPrecision8(), matching the decimal(18,8) DB column. The fingerprint check in positionExists() runs before every write. Positions are fetched in 7-day chunks with 100ms rate-limit delays to stay within Bybit's pagination limits.
Each trading community gets its own exchange-bot container, not a shared bot with multi-tenant config. The CDK stack (ExchangeBotStack) takes a communityId as a prop and deploys into a shared ECS cluster imported via CloudFormation cross-stack exports (InfrastructureClusterArn, InfrastructureVPCId). Adding a community is a deploy-exchange-bot.sh COMMUNITY_ID TOKEN call. For operators who want lower AWS costs, a Docker VPS alternative runs the same container at 512MB / 0.25 CPU with the same isolation guarantee per community ID.
Trading fees invoiced to followers go into a Hardhat-tested Solidity Splitter contract on BSC and Arbitrum that distributes to provider, platform, and referral wallets atomically. Each trader's wallet is derived deterministically via BSCWalletService.deriveTraderWallet(traderId, communityId), so wallets don't need manual provisioning per follower. The pipeline runs fully automated: position close triggers invoice, invoice triggers on-chain sweep. Coverage data shows 40+ contract statements exercised in tests.
On-chain operations stall when a wallet runs out of native gas. The wallet-service package runs a gas station per chain (BSC and Arbitrum): it monitors every operational wallet, refills below threshold (0.0002 BNB / 0.0001 ETH) from a dedicated funding wallet, and uses async-mutex to serialize refills so concurrent operations never trigger duplicate transfers. Two modes: withGas() waits for the refill to confirm before the dependent transaction; withGasHybrid() releases the caller as soon as gas lands on-chain. If the funding wallet itself is depleted, callers get a clean error instead of a silent stall.
Creating a community is one POST /communities call. The backend derives a deterministic HD branch from keccak256(communityId + WALLET_DERIVATION_SECRET) mod 2^31, persists the address, and queues splitter approval. A background worker then approves the Splitter contract on BSC and Arbitrum for that wallet, gated by an AUM threshold so dormant communities don't burn gas. The splitter_approved and arb_splitter_approved flags in community_bsc_wallets make the worker idempotent. No operator ever touches a key, and the derivation is deterministic, so the address is reproducible from communityId alone if state is ever lost.
The notification router fans events out by user preference (Expo push, Telegram, or both), but security-critical events (API key changes, withdrawals, delinquency) always go to both channels regardless of preference. Push tokens flagged as invalid by Expo get deactivated automatically on the next send failure, so dead devices don't accumulate and concurrent sends don't waste retry budget. Critical alerts never depend on a single transport.
CompoundFox is running live on Bybit with performance published at stats.autowealth.trade.
Equity curve, Sharpe, deposit-adjusted drawdown, and TWR are calculated from real position
history by a metrics engine built from scratch. The numbers on the dashboard are
either defensible or hidden: the startBalanceReliable flag suppresses
annualized return on accounts without a pre-trade AUM snapshot. Autowealth as a platform
handles multi-tenant copy trading across three exchanges, per-community ECS deployment via
CDK, on-chain fee settlement with a multi-chain gas station keeping operational wallets
funded, zero-touch HD wallet provisioning when a community is created, and a notification
router that fans security-critical events to push and Telegram in parallel. Four user
surfaces ship from one monorepo: web dashboard, Expo mobile app, Telegram bot, and an
in-Telegram mini-app.