← Back to blog index · 2026-05-10

Building Yieldsforge — Architecture, Deploy Pitfalls, Design Decisions

Complete technical writeup of Yieldsforge. Tech stack, why Bitfinex's region restriction is a Railway-default-region trap, why we picked NOWPayments over Stripe, multi-user isolation design. Transparency = trust.

Previous post covered the structural reasons users churn from Coinlend. This post is Yieldsforge’s technical record — architecture decisions, pitfalls hit, tech stack choices.

For two audiences:

  1. Potential users who want to know “what’s inside Yieldsforge” — transparency = trust
  2. Devs considering similar SaaS — my mistakes can save you time

TL;DR Tech Stack

  • Core strategy: Python + numpy/pandas (lending_bot/)
  • Web: FastAPI + fastapi-users (saas/web/)
  • Worker: ARQ + Redis (saas/worker/)
  • DB: PostgreSQL (multi-tenant)
  • Frontend: Astro + Tailwind (marketing/) + plain HTML+JS (saas dashboard)
  • Deploy: Railway (3 services + Postgres + Redis)
  • Billing: NOWPayments (USDT crypto payments)
  • Auth: Google OAuth + email/password fallback
  • Monitoring: Sentry
  • Backtest: custom-written walk-forward + fill model + prepayment model

Architecture Decision 1: Strategy as Standalone Module

Design goal: lending_bot/ module usable independently (personal CLI), or wrapped into multi-user SaaS.

Modules:

  • collector.py: pull funding book + funding stats from Bitfinex
  • decision_tree.py: multi-bucket allocation + floor + ladder logic
  • order_manager/: place + cancel + re-place
  • strategy_engine.py: tie above together, run on 60-second tick

Benefit: strategy logic can be backtested independently without spinning up the full web stack.

Architecture Decision 2: Backtest Engine Determines Strategy Quality

Before writing the walk-forward backtest, our strategy (like most funding bot devs’) was based on intuition. Three blindspots only became visible after the backtest:

  1. Single-30d looks best, but loses to multi-bucket after prepayment (detail)
  2. Floor shouldn’t be uniform across symbols — fUSD has spikes, fUST doesn’t, need per-symbol tuning
  3. “candle.low fills” assumption is severely over-optimistic — real queue competition is harsher

Each backtest exposed a wrong assumption. Final strategy looked nothing like v1.

Full backtest results in this post.

Architecture Decision 3: ARQ + Redis for Multi-User Ticks

Initial idea: single worker process running all users’ ticks per minute. Simple but problematic:

  1. 100 users × 60s tick = 1.7 calls/sec to Bitfinex (rate limit risky)
  2. One user’s tick stuck blocks everyone
  3. Bitfinex API timeouts need retry logic

Switched to ARQ + Redis, every (user, symbol) tick is an independent job:

  • Scheduler enqueues all jobs every minute
  • Worker pool processes concurrently (max 20 today)
  • ARQ job_id dedups to prevent double-enqueue per minute

Deploy Pitfall 1: Bitfinex Returns HTTP 451 to US IPs

Most painful technical trap.

Railway defaults to us-west2. First user tick from there got rejected by Bitfinex. Reason: Bitfinex doesn’t serve US jurisdictions, blocks by IP.

This is a “Yieldsforge on Railway’s default region” pitfall — Coinlend / Cryptolend etc. are German / EU operations with EU infrastructure, never hit this from day one.

Fix: workers must run in asia-southeast1. Multi-region Railway deploy + verify all outbound Bitfinex traffic exits SG. Web service can stay in us-west2 (no Bitfinex calls).

This decision became a hard rule in CLAUDE.md. Never move worker back to US region.

Deploy Pitfall 2: Multi-User Isolation

The “things that explode” portion of the build. Multi-tenant SaaS always carries cross-user data leak risk.

Design:

  1. API key encryption: Fernet symmetric encryption + global master key, decrypt only briefly during worker tick
  2. All DB queries forced to user_id: enforced at ORM layer (every SQLAlchemy query carries user_id where clause)
  3. Funding-only verification: when user submits a key, auto-test permissions; reject any key with withdraw

Every API endpoint has cross-user fuzz test: “if user A brings user B’s cookie, can they get B’s data?” Answer must always be no.

Design Decision: NOWPayments Over Stripe

Stripe has the best dev experience but is sensitive to “crypto-related services” and may ban. Crypto-adjacent businesses on Stripe either get rejected outright or have accounts frozen later.

NOWPayments:

  • Accepts USDT / USDC / BTC / ETH crypto
  • 0.5% transfer fee (vs Stripe 2.9% + $0.30)
  • Users don’t need credit cards, matches our crypto-native audience

Cost: dev pain higher than Stripe (sparse docs, IPN webhook signature verification complex).

Design Decision: Astro Marketing + Plain HTML Dashboard

Marketing site (/, /blog, /privacy): Astro + Tailwind. Reasons: SSG, SEO-friendly, fast build, TypeScript-friendly.

Dashboard (/dashboard/*): plain HTML + vanilla JS, no React. Reasons:

  • Data is server-rendered + 60s polling — no need for reactive framework
  • No build step, fast deploy
  • Small bundle
  • All charts are inline SVG, no chart library imported

Might trigger React believers, but for dashboard-level complexity, vanilla JS is plenty.

i18n: Strict zh-en Parity

All strings go through translation table, no hardcoding. zh-en parity 100% — because:

  • Bilingual SEO indexing
  • Maintenance consistency (drift makes the brand look unprofessional)
  • Easy to add languages (Spanish, Japanese coming next phase)

Implementation: i18n.py is a dict, t(key, lang) looks up, no i18n framework. Simple but effective.

Post-Ship Iteration (May 2026 onward)

What shipped after launch:

  1. Per-symbol floor upgrade — fUST APY 7.6% → 17.6%
  2. Cancel_misaligned floor-aware — auto-reprice when floors change
  3. Adaptive floor Phase 1 — MarketStats table accumulating data, Phase 2 will auto-tune
  4. Dashboard major redesign — depth charts, donuts, pressure bars
  5. 20 blog posts in this content batch (yes, this one)

Roadmap (H2 2026):

  • USDC funding pair
  • Adaptive floor opens for user opt-in
  • Partial source code open
  • Mobile-friendly dashboard

Advice for Devs Considering a Funding Bot SaaS

Real advice you won’t see in startup blog posts:

  1. Bitfinex region restriction is a deal-breaker — confirm you can deploy in asia region before starting
  2. Multi-tenant SaaS is 10x more complex than personal script — auth + billing + cross-user isolation + monitoring, each must be done right
  3. Don’t try Stripe for crypto-adjacent business — high ban risk, go directly to NOWPayments
  4. Without backtest, you don’t know your strategy is bad — intuition strategies typically underperform tested ones by 2-5pp
  5. Open-sourcing modules is marketing — transparency beats ads

If you only want to use it personally / share with friends, don’t build SaaS. Write a personal script and invite friends to ssh in. SaaS legal + ops cost isn’t worth it under 10 users.

Yieldsforge 7-day free trial →


Disclosure: I’m the developer of Yieldsforge. Technical details based on actual repo code; roadmap is current plan. Not investment or development advice.

Want to put your USDT on autopilot?

7-day free trial. No credit card required.

Start free trial