docs: add regulated products guides -- ATS, BD, TA from luxfi OSS components

This commit is contained in:
2026-03-22 12:44:46 -07:00
parent 51c1d45cf8
commit 44943c5403
8 changed files with 1212 additions and 0 deletions
+1
View File
@@ -20,6 +20,7 @@
"cex",
"broker",
"compliance",
"regulated",
"---Cross-Chain---",
"warp",
"teleport",
+252
View File
@@ -0,0 +1,252 @@
---
title: Alternative Trading System (ATS)
description: Build an SEC-registered ATS with CLOB matching, compliance, and multi-venue routing
icon: Building2
---
An Alternative Trading System (ATS) is an SEC-registered trading venue that matches buyer and seller orders internally. Unlike a national securities exchange, an ATS operates under Regulation ATS (Rules 300-303) and files Form ATS-N. It must maintain at least $250,000 in net capital and comply with fair access, order display, and record-keeping obligations.
The Lux stack provides every technical component needed to build and operate an ATS.
## Architecture
An ATS uses all four core modules:
```
┌────────────────────┐
│ atsd │
│ :8080 │
└─────────┬──────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ Broker │ │ CEX Engine │ │ Compliance │
│ Providers │ │ CLOB Match │ │ KYC / AML │
└──────┬──────┘ └──────┬──────┘ └─────────────┘
│ │
16 external venues Internal order book
```
- **Broker** provides external venue connectivity for routing unmatched orders and sourcing liquidity
- **CEX Engine** runs the central limit order book that matches orders internally
- **Compliance** screens every participant (KYC) and every transaction (AML) before execution
## Step-by-Step Build
### 1. Create Go project
```bash
mkdir atsd && cd atsd
go mod init github.com/yourorg/atsd
go get github.com/hanzoai/base
go get github.com/luxfi/broker
go get github.com/luxfi/cex
go get github.com/luxfi/compliance
```
### 2. Register providers from environment
```go
import (
"github.com/luxfi/broker/pkg/provider"
"github.com/luxfi/broker/pkg/provider/envconfig"
)
registry := provider.NewRegistry()
n := envconfig.RegisterFromEnv(registry)
slog.Info("providers ready", "count", n)
```
`RegisterFromEnv` reads environment variables for all 16 providers and registers whichever ones have credentials set. See [Provider Integration](/docs/regulated/providers) for the full list.
### 3. Create the broker server
```go
import "github.com/luxfi/broker/pkg/api"
brokerSrv := api.NewServer(api.Config{
Registry: registry,
ListenAddr: ":8090",
})
```
### 4. Create the CEX matching engine
```go
import "github.com/luxfi/cex/pkg/engine"
eng := engine.New(engine.Config{
EnableSurveillance: true,
EnableAudit: true,
})
```
### 5. Register markets
```go
import "github.com/luxfi/cex/pkg/markets"
// Auto-discover tradable assets from all connected providers
markets.RegisterFromProviders(ctx, registry, eng)
// Add explicit default markets with custom fee schedules
markets.RegisterDefaults(eng, []markets.DefaultMarket{
{Symbol: "BTC-USD", Base: "BTC", Quote: "USD", Class: types.AssetClassCrypto,
Tick: "0.01", Lot: "0.00001", MakerFee: "0.001", TakerFee: "0.002"},
{Symbol: "ETH-USD", Base: "ETH", Quote: "USD", Class: types.AssetClassCrypto,
Tick: "0.01", Lot: "0.0001", MakerFee: "0.001", TakerFee: "0.002"},
})
```
`RegisterFromProviders` queries every registered provider for tradable assets and adds them to the matching engine. `RegisterDefaults` adds specific markets with explicit parameters (tick size, lot size, fees).
### 6. Install compliance rules
```go
import (
"github.com/luxfi/compliance/pkg/aml"
"github.com/luxfi/compliance/pkg/jube"
)
// Install FinCEN-mandated baseline rules
monitoringSvc := aml.NewMonitoringService()
aml.InstallDefaultRules(monitoringSvc)
// Optional: connect Jube for ML-based transaction scoring
jubeClient := jube.NewClient(jube.Config{
BaseURL: os.Getenv("JUBE_BASE_URL"),
ModelID: os.Getenv("JUBE_MODEL_ID"),
FailOpen: false, // reject transactions when Jube is unreachable
Timeout: 5 * time.Second,
})
```
`InstallDefaultRules` adds three mandatory FinCEN rules:
- **CTR threshold**: flag transactions over $10,000 (31 CFR 1010.311)
- **Structuring**: detect $9,000-$9,999 patterns (31 USC 5324)
- **Velocity**: block if 24h volume exceeds $50,000 without enhanced KYC
### 7. Mount and serve
```go
import "github.com/hanzoai/base"
srv := base.NewServer(base.Config{
Addr: ":8080",
})
srv.Mount("/broker", brokerSrv.Handler())
srv.Mount("/exchange", cexGateway.Handler())
srv.Mount("/compliance", complianceSrv.Handler())
srv.ListenAndServe()
```
## Minimal ATS main.go
```go
package main
import (
"context"
"log/slog"
"os"
"time"
"github.com/hanzoai/base"
"github.com/luxfi/broker/pkg/api"
"github.com/luxfi/broker/pkg/provider"
"github.com/luxfi/broker/pkg/provider/envconfig"
"github.com/luxfi/cex/pkg/engine"
"github.com/luxfi/cex/pkg/gateway"
"github.com/luxfi/cex/pkg/markets"
"github.com/luxfi/compliance/pkg/aml"
"github.com/luxfi/compliance/pkg/jube"
compapi "github.com/luxfi/compliance/pkg/api"
)
func main() {
ctx := context.Background()
// 1. Providers
registry := provider.NewRegistry()
n := envconfig.RegisterFromEnv(registry)
slog.Info("providers registered", "count", n)
// 2. CEX matching engine
eng := engine.New(engine.Config{EnableSurveillance: true, EnableAudit: true})
markets.RegisterFromProviders(ctx, registry, eng)
// 3. Compliance
monSvc := aml.NewMonitoringService()
aml.InstallDefaultRules(monSvc)
if url := os.Getenv("JUBE_BASE_URL"); url != "" {
jc := jube.NewClient(jube.Config{
BaseURL: url, ModelID: os.Getenv("JUBE_MODEL_ID"),
FailOpen: false, Timeout: 5 * time.Second,
})
monSvc.SetExternalScorer(jc)
}
// 4. HTTP servers
brokerSrv := api.NewServer(api.Config{Registry: registry})
cexGw := gateway.New(gateway.Config{Engine: eng})
compSrv := compapi.NewServer(compapi.Config{Monitoring: monSvc})
// 5. Mount on base and serve
srv := base.NewServer(base.Config{Addr: ":8080"})
srv.Mount("/broker", brokerSrv.Handler())
srv.Mount("/exchange", cexGw.Handler())
srv.Mount("/compliance", compSrv.Handler())
slog.Info("ATS starting", "addr", ":8080", "providers", n)
if err := srv.ListenAndServe(); err != nil {
slog.Error("server error", "error", err)
os.Exit(1)
}
}
```
## Configuration
| Variable | Required | Description |
|----------|----------|-------------|
| At least one provider key pair | Yes | See [Provider Integration](/docs/regulated/providers) |
| `JUBE_BASE_URL` | No | Jube transaction monitoring endpoint |
| `JUBE_MODEL_ID` | No | Jube EntityAnalysisModel GUID |
| `COMPLIANCE_API_KEY` | Yes | API key for compliance endpoints |
| `JUMIO_API_TOKEN` | Recommended | Jumio IDV for subscriber KYC |
| `BROKER_API_KEY` | Yes | API key for broker endpoints |
## SEC Compliance Checklist
An ATS operating under Regulation ATS must:
- [ ] File Form ATS-N with the SEC before commencing operations
- [ ] Register as a broker-dealer (Form BD) with FINRA
- [ ] Maintain minimum $250,000 net capital (Rule 15c3-1)
- [ ] Implement fair access policies (Rule 301(b)(5)) if volume exceeds 5%
- [ ] Display best-priced orders (Rule 301(b)(3)) if volume exceeds 5%
- [ ] Maintain records of all orders, executions, and cancellations
- [ ] File quarterly reports on Form ATS-R
- [ ] Implement and test business continuity / disaster recovery plans
- [ ] Screen all subscribers via KYC/AML before granting access
- [ ] Monitor all transactions for CTR, structuring, and suspicious activity
- [ ] File SARs within 30 days of detection
## Docker Deployment
```bash
docker build --platform linux/amd64 -t ghcr.io/yourorg/atsd:latest .
docker run -p 8080:8080 \
-e ALPACA_API_KEY=pk_... \
-e ALPACA_API_SECRET=sk_... \
-e COMPLIANCE_API_KEY=... \
-e JUMIO_API_TOKEN=... \
-e JUMIO_API_SECRET=... \
ghcr.io/yourorg/atsd:latest
```
## Reference
See [luxfi/ats](https://github.com/luxfi/ats) for a working ATS implementation built on these components.
+214
View File
@@ -0,0 +1,214 @@
---
title: Broker-Dealer (BD)
description: Build a FINRA-registered broker-dealer with multi-venue order routing
icon: ArrowLeftRight
---
A Broker-Dealer (BD) is a firm registered with the SEC and FINRA that executes securities transactions on behalf of customers. Unlike an ATS, a BD does not operate an internal matching engine -- it routes customer orders to external venues (exchanges, ATSs, market makers) and earns commissions or markups. A BD must maintain at least $250,000 in net capital under Rule 15c3-1 and join SIPC for customer asset protection.
The Lux stack provides broker connectivity, smart order routing, risk management, and compliance -- everything a BD needs except the internal matching engine.
## Architecture
A BD uses Broker and Compliance but not CEX:
```
┌────────────────────┐
│ bdd │
│ :8080 │
└─────────┬──────────┘
│
┌────────────────────┼────────────────┐
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ Broker │ │ Risk │ │ Compliance │
│ 16 Venues │ │ Engine │ │ KYC / AML │
└──────┬──────┘ └────────────┘ └─────────────┘
│
┌──────┴──────┐
│ Smart Order │
│ Router │
└─────────────┘
```
The key difference from an ATS: orders always route to external venues. There is no internal order book. The Smart Order Router selects the best venue based on net price (spread + fees) and can split large orders across multiple venues using VWAP.
## Step-by-Step Build
### 1. Create Go project
```bash
mkdir bdd && cd bdd
go mod init github.com/yourorg/bdd
go get github.com/hanzoai/base
go get github.com/luxfi/broker
go get github.com/luxfi/compliance
```
### 2. Register providers
```go
import (
"github.com/luxfi/broker/pkg/provider"
"github.com/luxfi/broker/pkg/provider/envconfig"
)
registry := provider.NewRegistry()
n := envconfig.RegisterFromEnv(registry)
```
A BD typically connects to multiple venues for best execution. At minimum, register one equities provider (Alpaca, IBKR, or Tradier) and one crypto provider if offering digital assets.
### 3. Create broker with smart order routing
```go
import "github.com/luxfi/broker/pkg/api"
brokerSrv := api.NewServer(api.Config{
Registry: registry,
EnableSOR: true, // smart order routing
EnableRisk: true, // pre-trade risk checks
EnableSettlement: true, // prefunding pool for instant buys
EnableAudit: true, // immutable audit trail
})
```
### 4. Install compliance
```go
import (
"github.com/luxfi/compliance/pkg/aml"
"github.com/luxfi/compliance/pkg/kyc"
"github.com/luxfi/compliance/pkg/idv"
)
// KYC service with IDV provider
kycSvc := kyc.NewService()
kycSvc.RegisterProvider(idv.NewJumio(idv.JumioConfig{
APIToken: os.Getenv("JUMIO_API_TOKEN"),
APISecret: os.Getenv("JUMIO_API_SECRET"),
}))
// AML monitoring with FinCEN defaults
monSvc := aml.NewMonitoringService()
aml.InstallDefaultRules(monSvc)
```
### 5. Mount and serve
```go
import "github.com/hanzoai/base"
srv := base.NewServer(base.Config{Addr: ":8080"})
srv.Mount("/broker", brokerSrv.Handler())
srv.Mount("/compliance", compSrv.Handler())
slog.Info("BD starting", "addr", ":8080", "providers", n)
srv.ListenAndServe()
```
## Minimal BD main.go
```go
package main
import (
"log/slog"
"os"
"github.com/hanzoai/base"
"github.com/luxfi/broker/pkg/api"
"github.com/luxfi/broker/pkg/provider"
"github.com/luxfi/broker/pkg/provider/envconfig"
"github.com/luxfi/compliance/pkg/aml"
compapi "github.com/luxfi/compliance/pkg/api"
)
func main() {
// 1. Providers
registry := provider.NewRegistry()
n := envconfig.RegisterFromEnv(registry)
slog.Info("providers registered", "count", n)
// 2. Broker (SOR, risk, audit -- no matching engine)
brokerSrv := api.NewServer(api.Config{
Registry: registry,
EnableSOR: true,
EnableRisk: true,
EnableAudit: true,
})
// 3. Compliance
monSvc := aml.NewMonitoringService()
aml.InstallDefaultRules(monSvc)
compSrv := compapi.NewServer(compapi.Config{Monitoring: monSvc})
// 4. Serve
srv := base.NewServer(base.Config{Addr: ":8080"})
srv.Mount("/broker", brokerSrv.Handler())
srv.Mount("/compliance", compSrv.Handler())
slog.Info("BD starting", "addr", ":8080", "providers", n)
if err := srv.ListenAndServe(); err != nil {
slog.Error("server error", "error", err)
os.Exit(1)
}
}
```
## Key Difference from ATS
| Aspect | ATS | BD |
|--------|-----|-----|
| Internal matching | Yes (CLOB engine) | No |
| Order routing | Internal first, then external | External only |
| `luxfi/cex` dependency | Required | Not used |
| Registration | Form ATS-N + Form BD | Form BD only |
| Fair access rules | Yes (if >5% volume) | No |
| Best execution | Rule 5310 | Rule 5310 |
A BD has simpler technology requirements but the same compliance obligations for KYC, AML, and transaction monitoring.
## FINRA Compliance
A BD operating under SEC/FINRA rules must:
- [ ] Register with SEC on Form BD and join FINRA
- [ ] Join SIPC (Securities Investor Protection Corporation)
- [ ] Maintain $250,000 minimum net capital (Rule 15c3-1)
- [ ] Satisfy best execution obligations (Rule 5310) -- document venue selection rationale
- [ ] Perform suitability analysis for customer recommendations (Rule 2111 / Reg BI)
- [ ] Maintain books and records (Rule 17a-3, 17a-4)
- [ ] File FOCUS reports quarterly
- [ ] Screen all customers via KYC/AML before account opening
- [ ] Monitor transactions for CTR, structuring, and suspicious activity
- [ ] File SARs within 30 days of detection
- [ ] Implement supervisory procedures (Rule 3110)
- [ ] Designate a Chief Compliance Officer
## Configuration
| Variable | Required | Description |
|----------|----------|-------------|
| At least one provider key pair | Yes | See [Provider Integration](/docs/regulated/providers) |
| `BROKER_API_KEY` | Yes | API key for broker endpoints |
| `COMPLIANCE_API_KEY` | Yes | API key for compliance endpoints |
| `JUMIO_API_TOKEN` | Recommended | Jumio IDV for customer KYC |
| `JUMIO_API_SECRET` | Recommended | Jumio API secret |
## Docker Deployment
```bash
docker build --platform linux/amd64 -t ghcr.io/yourorg/bdd:latest .
docker run -p 8080:8080 \
-e ALPACA_API_KEY=pk_... \
-e ALPACA_API_SECRET=sk_... \
-e IBKR_ACCESS_TOKEN=... \
-e BROKER_API_KEY=... \
-e COMPLIANCE_API_KEY=... \
ghcr.io/yourorg/bdd:latest
```
## Reference
See [luxfi/bd](https://github.com/luxfi/bd) for a working broker-dealer implementation built on these components.
@@ -0,0 +1,240 @@
---
title: Compliance Integration
description: KYC, AML, sanctions screening, and regulatory frameworks
icon: ShieldCheck
---
The `luxfi/compliance` module provides a complete regulated financial compliance stack: identity verification, KYC orchestration, AML/sanctions screening, transaction monitoring, payment compliance, and multi-jurisdiction regulatory frameworks. Zero external dependencies -- standard library only.
## Overview
```
┌──────────────────┐
│ complianced │
│ :8091 │
└────────┬─────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ IDV │ │ AML │ │ Payments │
│ Providers │ │ Screening │ │Compliance │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
┌────┼────┐ ┌───┼───┐ ┌───┼───┐
│ │ │ │ │ │ │ │ │
Jumio Onfido Plaid OFAC EU PEP Travel CTR Stablecoin
SDN HMT Rule Validation
```
## Identity Verification Providers
Three IDV providers are supported out of the box. Each implements the same provider interface: initiate verification, check status, and parse webhooks.
| Provider | API | Features | Env Vars |
|----------|-----|----------|----------|
| Jumio | Netverify v4 | ID + selfie, liveness, document verification | `JUMIO_API_TOKEN`, `JUMIO_API_SECRET`, `JUMIO_WEBHOOK_SECRET` |
| Onfido | v3.6 | Applicant, check, SDK token, watchlist screening | `ONFIDO_API_TOKEN`, `ONFIDO_WEBHOOK_SECRET` |
| Plaid | Identity Verification | Session-based IDV, bank-linked identity | `PLAID_CLIENT_ID`, `PLAID_SECRET`, `PLAID_WEBHOOK_SECRET` |
All providers validate webhook signatures via HMAC-SHA256.
```go
import (
"github.com/luxfi/compliance/pkg/kyc"
"github.com/luxfi/compliance/pkg/idv"
)
svc := kyc.NewService()
svc.RegisterProvider(idv.NewJumio(idv.JumioConfig{
APIToken: os.Getenv("JUMIO_API_TOKEN"),
APISecret: os.Getenv("JUMIO_API_SECRET"),
WebhookSecret: os.Getenv("JUMIO_WEBHOOK_SECRET"),
}))
```
**Application status flow:**
```
draft -> pending -> pending_kyc -> approved
-> rejected
```
**KYC status flow:**
```
not_started -> pending -> verified
-> failed
```
## AML Screening
### Sanctions & PEP Screening
Screen individuals against global sanctions lists and politically exposed persons databases:
- **OFAC SDN** -- US Treasury Office of Foreign Assets Control
- **EU consolidated sanctions** -- European Union sanctions list
- **UK HM Treasury** -- Her Majesty's Treasury sanctions list
- **PEP databases** -- Politically Exposed Persons
- **Adverse media** -- Negative news screening
Match types: exact, fuzzy (Levenshtein distance), partial. Risk scoring: low, medium, high, critical.
```go
import "github.com/luxfi/compliance/pkg/aml"
screeningSvc := aml.NewScreeningService()
result, err := screeningSvc.Screen(ctx, aml.ScreenRequest{
FullName: "John Doe",
DateOfBirth: "1985-03-15",
Country: "US",
})
// result.Matches contains any sanctions/PEP hits with scores
```
### Transaction Monitoring
The rules engine detects suspicious transaction patterns. `InstallDefaultRules` adds the three mandatory FinCEN baselines:
```go
monSvc := aml.NewMonitoringService()
aml.InstallDefaultRules(monSvc)
```
This installs:
| Rule | Type | Threshold | Regulation |
|------|------|-----------|------------|
| CTR threshold | Single amount | $10,000 | 31 CFR 1010.311 |
| Structuring | Pattern detection | $9,000-$9,999 | 31 USC 5324 |
| Velocity (24h) | Aggregate volume | $50,000 | Enhanced KYC trigger |
Add custom rules:
```go
monSvc.AddRule(aml.Rule{
ID: "high-value-crypto",
Type: aml.RuleSingleAmount,
ThresholdAmount: 25000,
Currency: "USD",
Enabled: true,
Severity: aml.SeverityHigh,
Description: "Flag crypto transactions over $25,000",
})
```
Alert lifecycle: `open -> investigating -> escalated -> closed/filed`.
### Jube Integration
For ML-based transaction scoring beyond static rules, connect the Jube client:
```go
import "github.com/luxfi/compliance/pkg/jube"
client := jube.NewClient(jube.Config{
BaseURL: "http://jube.svc.cluster.local:5001",
ModelID: "your-model-guid",
FailOpen: false, // reject when Jube is unreachable
Timeout: 5 * time.Second,
})
```
Jube scores transactions in real time against configurable ML models, returning activation alerts, sanctions matches, and response elevations. The `FailOpen` flag controls behavior when Jube is unavailable -- set to `false` for regulated products.
## Regulatory Frameworks
Multi-jurisdiction compliance rules are built in:
| Jurisdiction | Regulator | Key Requirements |
|-------------|-----------|------------------|
| USA | FinCEN, SEC, FINRA | BSA (CIP, CTR $10k+, SAR), suitability, accredited investor |
| UK | FCA | Registration, 5AMLD CDD/EDD, HM Treasury sanctions |
| Isle of Man | IOMFSA | Designated Business, AML/CFT Code 2019, source of wealth/funds |
Each jurisdiction defines: required identity fields, application validation rules, and transaction limits.
```go
import "github.com/luxfi/compliance/pkg/regulatory"
jur := regulatory.Get("USA")
err := jur.ValidateApplication(application)
limits := jur.TransactionLimits()
```
## Payment Compliance
### Travel Rule
FATF Recommendation 16 requires originator and beneficiary information for transfers over $3,000:
```go
import "github.com/luxfi/compliance/pkg/payments"
result := payments.ValidateTravelRule(payments.TransferInfo{
Amount: 5000,
OriginatorName: "Alice",
BeneficiaryName: "Bob",
OriginatorAddr: "123 Main St",
})
// result.Compliant = true if all required fields present
```
### CTR Detection
Flags transactions at or above $10,000 for Currency Transaction Report filing.
### Stablecoin Validation
Token allowlists, per-jurisdiction policies, and address risk scoring with chain analysis integration point.
## Integration Methods
### Go Library (import directly)
The most common integration. Import packages and use them in your Go application:
```go
import (
"github.com/luxfi/compliance/pkg/kyc"
"github.com/luxfi/compliance/pkg/idv"
"github.com/luxfi/compliance/pkg/aml"
"github.com/luxfi/compliance/pkg/regulatory"
"github.com/luxfi/compliance/pkg/payments"
)
```
### HTTP API (standalone server)
Run `complianced` as a standalone service and call it over HTTP:
```bash
go build -o complianced ./cmd/complianced/
JUMIO_API_TOKEN=... COMPLIANCE_API_KEY=... ./complianced
```
| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/kyc/verify` | Initiate KYC verification |
| GET | `/v1/kyc/status/{id}` | Check verification status |
| POST | `/v1/aml/screen` | Screen against sanctions/PEP |
| POST | `/v1/aml/monitor` | Monitor transaction |
| GET | `/v1/aml/alerts` | List alerts |
| POST | `/v1/payments/validate` | Validate payment compliance |
| GET | `/v1/regulatory/{jurisdiction}` | Get jurisdiction requirements |
Auth: `X-Api-Key` header. Health check at `/healthz` requires no auth.
### TypeScript SDK
The `@luxbank/compliance` SDK provides a TypeScript client for NestJS applications:
```typescript
import { ComplianceModule } from '@luxbank/compliance'
ComplianceModule.forRoot({
baseUrl: process.env.COMPLIANCE_BASE_URL || 'http://compliance:8091',
apiKey: process.env.COMPLIANCE_API_KEY || '',
})
```
The TypeScript SDK lives in [luxfi/bank/pkg/compliance](https://github.com/luxfi/bank/tree/main/pkg/compliance).
@@ -0,0 +1,99 @@
---
title: Building Regulated Products
description: Assemble ATS, Broker-Dealer, and Transfer Agent systems from Lux OSS components
icon: Scale
---
The Lux regulated stack provides composable Go modules for building SEC/FINRA-registered financial products. Instead of building from scratch, you assemble proven components -- broker connectivity, matching engines, compliance pipelines, and custody integration -- into a product that meets specific regulatory requirements.
## Product Types
Three regulated product types can be built from the same set of modules:
| Product | Registration | What It Does | Key Modules |
|---------|-------------|--------------|-------------|
| **ATS** | SEC Reg ATS, Form ATS-N | Operates an internal matching engine (CLOB) with multi-venue routing | broker + cex + compliance + mpc |
| **Broker-Dealer** | SEC/FINRA, SIPC | Routes orders to external venues on behalf of customers | broker + compliance |
| **Transfer Agent** | SEC Rule 17Ad, Form TA-1 | Maintains shareholder records, processes transfers, manages cap tables | compliance + settlement webhooks |
## Architecture
All three products compose from the same building blocks:
```
┌─────────────────────────────────────┐
│ Your Application │
│ (ATS, BD, or TA main.go) │
└──────────────┬──────────────────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
┌────────┴────────┐ ┌────────┴────────┐ ┌────────┴────────┐
│ luxfi/broker │ │ luxfi/cex │ │ luxfi/compliance│
│ 16 providers │ │ matching engine │ │ KYC/AML/regs │
│ SOR, risk, │ │ order book, │ │ IDV, sanctions │
│ settlement │ │ markets │ │ monitoring │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
┌────────┴────────┐ ┌────────┴────────┐ ┌────────┴────────┐
│ luxfi/mpc │ │ hanzoai/base │ │ luxfi/kms │
│ MPC custody │ │ HTTP framework │ │ secret mgmt │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
**luxfi/broker** -- Multi-venue trading router with 16 providers (Alpaca, IBKR, Coinbase, Binance, Kraken, etc.), smart order routing, pre-trade risk, settlement engine, and audit trail.
**luxfi/cex** -- Central limit order book (CLOB) matching engine with market registration, fee schedules, and surveillance hooks. Used by ATS products that match orders internally.
**luxfi/compliance** -- KYC identity verification (Jumio, Onfido, Plaid), AML sanctions screening (OFAC, EU, UK, PEP), transaction monitoring (CTR, structuring, velocity), and multi-jurisdiction regulatory frameworks (USA, UK, IOM).
**luxfi/mpc** -- Multi-party computation custody for institutional key management. Threshold signatures without single points of failure.
**hanzoai/base** -- HTTP server framework with middleware, health checks, structured logging, and graceful shutdown. All regulated products mount their handlers on a Base server.
## How the Pieces Compose
Every regulated product follows the same initialization pattern:
1. Create a `hanzoai/base` server
2. Create a `provider.Registry` and call `envconfig.RegisterFromEnv()` to wire up trading venues
3. Optionally create a `cex/engine.Engine` and call `markets.RegisterFromProviders()` for internal matching
4. Call `aml.InstallDefaultRules()` to install baseline FinCEN monitoring rules
5. Optionally connect a Jube client for ML-based transaction scoring
6. Mount HTTP handlers and start the server
The difference between products is which steps you include and which regulatory obligations you satisfy.
## Prerequisites
- **Go 1.26+**
- **Hanzo Base** (`go get github.com/hanzoai/base`)
- At least one provider API key (see [Provider Integration](/docs/regulated/providers))
- For compliance: at least one IDV provider credential (Jumio, Onfido, or Plaid)
## Component Usage by Product
| Component | ATS | BD | TA |
|-----------|-----|----|----|
| `luxfi/broker` provider registry | Yes | Yes | -- |
| `luxfi/broker` smart order router | Yes | Yes | -- |
| `luxfi/broker` risk engine | Yes | Yes | -- |
| `luxfi/broker` settlement engine | Yes | Optional | -- |
| `luxfi/cex` matching engine | Yes | -- | -- |
| `luxfi/cex` market registration | Yes | -- | -- |
| `luxfi/compliance` KYC/IDV | Yes | Yes | Yes |
| `luxfi/compliance` AML screening | Yes | Yes | Yes |
| `luxfi/compliance` transaction monitoring | Yes | Yes | Yes |
| `luxfi/compliance` regulatory frameworks | Yes | Yes | Yes |
| `luxfi/compliance` payment compliance | Yes | Yes | Yes |
| `luxfi/mpc` custody | Yes | Optional | -- |
| Settlement webhooks | -- | -- | Yes |
| Cap table management | -- | -- | Yes |
## Next Steps
- [Alternative Trading System (ATS)](/docs/regulated/ats) -- Build an SEC-registered ATS with internal matching
- [Broker-Dealer (BD)](/docs/regulated/bd) -- Build a FINRA-registered BD with multi-venue routing
- [Transfer Agent (TA)](/docs/regulated/ta) -- Build an SEC-registered TA for securities records
- [Provider Integration](/docs/regulated/providers) -- Connect to 16+ trading venues
- [Compliance Integration](/docs/regulated/compliance-integration) -- KYC, AML, and regulatory frameworks
@@ -0,0 +1 @@
{"pages":["index","ats","bd","ta","providers","compliance-integration"]}
@@ -0,0 +1,160 @@
---
title: Provider Integration
description: Connect to 16+ trading venues via the broker provider interface
icon: Plug
---
The `luxfi/broker` provider system abstracts trading venues behind a unified interface. Each provider implements the same `Provider` interface, and `envconfig.RegisterFromEnv()` automatically registers every provider that has credentials in the environment.
## The Provider Interface
Every broker backend implements this core interface:
```go
type Provider interface {
Name() string
// Accounts
CreateAccount(ctx context.Context, req *types.CreateAccountRequest) (*types.Account, error)
GetAccount(ctx context.Context, providerAccountID string) (*types.Account, error)
ListAccounts(ctx context.Context) ([]*types.Account, error)
// Portfolio
GetPortfolio(ctx context.Context, providerAccountID string) (*types.Portfolio, error)
// Orders
CreateOrder(ctx context.Context, providerAccountID string, req *types.CreateOrderRequest) (*types.Order, error)
ListOrders(ctx context.Context, providerAccountID string) ([]*types.Order, error)
GetOrder(ctx context.Context, providerAccountID, providerOrderID string) (*types.Order, error)
CancelOrder(ctx context.Context, providerAccountID, providerOrderID string) error
// Transfers
CreateTransfer(ctx context.Context, providerAccountID string, req *types.CreateTransferRequest) (*types.Transfer, error)
ListTransfers(ctx context.Context, providerAccountID string) ([]*types.Transfer, error)
// Banking
CreateBankRelationship(ctx context.Context, providerAccountID string, ownerName, accountType, accountNumber, routingNumber string) (*types.BankRelationship, error)
ListBankRelationships(ctx context.Context, providerAccountID string) ([]*types.BankRelationship, error)
// Assets & Market Data
ListAssets(ctx context.Context, class string) ([]*types.Asset, error)
GetAsset(ctx context.Context, symbolOrID string) (*types.Asset, error)
GetSnapshot(ctx context.Context, symbol string) (*types.MarketSnapshot, error)
GetSnapshots(ctx context.Context, symbols []string) (map[string]*types.MarketSnapshot, error)
GetBars(ctx context.Context, symbol, timeframe, start, end string, limit int) ([]*types.Bar, error)
GetLatestTrades(ctx context.Context, symbols []string) (map[string]*types.Trade, error)
GetLatestQuotes(ctx context.Context, symbols []string) (map[string]*types.Quote, error)
GetClock(ctx context.Context) (*types.MarketClock, error)
GetCalendar(ctx context.Context, start, end string) ([]*types.MarketCalendarDay, error)
}
```
## Built-in Providers
| Provider | Asset Classes | Type | Env Vars |
|----------|-------------|------|----------|
| Alpaca | Equities, Crypto | Broker-dealer (SEC/FINRA) | `ALPACA_API_KEY`, `ALPACA_API_SECRET` |
| Interactive Brokers | Equities, Options, Futures, FX | Prime broker | `IBKR_ACCESS_TOKEN`, `IBKR_ACCOUNT_ID`, `IBKR_CONSUMER_KEY` |
| Tradier | Equities, Options | Broker-dealer | `TRADIER_ACCESS_TOKEN`, `TRADIER_ACCOUNT_ID` |
| Coinbase | Crypto | Exchange (Advanced Trade) | `COINBASE_API_KEY`, `COINBASE_API_SECRET` |
| Binance | Crypto | Exchange | `BINANCE_API_KEY`, `BINANCE_API_SECRET` |
| Kraken | Crypto | Exchange | `KRAKEN_API_KEY`, `KRAKEN_API_SECRET` |
| Gemini | Crypto | Exchange | `GEMINI_API_KEY`, `GEMINI_API_SECRET` |
| SFOX | Crypto | Prime dealer / SOR | `SFOX_API_KEY` |
| FalconX | Crypto | Institutional RFQ | `FALCON_API_KEY`, `FALCON_API_SECRET`, `FALCON_PASSPHRASE` |
| Fireblocks | Crypto | Custody + OTC | `FIREBLOCKS_API_KEY`, `FIREBLOCKS_PRIVATE_KEY` |
| BitGo | Crypto | Custody + Prime | `BITGO_ACCESS_TOKEN`, `BITGO_ENTERPRISE` |
| Circle | USDC / Stablecoins | Stablecoin infrastructure | `CIRCLE_API_KEY` |
| CurrencyCloud | FX (35+ pairs) | Institutional FX (Visa) | `CURRENCYCLOUD_LOGIN_ID`, `CURRENCYCLOUD_API_KEY` |
| LMAX | FX, Crypto, Metals | Institutional CLOB | `LMAX_API_KEY`, `LMAX_USERNAME`, `LMAX_PASSWORD` |
| Polygon.io | Equities, FX, Crypto | Market data only | `POLYGON_API_KEY` |
| Finix | Payments | Payment processing | `FINIX_USERNAME`, `FINIX_PASSWORD` |
## Automatic Registration
`envconfig.RegisterFromEnv()` checks environment variables for all 16 providers and registers whichever ones have credentials:
```go
import (
"github.com/luxfi/broker/pkg/provider"
"github.com/luxfi/broker/pkg/provider/envconfig"
)
registry := provider.NewRegistry()
n := envconfig.RegisterFromEnv(registry)
// n = number of providers successfully registered
```
Set `ALPACA_API_KEY` and `ALPACA_API_SECRET` in the environment, and Alpaca is registered. Set `COINBASE_API_KEY` and `COINBASE_API_SECRET`, and Coinbase is added. No code changes needed.
Each provider also accepts an optional base URL override for sandbox/testing:
| Provider | URL Override Var | Default |
|----------|-----------------|---------|
| Alpaca | `ALPACA_BASE_URL` | Sandbox |
| IBKR | `IBKR_GATEWAY_URL` | Default gateway |
| BitGo | `BITGO_BASE_URL` | Test |
| FalconX | `FALCON_BASE_URL` | Sandbox |
| Finix | `FINIX_BASE_URL` | Sandbox |
| Circle | `CIRCLE_BASE_URL` | Sandbox |
| Tradier | `TRADIER_BASE_URL` | Sandbox |
| LMAX | `LMAX_BASE_URL` | Sandbox |
| CurrencyCloud | `CURRENCYCLOUD_BASE_URL` | Demo |
Production URLs are used by default for: Coinbase, Binance, Kraken, Gemini, SFOX, Fireblocks, Polygon.
## Adding a Custom Provider
Implement the `Provider` interface and register it on the registry:
```go
package myprovider
import (
"context"
"github.com/luxfi/broker/pkg/provider"
"github.com/luxfi/broker/pkg/types"
)
type MyProvider struct {
apiKey string
}
func New(apiKey string) *MyProvider {
return &MyProvider{apiKey: apiKey}
}
func (p *MyProvider) Name() string { return "myprovider" }
func (p *MyProvider) CreateAccount(ctx context.Context, req *types.CreateAccountRequest) (*types.Account, error) {
// Your implementation
}
// ... implement remaining Provider interface methods
```
Register manually:
```go
registry := provider.NewRegistry()
envconfig.RegisterFromEnv(registry) // built-in providers
registry.Register(myprovider.New(os.Getenv("MY_API_KEY"))) // custom provider
```
## Optional Capability Interfaces
Providers can implement additional interfaces for extended functionality. The broker API detects these at runtime and exposes the additional endpoints only for providers that support them.
| Interface | Methods | Used By |
|-----------|---------|---------|
| `TradingExtended` | ReplaceOrder, CancelAllOrders, GetPosition, ClosePosition, CloseAllPositions | Alpaca, IBKR, Tradier |
| `AccountManager` | UpdateAccount, CloseAccount, GetAccountActivities | Alpaca, IBKR |
| `DocumentManager` | UploadDocument, ListDocuments, GetDocument, DownloadDocument | Alpaca |
| `JournalManager` | CreateJournal, ListJournals, BatchJournal, ReverseBatchJournal | Alpaca |
| `TransferExtended` | CancelTransfer, DeleteACHRelationship, RecipientBanks | Alpaca, IBKR |
| `CryptoDataProvider` | CryptoBars, CryptoQuotes, CryptoTrades, CryptoSnapshots | Coinbase, Binance, Kraken |
| `EventStreamer` | StreamTradeEvents, StreamAccountEvents, StreamTransferEvents | Alpaca |
| `PortfolioAnalyzer` | GetPortfolioHistory | Alpaca, IBKR |
| `WatchlistManager` | CreateWatchlist, ListWatchlists, CRUD operations | Alpaca |
You do not need to implement these. The base `Provider` interface is sufficient. Add optional interfaces only for capabilities your provider actually supports.
+245
View File
@@ -0,0 +1,245 @@
---
title: Transfer Agent (TA)
description: Build an SEC-registered transfer agent for securities ownership records
icon: FileCheck
---
A Transfer Agent (TA) is an SEC-registered entity that maintains the official record of securities ownership. It processes transfers between holders, issues and cancels certificates, handles lost/stolen certificates, and distributes dividends. A TA registers on Form TA-1 and operates under SEC Rules 17Ad-1 through 17Ad-17, with a minimum $25,000 net capital bond.
The Lux stack provides compliance infrastructure and settlement webhook integration. Unlike an ATS or BD, a TA does not route orders or operate a matching engine -- it receives settlement notifications from trading venues and updates the ownership ledger.
## Architecture
A TA uses Compliance plus settlement webhooks and a cap table store:
```
┌────────────────────┐
│ tad │
│ :8080 │
└─────────┬──────────┘
│
┌────────────────────┼────────────────┐
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ Compliance │ │ Settlement │ │ Cap Table │
│ KYC / AML │ │ Webhooks │ │ Ledger │
└─────────────┘ └─────────────┘ └─────────────┘
```
- **Compliance** screens every shareholder (KYC) and every transfer (AML) before recording ownership changes
- **Settlement Webhooks** receive trade completion notifications from ATSs and clearing houses
- **Cap Table Ledger** is the authoritative record of who owns what
## Step-by-Step Build
### 1. Create Go project
```bash
mkdir tad && cd tad
go mod init github.com/yourorg/tad
go get github.com/hanzoai/base
go get github.com/luxfi/compliance
```
A TA does not need `luxfi/broker` or `luxfi/cex` since it does not route or match orders.
### 2. Set up compliance
```go
import (
"github.com/luxfi/compliance/pkg/aml"
"github.com/luxfi/compliance/pkg/kyc"
"github.com/luxfi/compliance/pkg/idv"
)
// KYC -- verify every shareholder identity
kycSvc := kyc.NewService()
kycSvc.RegisterProvider(idv.NewJumio(idv.JumioConfig{
APIToken: os.Getenv("JUMIO_API_TOKEN"),
APISecret: os.Getenv("JUMIO_API_SECRET"),
}))
// AML -- screen transfers against sanctions
monSvc := aml.NewMonitoringService()
aml.InstallDefaultRules(monSvc)
```
### 3. Set up settlement webhook handler
The TA receives webhook notifications when trades settle on an ATS or clearing house. Each notification contains the security, buyer, seller, quantity, and settlement timestamp.
```go
import "github.com/luxfi/compliance/pkg/webhook"
webhookHandler := webhook.NewHandler(webhook.Config{
MaxRetries: 3,
DeduplicationTTL: 24 * time.Hour,
})
// Register settlement callback
webhookHandler.OnSettlement(func(ctx context.Context, evt *SettlementEvent) error {
// 1. AML screen the transfer
result, err := monSvc.Monitor(ctx, aml.Transaction{
SenderID: evt.SellerID,
ReceiverID: evt.BuyerID,
Amount: evt.NotionalValue,
Currency: "USD",
})
if err != nil || result.Blocked {
return fmt.Errorf("transfer blocked: %s", result.Reason)
}
// 2. Update cap table
return capTable.Transfer(ctx, evt.SecurityID, evt.SellerID, evt.BuyerID, evt.Quantity)
})
```
### 4. Set up cap table ledger
```go
// Cap table is the authoritative ownership record
capTable := captable.New(captable.Config{
DB: db, // PostgreSQL via hanzoai/sql
})
```
The cap table tracks: holder ID, security ID, share quantity, acquisition date, cost basis, restriction status, and certificate number.
### 5. Mount and serve
```go
import "github.com/hanzoai/base"
srv := base.NewServer(base.Config{Addr: ":8080"})
srv.Mount("/compliance", compSrv.Handler())
srv.Mount("/webhooks", webhookHandler.Handler())
srv.Mount("/captable", capTableAPI.Handler())
slog.Info("TA starting", "addr", ":8080")
srv.ListenAndServe()
```
## Minimal TA main.go
```go
package main
import (
"log/slog"
"os"
"time"
"github.com/hanzoai/base"
"github.com/luxfi/compliance/pkg/aml"
compapi "github.com/luxfi/compliance/pkg/api"
"github.com/luxfi/compliance/pkg/webhook"
)
func main() {
// 1. Compliance
monSvc := aml.NewMonitoringService()
aml.InstallDefaultRules(monSvc)
compSrv := compapi.NewServer(compapi.Config{Monitoring: monSvc})
// 2. Settlement webhooks
wh := webhook.NewHandler(webhook.Config{
MaxRetries: 3,
DeduplicationTTL: 24 * time.Hour,
})
// 3. Cap table (your application logic)
// capTable := captable.New(...)
// capTableAPI := captableapi.New(capTable)
// 4. Serve
srv := base.NewServer(base.Config{Addr: ":8080"})
srv.Mount("/compliance", compSrv.Handler())
srv.Mount("/webhooks", wh.Handler())
// srv.Mount("/captable", capTableAPI.Handler())
slog.Info("TA starting", "addr", ":8080")
if err := srv.ListenAndServe(); err != nil {
slog.Error("server error", "error", err)
os.Exit(1)
}
}
```
## Settlement Webhook Flow
When an ATS or clearing house completes a trade, it sends a webhook to the TA:
```
ATS settles trade
│
▼
POST /webhooks/settlement
│
├── HMAC-SHA256 signature validation
├── Idempotency check (deduplication)
│
▼
AML screen transfer
│
├── OFAC / sanctions check on buyer & seller
├── CTR check on notional value
├── Velocity check on both parties
│
▼
Update cap table
│
├── Debit seller's position
├── Credit buyer's position
├── Record transfer with timestamp
└── Generate confirmation notices
```
## Compliance: KYC on Shareholders
Every shareholder must be KYC-verified before appearing in the cap table. The TA verifies identity when:
- A new shareholder acquires shares for the first time
- A shareholder requests a certificate transfer
- Regulatory re-verification is triggered (e.g., change of address, dormant account reactivation)
## Record-Keeping: 17Ad-6/7 Requirements
SEC Rules 17Ad-6 and 17Ad-7 require a TA to maintain:
| Record | Retention | Description |
|--------|-----------|-------------|
| Transfer journal | 6 years | Every transfer with date, parties, quantity |
| Shareholder list | Current + 2 years | Name, address, holdings for each holder |
| Dividend records | 6 years | Payments, dates, amounts per holder |
| Certificate log | 6 years | Issuance, cancellation, replacement |
| Correspondence | 3 years | All written communications with holders |
| Complaint log | 3 years | Every complaint and resolution |
The cap table ledger with immutable audit trail satisfies the transfer journal and shareholder list requirements. Pair with the `luxfi/compliance` webhook handler's idempotency and dead letter queue for complete audit coverage.
## Configuration
| Variable | Required | Description |
|----------|----------|-------------|
| `COMPLIANCE_API_KEY` | Yes | API key for compliance endpoints |
| `JUMIO_API_TOKEN` | Recommended | Jumio IDV for shareholder KYC |
| `JUMIO_API_SECRET` | Recommended | Jumio API secret |
| `DATABASE_URL` | Yes | PostgreSQL connection string for cap table |
| `SETTLEMENT_WEBHOOK_SECRET` | Yes | HMAC secret for validating ATS webhooks |
## Docker Deployment
```bash
docker build --platform linux/amd64 -t ghcr.io/yourorg/tad:latest .
docker run -p 8080:8080 \
-e COMPLIANCE_API_KEY=... \
-e JUMIO_API_TOKEN=... \
-e JUMIO_API_SECRET=... \
-e DATABASE_URL=postgres://... \
-e SETTLEMENT_WEBHOOK_SECRET=... \
ghcr.io/yourorg/tad:latest
```
## Reference
See [partner/app](https://github.com/luxfi/app) for a working transfer agent implementation built on these components.