trading-platform/docs/02-definicion-modulos/OQI-004-investment-accounts/especificaciones/ET-INV-001-database.md
rckrdmrd c1b5081208 feat(ml): Complete FASE 11 - BTCUSD update and comprehensive documentation alignment
ML Engine Updates:
- Updated BTCUSD with Polygon API data (2024-2025): 215,699 new records
- Re-trained all ML models: Attention (R²: 0.223), Base, Metamodel (87.3% confidence)
- Backtest results: +176.71R profit with aggressive_filter strategy

Documentation Consolidation:
- Created docs/99-analisis/_MAP.md index with 13 new analysis documents
- Consolidated inventories: removed duplicates from orchestration/inventarios/
- Updated ML_INVENTORY.yml with BTCUSD metrics and training results
- Added execution reports: FASE11-BTCUSD, correction issues, alignment validation

Architecture & Integration:
- Updated all module documentation with NEXUS v3.4 frontmatter
- Fixed _MAP.md indexes across all folders
- Updated orchestration plans and traces

Files: 229 changed, 5064 insertions(+), 1872 deletions(-)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 09:31:29 -06:00

26 KiB

id title type status priority epic project version created_date updated_date
ET-INV-001 Modelo de Datos Investment Technical Specification Done Alta OQI-004 trading-platform 1.0.0 2025-12-05 2026-01-04

ET-INV-001: Modelo de Datos Investment

Epic: OQI-004 Cuentas de Inversión Versión: 1.0 Fecha: 2025-12-05 Responsable: Requirements-Analyst


1. Descripción

Define el modelo de datos completo para el schema investment en PostgreSQL 15+, incluyendo:

  • Productos de inversión (agentes ML)
  • Cuentas de inversión de usuarios
  • Transacciones (depósitos, retiros, distribuciones)
  • Solicitudes de retiro
  • Performance diaria de cuentas
  • Distribuciones mensuales de utilidades

2. Arquitectura de Base de Datos

┌─────────────────────────────────────────────────────────────────┐
│                     SCHEMA: investment                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌──────────────┐         ┌─────────────────┐                   │
│  │   products   │◄────────│    accounts     │                   │
│  └──────────────┘         └─────────────────┘                   │
│         │                          │                             │
│         │                          ▼                             │
│         │                  ┌─────────────────┐                   │
│         │                  │  transactions   │                   │
│         │                  └─────────────────┘                   │
│         │                          │                             │
│         │                          ▼                             │
│         │                  ┌─────────────────┐                   │
│         └─────────────────►│ daily_performance│                  │
│                            └─────────────────┘                   │
│                                     │                             │
│                                     ▼                             │
│                            ┌─────────────────┐                   │
│                            │ distributions   │                   │
│                            └─────────────────┘                   │
│                                                                   │
│                            ┌──────────────────┐                  │
│                            │withdrawal_requests│                 │
│                            └──────────────────┘                  │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

3. Especificación de Tablas

3.1 Tabla: products

Productos de inversión basados en agentes ML.

CREATE TABLE investment.products (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Información básica
    name VARCHAR(100) NOT NULL,
    description TEXT,
    agent_type VARCHAR(50) NOT NULL, -- 'swing', 'day', 'scalping', 'arbitrage'

    -- Configuración financiera
    min_investment DECIMAL(15, 2) NOT NULL DEFAULT 100.00,
    max_investment DECIMAL(15, 2), -- NULL = sin límite
    performance_fee_percentage DECIMAL(5, 2) NOT NULL DEFAULT 20.00, -- 20%
    target_annual_return DECIMAL(5, 2), -- Estimado, opcional
    risk_level VARCHAR(20) NOT NULL, -- 'low', 'medium', 'high', 'very_high'

    -- ML Engine
    ml_agent_id VARCHAR(100) NOT NULL UNIQUE, -- ID del agente en ML Engine
    ml_config JSONB, -- Configuración específica del agente

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'active', -- 'active', 'paused', 'closed'
    is_accepting_new_investors BOOLEAN NOT NULL DEFAULT true,
    total_aum DECIMAL(15, 2) NOT NULL DEFAULT 0.00, -- Assets Under Management
    total_investors INTEGER NOT NULL DEFAULT 0,

    -- Metadata
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT chk_min_investment_positive CHECK (min_investment > 0),
    CONSTRAINT chk_max_investment_valid CHECK (max_investment IS NULL OR max_investment >= min_investment),
    CONSTRAINT chk_performance_fee_range CHECK (performance_fee_percentage >= 0 AND performance_fee_percentage <= 100),
    CONSTRAINT chk_risk_level CHECK (risk_level IN ('low', 'medium', 'high', 'very_high')),
    CONSTRAINT chk_status CHECK (status IN ('active', 'paused', 'closed'))
);

