# Stack Tecnologico - OrbiQuant IA

**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 | 5.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 | 4.18.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 | 15+ | 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": "^4.18.2",
    "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="OrbiQuant 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 OrbiQuant: ${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:15-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 | 18.0.0 | 20.x LTS | 22.x |
| Python | 3.10 | 3.11 | 3.12 |
| PostgreSQL | 14 | 15 | 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/)
