fintech · backend · web · real-time
Ongold — live gold trading platform
A real-time gold trading platform in Toman: Go trading engine with spot/pending orders, SL/TP, auto-close and wallets, streamed to a Next.js 16 web app over WebSocket.
- Role
- Sole developer — backend, trading engine, web and admin
- Period
- Apr 2024 – Present
- Client
- Ongold
- Status
- Live
- Go
- Gin
- PostgreSQL
- Redis
- WebSocket
- Next.js
- TypeScript
- React
- JWT
- Apache
1
Developer
Real-time
Price updates
Spot · Pending · SL/TP
Order types
The problem
Ongold needed a trading platform where users buy and sell gold priced in Iranian Toman, in real time, with the same tools traders expect from forex platforms: pending orders, stop-loss / take-profit, multiple lots and automatic protection when a portfolio hits a critical loss. Money is involved, so correctness under concurrency mattered more than anything else.
What I built
I was the only developer on the project, end to end:
- Backend — a Go monolith on the Gin framework with a PostgreSQL schema for users, wallets, orders, transactions and referrals.
- Trading engine — spot and pending execution, SL/TP triggers, portfolio auto-close on a loss threshold and multi-lot positions.
- Real-time layer — a WebSocket server that pushes prices and order updates to every connected client.
- Wallet & referrals — deposits, withdrawals, balance management and referral commission tracking.
- Web app — Next.js 16 + TypeScript, fully responsive.
- Admin — a React dashboard for user and order management.
Architecture
Price feed ──► Go service ──► Redis (latest prices, locks)
│ │
├── PostgreSQL ◄──────┘ orders · wallets · transactions
│
└── WebSocket hub ──► Next.js web app / adminWhy a monolith?
One developer, one deployable. A well-structured Go monolith gave me fast iteration, simple transactions across wallets and orders, and no distributed-systems tax. Packages are split by domain (orders, wallets, pricing, referrals) so pieces can be extracted later if load demands it.
Concurrency and money
Every order mutation takes a short-lived Redis lock on the user's portfolio before touching PostgreSQL, and the wallet update and order state change happen in the same database transaction. That combination prevents double fills and negative balances when price ticks, user actions and auto-close all race each other.
func (s *Service) Execute(ctx context.Context, orderID int64) error {
unlock, err := s.locks.Acquire(ctx, lockKey(orderID), 3*time.Second)
if err != nil {
return ErrBusy
}
defer unlock()
return s.db.WithTx(ctx, func(tx pgx.Tx) error {
order, err := s.orders.ForUpdate(ctx, tx, orderID)
if err != nil {
return err
}
price := s.prices.Latest(order.Symbol)
if !order.ShouldTrigger(price) {
return nil
}
if err := s.wallets.Settle(ctx, tx, order, price); err != nil {
return err
}
return s.orders.MarkFilled(ctx, tx, order.ID, price)
})
}Results
- Live in production, deployed on Linux behind Apache, with ongoing maintenance and monitoring.
- Real-time prices and order updates for every user without polling.
- A codebase a single engineer can reason about — and extend.
Placing a pending order with stop-loss and take-profit.
React admin dashboard for users and orders.
Paygus — PropTech intelligence platform
2020Seven platform generations in five years: from React Native + PHP to Next.js, Directus, GIS map layers and a Go + Redis core for a subscription real-estate intelligence product.
- Next.jsTailwind CSSGoRedis
ChatrFinance — real-time market dashboard
2023Live prices for gold, currencies, crypto, stocks, commodities and forex — Node.js + MongoDB backend, WebSocket streaming, Flutter apps and TradingView charts.
- Node.jsExpressMongoDBSocket.io
Ferzz — on-demand delivery apps
Two-sided delivery platform with separate customer and courier apps, real-time tracking and in-app chat — rewritten from React Native to Flutter.
- FlutterReact NativeDartPHP