-- Índices
CREATE INDEX idx_products_agent_type ON investment.products(agent_type);
CREATE INDEX idx_products_status ON investment.products(status);
CREATE INDEX idx_products_ml_agent_id ON investment.products(ml_agent_id);

-- Trigger para updated_at
CREATE TRIGGER update_products_updated_at BEFORE UPDATE ON investment.products
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

3.2 Tabla: accounts

Cuentas de inversión de usuarios en productos específicos.

CREATE TABLE investment.accounts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    product_id UUID NOT NULL REFERENCES investment.products(id) ON DELETE RESTRICT,

    -- Balance y performance
    current_balance DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
    initial_investment DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
    total_deposited DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
    total_withdrawn DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
    total_profit_distributed DECIMAL(15, 2) NOT NULL DEFAULT 0.00,

    -- Performance
    total_return_percentage DECIMAL(10, 4), -- Retorno total %
    annualized_return_percentage DECIMAL(10, 4), -- Retorno anualizado %

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'active', -- 'active', 'paused', 'closed'
    opened_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    closed_at TIMESTAMP WITH TIME ZONE,

    -- Metadata
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT uq_user_product UNIQUE (user_id, product_id),
    CONSTRAINT chk_current_balance_positive CHECK (current_balance >= 0),
    CONSTRAINT chk_account_status CHECK (status IN ('active', 'paused', 'closed'))
);

-- Índices
CREATE INDEX idx_accounts_user_id ON investment.accounts(user_id);
CREATE INDEX idx_accounts_product_id ON investment.accounts(product_id);
CREATE INDEX idx_accounts_status ON investment.accounts(status);
CREATE INDEX idx_accounts_opened_at ON investment.accounts(opened_at DESC);

-- Trigger para updated_at
CREATE TRIGGER update_accounts_updated_at BEFORE UPDATE ON investment.accounts
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

3.3 Tabla: transactions

Registro de todas las transacciones de cuentas de inversión.

CREATE TABLE investment.transactions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    account_id UUID NOT NULL REFERENCES investment.accounts(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,

    -- Tipo y estado
    type VARCHAR(30) NOT NULL, -- 'deposit', 'withdrawal', 'profit_distribution', 'fee'
    status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 'pending', 'completed', 'failed', 'cancelled'

    -- Montos
    amount DECIMAL(15, 2) NOT NULL,
    balance_before DECIMAL(15, 2) NOT NULL,
    balance_after DECIMAL(15, 2),

    -- Integración con payments
    payment_id UUID, -- Referencia a financial.payments para depósitos
    stripe_payment_intent_id VARCHAR(255),

    -- Retiros
    withdrawal_request_id UUID, -- Referencia a withdrawal_requests
    withdrawal_method VARCHAR(50), -- 'bank_transfer', 'stripe_payout'
    withdrawal_destination_id VARCHAR(255), -- bank_account_id o stripe_payout_id

    -- Distribuciones
    distribution_id UUID, -- Referencia a distributions
    distribution_period VARCHAR(20), -- '2025-01', '2025-02'

    -- Metadata
    notes TEXT,
    processed_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT chk_amount_positive CHECK (amount > 0),
    CONSTRAINT chk_transaction_type CHECK (type IN ('deposit', 'withdrawal', 'profit_distribution', 'fee')),
    CONSTRAINT chk_transaction_status CHECK (status IN ('pending', 'completed', 'failed', 'cancelled'))
);

