-- ============================================================================ -- OrbiQuant IA - Esquema INVESTMENT -- ============================================================================ -- Archivo: 04_investment_schema.sql -- Descripción: Cuentas de inversión, productos y gestión de portafolios -- Fecha: 2025-12-05 -- ============================================================================ SET search_path TO investment; -- ============================================================================ -- TIPOS ENUMERADOS -- ============================================================================ CREATE TYPE product_type_enum AS ENUM ( 'fixed_return', -- Rendimiento fijo objetivo (ej: 5% mensual) 'variable_return', -- Rendimiento variable con reparto de utilidades 'long_term_portfolio' -- Cartera de largo plazo (acciones, ETFs) ); CREATE TYPE account_status_enum AS ENUM ( 'pending_kyc', -- Esperando verificación KYC 'pending_deposit', -- Esperando depósito inicial 'active', -- Cuenta activa operando 'paused', -- Pausada por usuario o admin 'suspended', -- Suspendida por compliance 'closed' -- Cerrada ); CREATE TYPE fee_type_enum AS ENUM ( 'management', -- Comisión de administración 'performance', -- Comisión de rendimiento 'deposit', -- Comisión de depósito 'withdrawal', -- Comisión de retiro 'subscription' -- Comisión de suscripción ); -- ============================================================================ -- TABLA: products -- Descripción: Productos de inversión disponibles -- ============================================================================ CREATE TABLE IF NOT EXISTS products ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), -- Identificación name VARCHAR(100) NOT NULL, slug VARCHAR(100) NOT NULL UNIQUE, description TEXT, short_description VARCHAR(500), -- Tipo y riesgo product_type product_type_enum NOT NULL, risk_profile public.risk_profile_enum NOT NULL, -- Objetivos target_monthly_return DECIMAL(5,2), -- % objetivo mensual max_drawdown DECIMAL(5,2), -- % máximo drawdown permitido guaranteed_return BOOLEAN DEFAULT FALSE, -- SIEMPRE FALSE para cumplimiento -- Comisiones management_fee_percent DECIMAL(5,2) DEFAULT 0, -- % anual sobre AUM performance_fee_percent DECIMAL(5,2) DEFAULT 0, -- % sobre ganancias profit_share_platform DECIMAL(5,2), -- % de utilidades para plataforma (ej: 50) profit_share_client DECIMAL(5,2), -- % de utilidades para cliente (ej: 50) -- Límites min_investment DECIMAL(15,2) DEFAULT 100, max_investment DECIMAL(15,2), min_investment_period_days INT DEFAULT 30, -- Restricciones requires_kyc_level INT DEFAULT 1, allowed_risk_profiles public.risk_profile_enum[], -- Bot/Agente asociado default_bot_id UUID REFERENCES trading.bots(id), -- Estado is_active BOOLEAN DEFAULT TRUE, is_visible BOOLEAN DEFAULT TRUE, -- Metadata terms_url TEXT, risk_disclosure_url TEXT, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_products_slug ON products(slug); CREATE INDEX idx_products_type ON products(product_type); CREATE INDEX idx_products_risk ON products(risk_profile); -- ============================================================================ -- TABLA: accounts -- Descripción: Cuentas de inversión de usuarios -- ============================================================================ CREATE TABLE IF NOT EXISTS accounts ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), -- Referencias user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE RESTRICT, product_id UUID NOT NULL REFERENCES products(id), bot_id UUID REFERENCES trading.bots(id), -- Identificación account_number VARCHAR(20) NOT NULL UNIQUE, -- OQ-INV-XXXXXX name VARCHAR(100), -- Moneda currency CHAR(3) DEFAULT 'USD', -- Balances initial_deposit DECIMAL(15,2) NOT NULL, current_balance DECIMAL(15,2) NOT NULL, available_balance DECIMAL(15,2) NOT NULL, -- Balance disponible para retiro reserved_balance DECIMAL(15,2) DEFAULT 0, -- En operaciones abiertas -- Rendimiento acumulado total_profit DECIMAL(15,2) DEFAULT 0, total_fees_paid DECIMAL(15,2) DEFAULT 0, total_deposits DECIMAL(15,2) DEFAULT 0, total_withdrawals DECIMAL(15,2) DEFAULT 0, -- Métricas de rendimiento total_return_percent DECIMAL(8,4) DEFAULT 0, monthly_return_percent DECIMAL(8,4) DEFAULT 0, max_drawdown_percent DECIMAL(5,2) DEFAULT 0, sharpe_ratio DECIMAL(5,2), -- Estado status account_status_enum DEFAULT 'pending_deposit', activated_at TIMESTAMPTZ, paused_at TIMESTAMPTZ, closed_at TIMESTAMPTZ, -- Configuración del usuario auto_compound BOOLEAN DEFAULT TRUE, -- Reinvertir ganancias max_drawdown_override DECIMAL(5,2), -- Override del drawdown máximo pause_on_drawdown BOOLEAN DEFAULT TRUE, -- Pausar si alcanza DD máximo -- Aceptación de términos terms_accepted_at TIMESTAMPTZ, risk_disclosure_accepted_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_accounts_user ON accounts(user_id); CREATE INDEX idx_accounts_product ON accounts(product_id); CREATE INDEX idx_accounts_status ON accounts(status); CREATE INDEX idx_accounts_number ON accounts(account_number); -- ============================================================================ -- TABLA: account_transactions -- Descripción: Movimientos en cuentas de inversión -- ============================================================================ CREATE TYPE account_transaction_type AS ENUM ( 'deposit', 'withdrawal', 'profit', 'loss', 'fee', 'adjustment', 'transfer_in', 'transfer_out' ); CREATE TABLE IF NOT EXISTS account_transactions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT, -- Tipo y monto transaction_type account_transaction_type NOT NULL, amount DECIMAL(15,2) NOT NULL, -- Positivo o negativo según tipo currency CHAR(3) DEFAULT 'USD', -- Balances balance_before DECIMAL(15,2) NOT NULL, balance_after DECIMAL(15,2) NOT NULL, -- Referencia externa reference_type VARCHAR(50), -- 'wallet_transaction', 'position', 'fee_charge' reference_id UUID, -- Descripción description TEXT, -- Estado status VARCHAR(20) DEFAULT 'completed', -- 'pending', 'completed', 'cancelled' processed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_account_tx_account ON account_transactions(account_id); CREATE INDEX idx_account_tx_type ON account_transactions(transaction_type); CREATE INDEX idx_account_tx_created ON account_transactions(created_at DESC); -- ============================================================================ -- TABLA: performance_snapshots -- Descripción: Snapshots periódicos de rendimiento -- ============================================================================ CREATE TABLE IF NOT EXISTS performance_snapshots ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, -- Período snapshot_date DATE NOT NULL, period_type VARCHAR(20) NOT NULL, -- 'daily', 'weekly', 'monthly' -- Valores opening_balance DECIMAL(15,2) NOT NULL, closing_balance DECIMAL(15,2) NOT NULL, deposits DECIMAL(15,2) DEFAULT 0, withdrawals DECIMAL(15,2) DEFAULT 0, profit_loss DECIMAL(15,2) NOT NULL, fees DECIMAL(15,2) DEFAULT 0, -- Métricas return_percent DECIMAL(8,4), drawdown_percent DECIMAL(5,2), -- Trading stats total_trades INT DEFAULT 0, winning_trades INT DEFAULT 0, positions_opened INT DEFAULT 0, positions_closed INT DEFAULT 0, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, UNIQUE(account_id, snapshot_date, period_type) ); CREATE INDEX idx_perf_snapshots_account ON performance_snapshots(account_id); CREATE INDEX idx_perf_snapshots_date ON performance_snapshots(snapshot_date DESC); -- ============================================================================ -- TABLA: profit_distributions -- Descripción: Distribución de utilidades (para cuentas con profit sharing) -- ============================================================================ CREATE TABLE IF NOT EXISTS profit_distributions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT, -- Período period_start DATE NOT NULL, period_end DATE NOT NULL, -- Cálculo gross_profit DECIMAL(15,2) NOT NULL, management_fee DECIMAL(15,2) DEFAULT 0, net_profit DECIMAL(15,2) NOT NULL, -- Distribución platform_share_percent DECIMAL(5,2) NOT NULL, client_share_percent DECIMAL(5,2) NOT NULL, platform_amount DECIMAL(15,2) NOT NULL, client_amount DECIMAL(15,2) NOT NULL, -- Estado status VARCHAR(20) DEFAULT 'pending', -- 'pending', 'approved', 'distributed', 'cancelled' distributed_at TIMESTAMPTZ, approved_by UUID REFERENCES auth.users(id), -- Referencia de pago payment_reference VARCHAR(100), created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_profit_dist_account ON profit_distributions(account_id); CREATE INDEX idx_profit_dist_period ON profit_distributions(period_end DESC); CREATE INDEX idx_profit_dist_status ON profit_distributions(status); -- ============================================================================ -- TABLA: deposit_requests -- Descripción: Solicitudes de depósito a cuentas de inversión -- ============================================================================ CREATE TABLE IF NOT EXISTS deposit_requests ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT, user_id UUID NOT NULL REFERENCES auth.users(id), amount DECIMAL(15,2) NOT NULL, currency CHAR(3) DEFAULT 'USD', -- Origen source_type VARCHAR(50) NOT NULL, -- 'wallet', 'external' source_wallet_id UUID, -- Referencia a financial.wallets -- Estado status VARCHAR(20) DEFAULT 'pending', -- 'pending', 'approved', 'completed', 'rejected' processed_at TIMESTAMPTZ, processed_by UUID REFERENCES auth.users(id), rejection_reason TEXT, -- Transacción resultante transaction_id UUID REFERENCES account_transactions(id), created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_deposit_requests_account ON deposit_requests(account_id); CREATE INDEX idx_deposit_requests_user ON deposit_requests(user_id); CREATE INDEX idx_deposit_requests_status ON deposit_requests(status); -- ============================================================================ -- TABLA: withdrawal_requests -- Descripción: Solicitudes de retiro de cuentas de inversión -- ============================================================================ CREATE TABLE IF NOT EXISTS withdrawal_requests ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT, user_id UUID NOT NULL REFERENCES auth.users(id), amount DECIMAL(15,2) NOT NULL, currency CHAR(3) DEFAULT 'USD', -- Destino destination_type VARCHAR(50) NOT NULL, -- 'wallet', 'bank_transfer' destination_wallet_id UUID, -- Estado status VARCHAR(20) DEFAULT 'pending', -- 'pending', 'processing', 'completed', 'rejected' processed_at TIMESTAMPTZ, processed_by UUID REFERENCES auth.users(id), rejection_reason TEXT, -- Comisiones fee_amount DECIMAL(10,2) DEFAULT 0, net_amount DECIMAL(15,2), -- Transacción resultante transaction_id UUID REFERENCES account_transactions(id), created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_withdrawal_requests_account ON withdrawal_requests(account_id); CREATE INDEX idx_withdrawal_requests_user ON withdrawal_requests(user_id); CREATE INDEX idx_withdrawal_requests_status ON withdrawal_requests(status); -- ============================================================================ -- TABLA: bot_assignments -- Descripción: Asignación de bots a cuentas de inversión -- ============================================================================ CREATE TABLE IF NOT EXISTS bot_assignments ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, bot_id UUID NOT NULL REFERENCES trading.bots(id), -- Estado is_active BOOLEAN DEFAULT TRUE, assigned_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, deactivated_at TIMESTAMPTZ, -- Razón del cambio assignment_reason TEXT, deactivation_reason TEXT, -- Usuario que asignó assigned_by UUID REFERENCES auth.users(id), -- NULL = automático created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_bot_assignments_account ON bot_assignments(account_id); CREATE INDEX idx_bot_assignments_bot ON bot_assignments(bot_id); CREATE INDEX idx_bot_assignments_active ON bot_assignments(is_active) WHERE is_active = TRUE; -- ============================================================================ -- TRIGGERS -- ============================================================================ CREATE TRIGGER update_products_updated_at BEFORE UPDATE ON products FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column(); CREATE TRIGGER update_accounts_updated_at BEFORE UPDATE ON accounts FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column(); CREATE TRIGGER update_deposit_requests_updated_at BEFORE UPDATE ON deposit_requests FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column(); CREATE TRIGGER update_withdrawal_requests_updated_at BEFORE UPDATE ON withdrawal_requests FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column(); CREATE TRIGGER update_profit_distributions_updated_at BEFORE UPDATE ON profit_distributions FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column(); -- ============================================================================ -- FUNCIÓN: Generar número de cuenta -- ============================================================================ CREATE OR REPLACE FUNCTION generate_account_number() RETURNS TRIGGER AS $$ DECLARE seq_num INT; BEGIN SELECT COALESCE(MAX(CAST(SUBSTRING(account_number FROM 8) AS INT)), 0) + 1 INTO seq_num FROM investment.accounts; NEW.account_number := 'OQ-INV-' || LPAD(seq_num::TEXT, 6, '0'); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER set_account_number BEFORE INSERT ON accounts FOR EACH ROW WHEN (NEW.account_number IS NULL) EXECUTE FUNCTION generate_account_number();