A personal investing platform served at https://coin.bobagi.space. It unifies two previously separate projects into one multi-user application:
- Crypto — log and automate Binance trades (market buy, take-profit limit sell, daily DCA, price alerts) with per-user, encrypted API credentials. Testnet by default; live trading is an explicit per-user opt-in.
- B3 portfolio — read an Investidor10 public wallet to show holdings, order history, and upcoming dividend (data-com) dates.
Merges two previously separate projects — a Go crypto trading app and the Python
Bobagi/investidor10reader — into one repo.
Coin Hub's automation is built around trading robots. A robot is one automated bot bound to a
single coin/pair in one environment (e.g. BTCBRL on Testnet). It is not a black box that
"prints money" — it mechanically executes a strategy you configure, and it carries real risk.
A robot combines three well-known, conservative mechanics:
- DCA (Dollar-Cost Averaging) — the daily buy. Once a day, at the time you choose, the robot market-buys a fixed amount ("Capital per buy") of the coin, regardless of price. Buying steadily smooths out volatility and your average entry price instead of trying to time the market.
- Take-profit — the automatic sale. Every buy immediately places a limit sell order a set percentage above its purchase price ("Target profit %"). When the market reaches it, that lot is sold for a profit, automatically.
- Stop-loss (optional) — the safety exit. If a position's price falls a chosen percentage below its buy price ("Stop-loss %"), the robot cancels the take-profit and sells at market to cap the loss. Leave it empty to disable.
So each daily DCA buy becomes its own tracked position with its own take-profit (and optional stop-loss). The robot reviews open positions about every 30 seconds.
- Not an arbitrage bot. Arbitrage means buying on one exchange and selling on another to pocket the price gap. Coin Hub connects to one exchange per user (Binance), so cross-exchange arbitrage is impossible here by design — and on the major exchanges that gap is now < 0.5% anyway.
- Not a grid-trading bot. Grid bots place many staggered buy/sell orders across a price range to scalp sideways markets. Coin Hub does scheduled DCA + one take-profit per lot, not grid scalping.
- Not a profit guarantee. "3% a day on autopilot" is marketing, not reality. Returns depend on the market, the coin and your settings. Only allocate money you could tolerate losing.
| Setting | What it does |
|---|---|
| Coin/pair | The market the robot trades (e.g. BTCBRL). It buys using the quote currency — the asset at the end of the pair (BTCBRL → BRL, BTCUSDT → USDT) — so keep enough of it in your Binance spot wallet. |
| Capital per buy | How much of the quote currency each daily DCA buy spends. 0 disables the daily buy. |
| Max invested | The most the robot keeps invested in this coin at once (cost basis of open positions). When it's reached, the daily buy pauses and resumes only after a position sells to free up room. 0 = no limit. |
| Target profit % | How far above the buy price the automatic take-profit sell is placed. |
| Stop-loss % | How far below the buy price to market-sell as a safety exit. Empty = disabled. |
| Daily buy time | The time (your timezone) the one daily DCA buy runs. |
| Sell-order validity (days) | How long a take-profit order stays open before auto-cancelling. 0 = never expires (GTC). |
| Environment | Testnet (practice, fake money) or Production (real money, requires opt-in). |
- Per-order ceiling: every buy — manual or robot — is capped server-side at
MAX_ORDER_QUOTE_AMOUNT(default 100,000 in the quote currency). It's a global anti-mistake / anti-abuse guardrail, not a per-robot setting; its value is shown as a help line in the robot editor. - Per-robot max-invested ceiling: the Max invested setting (above) caps the total kept invested
in a coin at once. The robot's daily buy pauses when open positions for that coin reach the cap and
resumes once one sells to free capital.
0= no limit. (Manual buys are not blocked by this cap — only the robot's automatic daily buy is.)
Standard accounts get 1 robot per environment; admins get unlimited (a monetization hook — billing isn't built yet).
- Sign-in: email + password (bcrypt) or Google sign-in. Email verification is enforced — unverified accounts can browse but every save (connect Binance, trade, robots) is blocked. Password reset + email verification go out over Gmail SMTP. Sessions are opaque tokens in a Secure httpOnly cookie; only the SHA-256 hash is stored. Money actions require a fresh step-up ("sudo") re-auth.
- Consent (Terms of Use + Privacy Policy): a new account must accept the Terms + Privacy before
using anything — a blocking gate, with the acceptance recorded server-side (version, timestamp,
IP, user-agent). The version is bumped when the text changes materially, forcing re-acceptance. The
full documents are public and permanent at
#/termsand#/privacy; the account page shows which version you accepted and when. The API also refuses money/robot actions without an on-record acceptance (403 terms_not_accepted). - Your data & privacy (LGPD-aligned): Binance API keys are encrypted at rest (AES-256-GCM) and used
only to place the orders you configure — use trade-only keys with withdrawals disabled. Account
deletion is a privacy-preserving hard delete (PII + keys erased; only a non-identifying HMAC audit
row remains). A durable sign-in/access log (IP + offline city geolocation + device) powers
new-device email alerts. See the full Privacy Policy in-app and the risk/compliance review in
legal-audit-2026-06-21.md. - Not custodial, not advice: Coin Hub never holds or can withdraw your funds; it is software you configure, not a broker, fund manager or investment adviser, and is not affiliated with Binance.
coin.bobagi.space ──nginx(TLS)──▶ web (Svelte+Vite SPA, static dist/)
└────▶ api (Go, 127.0.0.1:5020) ──▶ Postgres (internal)
│
└─▶ scraper (Python/Flask + Selenium)
nginx serves the SPA's static dist/ directly and reverse-proxies /api,/auth,/health to the Go
API on 127.0.0.1:5020. The DB has no host port (internal only).
| Path | Service | Stack | Role |
|---|---|---|---|
apps/api |
api |
Go (SOLID, single binary, distroless) | Trading engine + REST API + auth (core) |
apps/scraper |
scraper |
Python 3.11 / Flask + Selenium | Scrapes Investidor10 wallets (internal-only service) |
apps/web |
web |
Svelte 4 + Vite + TypeScript (static SPA) | Dashboard SPA with real charts; built to dist/, served by nginx |
migrations |
migrate |
golang-migrate SQL | Versioned DB schema (0001..00NN) |
deploy |
— | nginx vhost reference | Ops reference |
The robots are driven by a background automation worker inside the API process: a monitor loop (~30s) that reconciles take-profit fills, runs stop-loss and expires stale sell orders, plus a daily-purchase loop that runs each robot's DCA buy once a day. Without it, robots are just config rows.
- Liveness & alerting. The worker writes a heartbeat every tick (
worker_heartbeattable).GET /health/workerreturns 503 when the heartbeat is stale — point an external uptime monitor at it — and a built-in watchdog emails admins if the worker stalls while the process is still alive. - Operational status in the UI.
GET /api/v1/system/statusaggregates worker liveness + the shared Binance rate-limit gate. The light next to "Binance" in the header turns red (green = OK) whenever automation is paused, with the reason on hover plus a banner; a manual buy/sell during a Binance cooldown fails fast with a clearbinance_busymessage instead of hanging. The Terms disclose that automated operation may pause and resumes automatically once the condition clears. - Singleton worker, scaling & parallelism. The worker runs only on the replica holding a Postgres
advisory lock (
LeaderLock), so the stateless HTTP API scales behind a load balancer without every replica double-executing daily buys/stop-loss. To actually run workers in parallel, setWORKER_SHARD_COUNT=Nand deploy N instances withWORKER_SHARD_INDEX0..N-1 — each processes a disjoint slice of users (id % N == index) under its own per-shard lock. Because the Binance weight limit is per IP, parallel workers only help when they egress from different IPs: give each shard its ownBINANCE_HTTP_PROXY. DefaultWORKER_SHARD_COUNT=1= one worker, unchanged. (At the current handful of users this gives no benefit — the bottleneck is the per-IP limit, which the market-price WebSocket + price cache already relieve — so the mechanism exists but is meant for real volume.)
cp .env.example .env # then fill in real values
docker compose up --build # db + migrate + scraper + api
# API: http://localhost:5020
# Scraper is internal-only (http://scraper:5000 from the API)Apply a new migration: add NNNN_name.up.sql / .down.sql under migrations/, then
docker compose up migrate.
Live today at https://coin.bobagi.space: monorepo + compose; multi-user auth (email + Google, enforced email verification, password reset, step-up re-auth, access/sign-in history); per-user encrypted Binance credentials with testnet/production isolation; manual buy + take-profit + manual close; trading robots (per-coin DCA + take-profit + optional stop-loss) run by a single-process automation worker; the Svelte SPA with the design system, real charts (allocation donut, price/profit history), pagination, toasts and trilingual i18n; the B3/Investidor10 portfolio (admin-only); the Terms + Privacy consent gate with public legal pages; and a privacy-preserving account deletion.
- Secret rotation + git-history purge — Binance/DB/email creds were committed in history; rotation
still pending (do not rotate
CREDENTIALS_ENCRYPTION_KEYwithout a re-encrypt migration). - WebSocket streams — market-price stream is live (pushes prices into the shared cache; REST is the fallback). User-data (order-fill) push is blocked by Binance: the classic listenKey endpoint is retired (HTTP 410), so real-time fills now require Binance's new WebSocket-API session, which needs Ed25519 keys (the app uses HMAC keys today) — a follow-up. Meanwhile the 30s poller reconciles fills correctly.
Leader lock before running >1 API replica— done: the worker is now a singleton via a Postgres advisory lock (LeaderLock), so the API is safe to scale. Next, only when volume needs it: shard users across worker instances.- Real server-side pagination for Positions/History; drop the vestigial
trading_robots.daily_purchase_enabledcolumn; remove dead legacy single-user services.
- Monetization: paid robots beyond the free tier (standard users are capped at 1 robot/env today)
- advertising — both need the items in
legal-audit-2026-06-21.mdfirst (a billing/subscription system with a 7-day-withdrawal cancel/refund flow, a cookie-consent banner — already built and dormant behindstores.adsEnabled— and the legal/fiscal setup below).
- advertising — both need the items in
- 2FA (TOTP), per-user email price alerts, more charts (PnL over time, dividend calendar).
- Operator/legal (not code): lawyer review + a CVM opinion on the paid robot, a CNPJ + invoicing,
and AdSense eligibility — tracked with status in
legal-audit-2026-06-21.md§3/§3a.
- Per-user Binance secrets are encrypted at rest (AES-256-GCM) and never logged.
- Use trade-only Binance API keys (withdrawals disabled).
- New users start on Binance Testnet; live trading requires explicit opt-in.
- Automated trading carries real financial risk; risk controls (stop-loss, per-order + per-robot caps) are built in. Returns are never guaranteed.
- See Accounts, security & legal above and
legal-audit-2026-06-21.mdfor the full security/compliance picture and the operator action list.