- FASE-0: Diagnostic audit of 500+ files, 33 findings cataloged (7P0/8P1/12P2/6P3) - FASE-1: Resolved 7 P0 critical conflicts (ports, paths, dedup OQI-010/ADR-002, orphan schemas) - FASE-2: Resolved 8 P1 issues (traces, README/CLAUDE.md, DEPENDENCY-GRAPH v2.0, DDL drift, stack versions, DoR/DoD) - FASE-3: Resolved 12 P2 issues (archived tasks indexed, RNFs created, OQI-010 US/RF/ET, AGENTS v2.0) - FASE-4: Purged 3 obsolete docs to _archive/, fixed MODELO-NEGOCIO.md broken ref - FASE-5: Cross-layer validation (DDL→OQI 66%, OQI→BE 72%, BE→FE 78%, Inventories 95%) - FASE-6: INFORME-FINAL, SA-INDEX (18 subagents), METADATA COMPLETED 27/33 findings resolved (82%), 6 P3 deferred to backlog. 18 new files created, 40+ modified, 4 archived. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
510 lines
10 KiB
Markdown
510 lines
10 KiB
Markdown
---
|
|
id: "STACK-TECNOLOGICO"
|
|
title: "Stack Tecnologico - Trading Platform"
|
|
type: "Documentation"
|
|
project: "trading-platform"
|
|
version: "1.0.0"
|
|
updated_date: "2026-02-06"
|
|
---
|
|
|
|
# Stack Tecnologico - Trading Platform
|
|
|
|
**Version:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**ADR Relacionado:** [ADR-001](../97-adr/ADR-001-stack-tecnologico.md)
|
|
|
|
---
|
|
|
|
## Resumen del Stack
|
|
|
|
| Capa | Tecnologia | Version | Justificacion |
|
|
|------|------------|---------|---------------|
|
|
| Frontend | React + TypeScript | 18.x + 5.x | Ecosistema maduro, tipado fuerte |
|
|
| Build Tool | Vite | 6.x | HMR rapido, ESM nativo |
|
|
| Styling | Tailwind CSS | 3.x | Utility-first, dark mode |
|
|
| State | Zustand | 4.x | Simple, sin boilerplate |
|
|
| Data Fetching | TanStack Query | 5.x | Cache, refetch, optimistic |
|
|
| Charts | Lightweight Charts | 4.x | TradingView quality, ligero |
|
|
| Backend API | Express.js + TypeScript | 5.0.x + 5.x | Probado, flexible |
|
|
| Validation | Zod | 3.x | Schema validation |
|
|
| Auth | JWT + Passport | 9.x + 0.7.x | Estandar industria |
|
|
| ML Engine | Python + FastAPI | 3.11+ + 0.100+ | Async, tipado, OpenAPI |
|
|
| ML Models | XGBoost | 2.x | Gradient boosting, rapido |
|
|
| Database | PostgreSQL | 16+ | ACID, JSON, extensiones |
|
|
| Cache | Redis | 7.x | In-memory, pub/sub |
|
|
| Payments | Stripe | Latest | PCI compliant |
|
|
| SMS/WhatsApp | Twilio | Latest | Reach global |
|
|
|
|
---
|
|
|
|
## Frontend
|
|
|
|
### Core
|
|
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"react": "^18.2.0",
|
|
"react-dom": "^18.2.0",
|
|
"react-router-dom": "^6.20.0",
|
|
"typescript": "^5.3.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
### State Management
|
|
|
|
```typescript
|
|
// Zustand store example
|
|
import { create } from 'zustand';
|
|
|
|
interface AuthState {
|
|
user: User | null;
|
|
isAuthenticated: boolean;
|
|
login: (user: User) => void;
|
|
logout: () => void;
|
|
}
|
|
|
|
export const useAuthStore = create<AuthState>((set) => ({
|
|
user: null,
|
|
isAuthenticated: false,
|
|
login: (user) => set({ user, isAuthenticated: true }),
|
|
logout: () => set({ user: null, isAuthenticated: false }),
|
|
}));
|
|
```
|
|
|
|
### Data Fetching
|
|
|
|
```typescript
|
|
// TanStack Query example
|
|
import { useQuery, useMutation } from '@tanstack/react-query';
|
|
|
|
function useCourses() {
|
|
return useQuery({
|
|
queryKey: ['courses'],
|
|
queryFn: () => api.get('/courses'),
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
});
|
|
}
|
|
|
|
function useEnrollCourse() {
|
|
return useMutation({
|
|
mutationFn: (courseId: string) => api.post(`/courses/${courseId}/enroll`),
|
|
onSuccess: () => queryClient.invalidateQueries(['enrollments']),
|
|
});
|
|
}
|
|
```
|
|
|
|
### Charts
|
|
|
|
```typescript
|
|
// Lightweight Charts example
|
|
import { createChart } from 'lightweight-charts';
|
|
|
|
const chart = createChart(container, {
|
|
width: 800,
|
|
height: 400,
|
|
layout: {
|
|
background: { color: '#1a1a2e' },
|
|
textColor: '#d1d5db',
|
|
},
|
|
grid: {
|
|
vertLines: { color: '#2d2d44' },
|
|
horzLines: { color: '#2d2d44' },
|
|
},
|
|
});
|
|
|
|
const candlestickSeries = chart.addCandlestickSeries({
|
|
upColor: '#22c55e',
|
|
downColor: '#ef4444',
|
|
});
|
|
```
|
|
|
|
### Styling
|
|
|
|
```css
|
|
/* Tailwind config */
|
|
module.exports = {
|
|
darkMode: 'class',
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: {
|
|
50: '#eef2ff',
|
|
500: '#6366f1',
|
|
900: '#312e81',
|
|
},
|
|
success: '#22c55e',
|
|
danger: '#ef4444',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Backend API
|
|
|
|
### Core
|
|
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"express": "^5.0.1",
|
|
"typescript": "^5.3.0",
|
|
"cors": "^2.8.5",
|
|
"helmet": "^7.1.0",
|
|
"compression": "^1.7.4"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Authentication
|
|
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"jsonwebtoken": "^9.0.2",
|
|
"bcryptjs": "^2.4.3",
|
|
"passport": "^0.7.0",
|
|
"passport-google-oauth20": "^2.0.0",
|
|
"passport-facebook": "^3.0.0",
|
|
"passport-twitter": "^1.0.4",
|
|
"passport-apple": "^2.0.2",
|
|
"passport-github2": "^0.1.12",
|
|
"speakeasy": "^2.0.0",
|
|
"qrcode": "^1.5.3"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Database
|
|
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"pg": "^8.11.3",
|
|
"redis": "^4.6.10"
|
|
}
|
|
}
|
|
```
|
|
|
|
### External Services
|
|
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"stripe": "^14.7.0",
|
|
"twilio": "^4.19.3",
|
|
"nodemailer": "^6.9.7",
|
|
"axios": "^1.6.2"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Validation
|
|
|
|
```typescript
|
|
// Zod schema example
|
|
import { z } from 'zod';
|
|
|
|
export const registerSchema = z.object({
|
|
email: z.string().email(),
|
|
password: z.string().min(8).regex(/[A-Z]/).regex(/[0-9]/),
|
|
firstName: z.string().min(1).max(100),
|
|
lastName: z.string().min(1).max(100),
|
|
acceptTerms: z.literal(true),
|
|
});
|
|
|
|
export type RegisterInput = z.infer<typeof registerSchema>;
|
|
```
|
|
|
|
---
|
|
|
|
## ML Engine
|
|
|
|
### Core
|
|
|
|
```txt
|
|
# requirements.txt
|
|
fastapi>=0.100.0
|
|
uvicorn[standard]>=0.23.0
|
|
pydantic>=2.0.0
|
|
python-dotenv>=1.0.0
|
|
```
|
|
|
|
### ML Libraries
|
|
|
|
```txt
|
|
# ML dependencies
|
|
xgboost>=2.0.0
|
|
scikit-learn>=1.3.0
|
|
pandas>=2.0.0
|
|
numpy>=1.24.0
|
|
ta-lib # Technical analysis
|
|
```
|
|
|
|
### Data Processing
|
|
|
|
```txt
|
|
# Data dependencies
|
|
pyarrow>=14.0.0
|
|
polars>=0.19.0 # Fast dataframes
|
|
```
|
|
|
|
### API Example
|
|
|
|
```python
|
|
# FastAPI endpoint
|
|
from fastapi import FastAPI, HTTPException
|
|
from pydantic import BaseModel
|
|
import numpy as np
|
|
|
|
app = FastAPI(title="Trading Platform ML Engine")
|
|
|
|
class PredictionRequest(BaseModel):
|
|
symbol: str
|
|
timeframe: str
|
|
features: list[float]
|
|
|
|
class PredictionResponse(BaseModel):
|
|
delta_high: float
|
|
delta_low: float
|
|
confidence: float
|
|
signal: str
|
|
|
|
@app.post("/predict", response_model=PredictionResponse)
|
|
async def predict(request: PredictionRequest):
|
|
# Load model and predict
|
|
prediction = model.predict(np.array([request.features]))
|
|
return PredictionResponse(
|
|
delta_high=prediction[0][0],
|
|
delta_low=prediction[0][1],
|
|
confidence=0.85,
|
|
signal="BUY" if prediction[0][0] > 0 else "SELL"
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Database
|
|
|
|
### PostgreSQL Extensions
|
|
|
|
```sql
|
|
-- Required extensions
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID generation
|
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- Encryption
|
|
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- Text search
|
|
CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- Index type
|
|
```
|
|
|
|
### Schema Organization
|
|
|
|
```
|
|
PostgreSQL
|
|
├── public # Users, profiles, auth
|
|
├── education # Courses, lessons, quizzes
|
|
├── trading # Bots, signals, positions
|
|
├── investment # Products, accounts
|
|
├── financial # Subscriptions, wallets
|
|
├── ml # Models, predictions
|
|
└── audit # Logs, events
|
|
```
|
|
|
|
### Connection Pool
|
|
|
|
```typescript
|
|
// Pool configuration
|
|
const pool = new Pool({
|
|
host: config.database.host,
|
|
port: config.database.port,
|
|
database: config.database.name,
|
|
user: config.database.user,
|
|
password: config.database.password,
|
|
max: 20, // Max connections
|
|
idleTimeoutMillis: 30000, // Close idle after 30s
|
|
connectionTimeoutMillis: 5000,
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Redis
|
|
|
|
### Use Cases
|
|
|
|
| Use Case | TTL | Pattern |
|
|
|----------|-----|---------|
|
|
| Session tokens | 7d | `session:{userId}:{sessionId}` |
|
|
| Rate limiting | 15m | `ratelimit:{ip}:{endpoint}` |
|
|
| ML predictions cache | 5m | `ml:prediction:{symbol}:{tf}` |
|
|
| Real-time data | 1s | `realtime:{symbol}` |
|
|
| User preferences | 1h | `user:{userId}:prefs` |
|
|
|
|
### Configuration
|
|
|
|
```typescript
|
|
import { createClient } from 'redis';
|
|
|
|
const redis = createClient({
|
|
socket: {
|
|
host: config.redis.host,
|
|
port: config.redis.port,
|
|
},
|
|
password: config.redis.password,
|
|
});
|
|
|
|
// Example usage
|
|
await redis.setEx(`session:${userId}`, 7 * 24 * 60 * 60, sessionData);
|
|
```
|
|
|
|
---
|
|
|
|
## External Services
|
|
|
|
### Stripe
|
|
|
|
```typescript
|
|
import Stripe from 'stripe';
|
|
|
|
const stripe = new Stripe(config.stripe.secretKey);
|
|
|
|
// Create subscription
|
|
const subscription = await stripe.subscriptions.create({
|
|
customer: customerId,
|
|
items: [{ price: priceId }],
|
|
payment_behavior: 'default_incomplete',
|
|
expand: ['latest_invoice.payment_intent'],
|
|
});
|
|
```
|
|
|
|
### Twilio
|
|
|
|
```typescript
|
|
import twilio from 'twilio';
|
|
|
|
const client = twilio(accountSid, authToken);
|
|
|
|
// Send WhatsApp OTP
|
|
await client.messages.create({
|
|
body: `Tu codigo Trading Platform: ${otp}`,
|
|
from: 'whatsapp:+14155238886',
|
|
to: `whatsapp:${phoneNumber}`,
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## DevOps
|
|
|
|
### Docker
|
|
|
|
```dockerfile
|
|
# Backend Dockerfile
|
|
FROM node:20-alpine
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm ci --only=production
|
|
COPY dist ./dist
|
|
EXPOSE 3000
|
|
CMD ["node", "dist/index.js"]
|
|
```
|
|
|
|
### Docker Compose
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
services:
|
|
backend:
|
|
build: ./apps/backend
|
|
ports:
|
|
- "3000:3000"
|
|
depends_on:
|
|
- postgres
|
|
- redis
|
|
|
|
ml-engine:
|
|
build: ./apps/ml-engine
|
|
ports:
|
|
- "8001:8001"
|
|
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
volumes:
|
|
- redis_data:/data
|
|
|
|
volumes:
|
|
postgres_data:
|
|
redis_data:
|
|
```
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
| Layer | Framework | Coverage Target |
|
|
|-------|-----------|-----------------|
|
|
| Frontend | Vitest + Testing Library | 70% |
|
|
| Backend | Jest + Supertest | 80% |
|
|
| ML Engine | Pytest | 75% |
|
|
| E2E | Playwright | Critical paths |
|
|
|
|
### Example Test
|
|
|
|
```typescript
|
|
// Jest test example
|
|
describe('AuthService', () => {
|
|
describe('login', () => {
|
|
it('should return tokens for valid credentials', async () => {
|
|
const result = await authService.login({
|
|
email: 'test@example.com',
|
|
password: 'ValidPass123!',
|
|
});
|
|
|
|
expect(result.tokens.accessToken).toBeDefined();
|
|
expect(result.tokens.refreshToken).toBeDefined();
|
|
});
|
|
|
|
it('should throw for invalid password', async () => {
|
|
await expect(
|
|
authService.login({
|
|
email: 'test@example.com',
|
|
password: 'wrong',
|
|
})
|
|
).rejects.toThrow('Invalid email or password');
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Version Matrix
|
|
|
|
| Dependency | Min Version | Recommended | Max Version |
|
|
|------------|-------------|-------------|-------------|
|
|
| Node.js | 20.0.0 | 20.x LTS | 22.x |
|
|
| Python | 3.11 | 3.11 | 3.12 |
|
|
| PostgreSQL | 15 | 16 | 16 |
|
|
| Redis | 6.x | 7.x | 7.x |
|
|
| React | 18.0 | 18.2 | 18.x |
|
|
| TypeScript | 5.0 | 5.3 | 5.x |
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [ADR-001: Stack Tecnologico](../97-adr/ADR-001-stack-tecnologico.md)
|
|
- [React Documentation](https://react.dev)
|
|
- [Express.js](https://expressjs.com)
|
|
- [FastAPI](https://fastapi.tiangolo.com)
|
|
- [PostgreSQL](https://www.postgresql.org/docs/)
|
|
- [Redis](https://redis.io/docs/)
|