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

819 lines
26 KiB
Markdown

---
id: "ET-INV-001"
title: "Modelo de Datos Investment"
type: "Technical Specification"
status: "Done"
priority: "Alta"
epic: "OQI-004"
project: "trading-platform"
version: "1.0.0"
created_date: "2025-12-05"
updated_date: "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.
```sql
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.
```sql
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.
```sql
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.
```sql
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.
```sql
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).
```sql
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
```typescript
// 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
```typescript
// 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()`
```sql
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
```sql
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
```sql
-- 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
```sql
-- 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)
```sql
-- 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
```sql
-- 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
```sql
-- 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
```bash
# 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
```sql
-- 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