-- Índices
CREATE INDEX idx_transactions_account_id ON investment.transactions(account_id);
CREATE INDEX idx_transactions_user_id ON investment.transactions(user_id);
CREATE INDEX idx_transactions_type ON investment.transactions(type);
CREATE INDEX idx_transactions_status ON investment.transactions(status);
CREATE INDEX idx_transactions_created_at ON investment.transactions(created_at DESC);
CREATE INDEX idx_transactions_payment_id ON investment.transactions(payment_id);

3.4 Tabla: withdrawal_requests

Solicitudes de retiro pendientes de aprobación.

CREATE TABLE investment.withdrawal_requests (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    account_id UUID NOT NULL REFERENCES investment.accounts(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,

    -- Monto y método
    amount DECIMAL(15, 2) NOT NULL,
    withdrawal_method VARCHAR(50) NOT NULL, -- 'bank_transfer', 'stripe_payout'
    destination_details JSONB NOT NULL, -- { "bank_account": "...", "routing": "..." }

    -- Estado y procesamiento
    status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 'pending', 'approved', 'rejected', 'completed', 'cancelled'
    requested_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    reviewed_at TIMESTAMP WITH TIME ZONE,
    reviewed_by UUID REFERENCES auth.users(id),
    processed_at TIMESTAMP WITH TIME ZONE,

    -- Razones
    rejection_reason TEXT,
    admin_notes TEXT,

    -- Referencia a transacción
    transaction_id UUID REFERENCES investment.transactions(id),

    -- Metadata
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT chk_withdrawal_amount_positive CHECK (amount > 0),
    CONSTRAINT chk_withdrawal_status CHECK (status IN ('pending', 'approved', 'rejected', 'completed', 'cancelled')),
    CONSTRAINT chk_withdrawal_method CHECK (withdrawal_method IN ('bank_transfer', 'stripe_payout'))
);

-- Índices
CREATE INDEX idx_withdrawal_requests_account_id ON investment.withdrawal_requests(account_id);
CREATE INDEX idx_withdrawal_requests_user_id ON investment.withdrawal_requests(user_id);
CREATE INDEX idx_withdrawal_requests_status ON investment.withdrawal_requests(status);
CREATE INDEX idx_withdrawal_requests_requested_at ON investment.withdrawal_requests(requested_at DESC);

3.5 Tabla: daily_performance

Registro diario del rendimiento de cada cuenta.

CREATE TABLE investment.daily_performance (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    account_id UUID NOT NULL REFERENCES investment.accounts(id) ON DELETE CASCADE,
    product_id UUID NOT NULL REFERENCES investment.products(id) ON DELETE CASCADE,

    -- Fecha
    date DATE NOT NULL,

    -- Valores del día
    opening_balance DECIMAL(15, 2) NOT NULL,
    closing_balance DECIMAL(15, 2) NOT NULL,
    daily_return DECIMAL(15, 2) NOT NULL, -- Ganancia/pérdida del día
    daily_return_percentage DECIMAL(10, 4),

    -- Métricas acumuladas
    cumulative_return DECIMAL(15, 2),
    cumulative_return_percentage DECIMAL(10, 4),

    -- Datos del agente ML
    trades_executed INTEGER DEFAULT 0,
    winning_trades INTEGER DEFAULT 0,
    losing_trades INTEGER DEFAULT 0,
    ml_agent_data JSONB, -- Datos adicionales del agente

    -- Metadata
    calculated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT uq_account_date UNIQUE (account_id, date)
);

-- Índices
CREATE INDEX idx_daily_performance_account_id ON investment.daily_performance(account_id);
CREATE INDEX idx_daily_performance_product_id ON investment.daily_performance(product_id);
CREATE INDEX idx_daily_performance_date ON investment.daily_performance(date DESC);
CREATE INDEX idx_daily_performance_account_date ON investment.daily_performance(account_id, date DESC);

3.6 Tabla: distributions

Distribuciones mensuales de utilidades (performance fee).

CREATE TABLE investment.distributions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    account_id UUID NOT NULL REFERENCES investment.accounts(id) ON DELETE CASCADE,
    product_id UUID NOT NULL REFERENCES investment.products(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,

    -- Período
    period VARCHAR(20) NOT NULL, -- '2025-01', '2025-02'
    period_start DATE NOT NULL,
    period_end DATE NOT NULL,

    -- Cálculos
    opening_balance DECIMAL(15, 2) NOT NULL,
    closing_balance DECIMAL(15, 2) NOT NULL,
    gross_profit DECIMAL(15, 2) NOT NULL,
    performance_fee_percentage DECIMAL(5, 2) NOT NULL,
    performance_fee_amount DECIMAL(15, 2) NOT NULL,
    net_profit DECIMAL(15, 2) NOT NULL, -- gross_profit - performance_fee_amount

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 'pending', 'distributed', 'failed'
    distributed_at TIMESTAMP WITH TIME ZONE,

    -- Referencia a transacción
    transaction_id UUID REFERENCES investment.transactions(id),

    -- Metadata
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT uq_account_period UNIQUE (account_id, period),
    CONSTRAINT chk_distribution_status CHECK (status IN ('pending', 'distributed', 'failed')),
    CONSTRAINT chk_net_profit CHECK (net_profit >= 0)
);

-- Índices
CREATE INDEX idx_distributions_account_id ON investment.distributions(account_id);
CREATE INDEX idx_distributions_product_id ON investment.distributions(product_id);
CREATE INDEX idx_distributions_user_id ON investment.distributions(user_id);
CREATE INDEX idx_distributions_period ON investment.distributions(period);
CREATE INDEX idx_distributions_status ON investment.distributions(status);

4. Interfaces TypeScript

4.1 Types del Modelo

// src/types/investment.types.ts

export type AgentType = 'swing' | 'day' | 'scalping' | 'arbitrage';
export type RiskLevel = 'low' | 'medium' | 'high' | 'very_high';
export type ProductStatus = 'active' | 'paused' | 'closed';
export type AccountStatus = 'active' | 'paused' | 'closed';
export type TransactionType = 'deposit' | 'withdrawal' | 'profit_distribution' | 'fee';
export type TransactionStatus = 'pending' | 'completed' | 'failed' | 'cancelled';
export type WithdrawalStatus = 'pending' | 'approved' | 'rejected' | 'completed' | 'cancelled';
export type WithdrawalMethod = 'bank_transfer' | 'stripe_payout';
export type DistributionStatus = 'pending' | 'distributed' | 'failed';

export interface Product {
  id: string;
  name: string;
  description: string | null;
  agent_type: AgentType;
  min_investment: number;
  max_investment: number | null;
  performance_fee_percentage: number;
  target_annual_return: number | null;
  risk_level: RiskLevel;
  ml_agent_id: string;
  ml_config: Record<string, any> | null;
  status: ProductStatus;
  is_accepting_new_investors: boolean;
  total_aum: number;
  total_investors: number;
  created_at: string;
  updated_at: string;
}

export interface Account {
  id: string;
  user_id: string;
  product_id: string;
  current_balance: number;
  initial_investment: number;
  total_deposited: number;
  total_withdrawn: number;
  total_profit_distributed: number;
  total_return_percentage: number | null;
  annualized_return_percentage: number | null;
  status: AccountStatus;
  opened_at: string;
  closed_at: string | null;
  created_at: string;
  updated_at: string;
}

export interface Transaction {
  id: string;
  account_id: string;
  user_id: string;
  type: TransactionType;
  status: TransactionStatus;
  amount: number;
  balance_before: number;
  balance_after: number | null;
  payment_id: string | null;
  stripe_payment_intent_id: string | null;
  withdrawal_request_id: string | null;
  withdrawal_method: WithdrawalMethod | null;
  withdrawal_destination_id: string | null;
  distribution_id: string | null;
  distribution_period: string | null;
  notes: string | null;
  processed_at: string | null;
  created_at: string;
}

export interface WithdrawalRequest {
  id: string;
  account_id: string;
  user_id: string;
  amount: number;
  withdrawal_method: WithdrawalMethod;
  destination_details: Record<string, any>;
  status: WithdrawalStatus;
  requested_at: string;
  reviewed_at: string | null;
  reviewed_by: string | null;
  processed_at: string | null;
  rejection_reason: string | null;
  admin_notes: string | null;
  transaction_id: string | null;
  created_at: string;
  updated_at: string;
}

export interface DailyPerformance {
  id: string;
  account_id: string;
  product_id: string;
  date: string;
  opening_balance: number;
  closing_balance: number;
  daily_return: number;
  daily_return_percentage: number | null;
  cumulative_return: number | null;
  cumulative_return_percentage: number | null;
  trades_executed: number;
  winning_trades: number;
  losing_trades: number;
  ml_agent_data: Record<string, any> | null;
  calculated_at: string;
  created_at: string;
}

export interface Distribution {
  id: string;
  account_id: string;
  product_id: string;
  user_id: string;
  period: string;
  period_start: string;
  period_end: string;
  opening_balance: number;
  closing_balance: number;
  gross_profit: number;
  performance_fee_percentage: number;
  performance_fee_amount: number;
  net_profit: number;
  status: DistributionStatus;
  distributed_at: string | null;
  transaction_id: string | null;
  created_at: string;
  updated_at: string;
}

4.2 DTOs para Creación

// src/types/investment.dtos.ts

export interface CreateProductDto {
  name: string;
  description?: string;
  agent_type: AgentType;
  min_investment: number;
  max_investment?: number;
  performance_fee_percentage: number;
  target_annual_return?: number;
  risk_level: RiskLevel;
  ml_agent_id: string;
  ml_config?: Record<string, any>;
}

export interface CreateAccountDto {
  user_id: string;
  product_id: string;
  initial_investment: number;
}

export interface CreateTransactionDto {
  account_id: string;
  user_id: string;
  type: TransactionType;
  amount: number;
  payment_id?: string;
  stripe_payment_intent_id?: string;
  notes?: string;
}

export interface CreateWithdrawalRequestDto {
  account_id: string;
  user_id: string;
  amount: number;
  withdrawal_method: WithdrawalMethod;
  destination_details: Record<string, any>;
}

5. Funciones SQL Auxiliares

5.1 Función: update_updated_at_column()

CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

5.2 Función: Actualizar AUM de producto

CREATE OR REPLACE FUNCTION update_product_aum()
RETURNS TRIGGER AS $$
BEGIN
    UPDATE investment.products
    SET total_aum = (
        SELECT COALESCE(SUM(current_balance), 0)
        FROM investment.accounts
        WHERE product_id = NEW.product_id
        AND status = 'active'
    )
    WHERE id = NEW.product_id;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_update_product_aum
AFTER INSERT OR UPDATE OF current_balance ON investment.accounts
FOR EACH ROW
EXECUTE FUNCTION update_product_aum();

6. Scripts de Migración

6.1 Up Migration

-- migrations/20250101_create_investment_schema.up.sql

-- Crear schema
CREATE SCHEMA IF NOT EXISTS investment;

-- Habilitar extensiones necesarias
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Crear función update_updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Crear tablas (ver secciones 3.1 a 3.6)
-- ... (incluir todas las sentencias CREATE TABLE)

-- Crear funciones auxiliares
-- ... (incluir funciones de la sección 5)

COMMENT ON SCHEMA investment IS 'Schema para gestión de cuentas de inversión y productos ML';

6.2 Down Migration

-- migrations/20250101_create_investment_schema.down.sql

DROP SCHEMA IF EXISTS investment CASCADE;
DROP FUNCTION IF EXISTS update_updated_at_column() CASCADE;

7. Validaciones y Constraints

7.1 Reglas de Negocio Implementadas

  1. Productos:

    • min_investment debe ser mayor a 0
    • max_investment debe ser mayor o igual a min_investment
    • performance_fee_percentage entre 0 y 100
    • Un usuario solo puede tener una cuenta por producto (uq_user_product)
  2. Cuentas:

    • current_balance no puede ser negativo
    • No se puede eliminar un producto si tiene cuentas asociadas (ON DELETE RESTRICT)
  3. Transacciones:

    • amount debe ser positivo
    • El tipo debe ser uno de los permitidos
  4. Retiros:

    • Solo se permite bank_transfer o stripe_payout
    • Monto debe ser positivo y no exceder balance disponible
  5. Distribuciones:

    • Una sola distribución por cuenta por período (uq_account_period)
    • net_profit no puede ser negativo

8. Seguridad

8.1 Row Level Security (RLS)

-- Habilitar RLS en todas las tablas
ALTER TABLE investment.products ENABLE ROW LEVEL SECURITY;
ALTER TABLE investment.accounts ENABLE ROW LEVEL SECURITY;
ALTER TABLE investment.transactions ENABLE ROW LEVEL SECURITY;
ALTER TABLE investment.withdrawal_requests ENABLE ROW LEVEL SECURITY;
ALTER TABLE investment.daily_performance ENABLE ROW LEVEL SECURITY;
ALTER TABLE investment.distributions ENABLE ROW LEVEL SECURITY;

-- Políticas para usuarios
CREATE POLICY users_view_products ON investment.products
    FOR SELECT USING (status = 'active');

CREATE POLICY users_view_own_accounts ON investment.accounts
    FOR SELECT USING (user_id = auth.uid());

CREATE POLICY users_view_own_transactions ON investment.transactions
    FOR SELECT USING (user_id = auth.uid());

CREATE POLICY users_view_own_withdrawals ON investment.withdrawal_requests
    FOR SELECT USING (user_id = auth.uid());

-- Políticas para admins (asumiendo rol 'admin')
CREATE POLICY admins_all_access ON investment.products
    FOR ALL USING (auth.jwt() ->> 'role' = 'admin');

9. Testing

9.1 Datos de Prueba

-- Seed data para testing
INSERT INTO investment.products (
    name, description, agent_type, min_investment,
    performance_fee_percentage, risk_level, ml_agent_id
) VALUES
(
    'Swing Trader Pro',
    'Agente de swing trading con estrategias de mediano plazo',
    'swing',
    500.00,
    20.00,
    'medium',
    'ml-agent-swing-001'
),
(
    'Day Trader Elite',
    'Trading intradía con alta frecuencia',
    'day',
    1000.00,
    25.00,
    'high',
    'ml-agent-day-001'
);

9.2 Tests de Integridad

-- Verificar constraints
DO $$
BEGIN
    -- Test: min_investment positivo
    BEGIN
        INSERT INTO investment.products (name, agent_type, min_investment, risk_level, ml_agent_id)
        VALUES ('Test', 'swing', -100, 'low', 'test-agent');
        RAISE EXCEPTION 'Should not allow negative min_investment';
    EXCEPTION WHEN check_violation THEN
        -- Expected
    END;

    -- Test: unique user_product
    BEGIN
        INSERT INTO investment.accounts (user_id, product_id)
        VALUES ('user-1', 'product-1');
        INSERT INTO investment.accounts (user_id, product_id)
        VALUES ('user-1', 'product-1');
        RAISE EXCEPTION 'Should not allow duplicate user-product';
    EXCEPTION WHEN unique_violation THEN
        -- Expected
    END;
END $$;

10. Dependencias

10.1 Dependencias de Schemas

  • auth.users: Para relaciones de usuarios
  • financial.payments: Para integración con pagos (opcional)

10.2 Extensiones PostgreSQL

  • uuid-ossp: Generación de UUIDs
  • pg_trgm: Para búsquedas de texto (opcional)

11. Configuración

11.1 Variables de Entorno

# Database
DATABASE_URL=postgresql://user:password@localhost:5432/trading
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=10

# Investment Schema
INVESTMENT_DEFAULT_PERFORMANCE_FEE=20.00
INVESTMENT_MIN_WITHDRAWAL_AMOUNT=50.00

12. Mantenimiento

12.1 Índices y Performance

-- Analizar uso de índices
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
WHERE schemaname = 'investment'
ORDER BY idx_scan ASC;

-- Vacuum y analyze periódicos
VACUUM ANALYZE investment.daily_performance;
VACUUM ANALYZE investment.transactions;

12.2 Respaldos

  • Backup diario de schema investment
  • Retention: 30 días
  • Point-in-time recovery habilitado

13. Referencias

  • PostgreSQL 15 Documentation
  • Schema de Stripe para payments
  • ML Engine API para integración de agentes