trading-platform-database-v2/ddl/schemas/broker_integration/tables/001_broker_accounts.sql
rckrdmrd 4631a58b42 [DDL] feat: Sprint 6 - Add broker_integration and portfolio_management schemas
broker_integration (5 tables):
- broker_accounts: Cuentas MT4/MT5/API conectadas
- broker_prices: Precios en tiempo real
- spread_statistics: Estadisticas historicas de spread
- price_adjustment_model: Modelos de ajuste de precio
- trade_execution: Ejecucion de ordenes

portfolio_management (6 tables):
- portfolios: Portafolios de inversion
- portfolio_accounts: Cuentas vinculadas a portafolios
- investment_goals: Metas de inversion
- rebalance_suggestions: Sugerencias de rebalanceo
- portfolio_snapshots: Snapshots historicos
- monte_carlo_projections: Proyecciones Monte Carlo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 20:19:27 -06:00

256 lines
7.8 KiB
PL/PgSQL

-- ============================================================================
-- SCHEMA: broker_integration
-- TABLE: broker_accounts
-- DESCRIPTION: Cuentas de broker conectadas (MT4, MT5, etc.)
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 6 - DDL Implementation Roadmap Q1-2026
-- ============================================================================
-- Crear schema
CREATE SCHEMA IF NOT EXISTS broker_integration;
COMMENT ON SCHEMA broker_integration IS
'Integracion con brokers de trading (MT4, MT5, API de brokers)';
-- Enum para tipo de broker
DO $$ BEGIN
CREATE TYPE broker_integration.broker_type AS ENUM (
'mt4',
'mt5',
'ctrader',
'fix_api',
'rest_api',
'binance',
'interactive_brokers',
'oanda',
'alpaca',
'custom'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Enum para tipo de cuenta
DO $$ BEGIN
CREATE TYPE broker_integration.account_type AS ENUM (
'demo',
'live',
'contest'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Enum para estado de conexion
DO $$ BEGIN
CREATE TYPE broker_integration.connection_status AS ENUM (
'connected',
'disconnected',
'connecting',
'error',
'maintenance',
'suspended',
'expired'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Tabla de Cuentas de Broker
CREATE TABLE IF NOT EXISTS broker_integration.broker_accounts (
-- Identificadores
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,
-- Broker info
broker_type broker_integration.broker_type NOT NULL,
broker_name VARCHAR(100) NOT NULL, -- "XM", "IC Markets"
broker_server VARCHAR(255), -- Server name/address
-- Cuenta
account_type broker_integration.account_type NOT NULL DEFAULT 'demo',
account_number VARCHAR(50) NOT NULL,
account_name VARCHAR(100),
-- Conexion
connection_status broker_integration.connection_status NOT NULL DEFAULT 'disconnected',
connection_provider VARCHAR(50), -- "metaapi", "direct"
provider_account_id VARCHAR(100), -- ID en el proveedor
-- Credenciales (encriptadas)
login_encrypted TEXT,
password_encrypted TEXT,
investor_password_encrypted TEXT,
api_token_encrypted TEXT,
additional_credentials JSONB DEFAULT '{}'::JSONB,
-- Configuracion
leverage INTEGER,
currency VARCHAR(10) NOT NULL DEFAULT 'USD',
timezone VARCHAR(50) DEFAULT 'UTC',
-- Balance y equity
balance DECIMAL(15, 2),
equity DECIMAL(15, 2),
margin DECIMAL(15, 2),
free_margin DECIMAL(15, 2),
margin_level DECIMAL(10, 2),
-- Configuracion de trading
enable_trading BOOLEAN NOT NULL DEFAULT FALSE,
max_positions INTEGER,
max_lot_size DECIMAL(10, 2),
min_lot_size DECIMAL(10, 4) DEFAULT 0.01,
lot_step DECIMAL(10, 4) DEFAULT 0.01,
-- Permisos
allow_copy_trading BOOLEAN NOT NULL DEFAULT FALSE,
allow_signals BOOLEAN NOT NULL DEFAULT TRUE,
allow_auto_trading BOOLEAN NOT NULL DEFAULT FALSE,
-- Restricciones
allowed_symbols VARCHAR(20)[], -- NULL = todos
blocked_symbols VARCHAR(20)[],
allowed_directions VARCHAR(10)[], -- ['long', 'short']
trading_hours JSONB,
-- Estado
is_active BOOLEAN NOT NULL DEFAULT TRUE,
is_default BOOLEAN NOT NULL DEFAULT FALSE,
is_verified BOOLEAN NOT NULL DEFAULT FALSE,
-- Ultima sincronizacion
last_sync_at TIMESTAMPTZ,
last_sync_error TEXT,
sync_interval_seconds INTEGER DEFAULT 60,
-- Estadisticas
total_trades INTEGER NOT NULL DEFAULT 0,
open_positions INTEGER NOT NULL DEFAULT 0,
total_profit DECIMAL(15, 2) NOT NULL DEFAULT 0,
today_profit DECIMAL(15, 2) NOT NULL DEFAULT 0,
-- Metadata
metadata JSONB DEFAULT '{}'::JSONB,
notes TEXT,
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
connected_at TIMESTAMPTZ,
disconnected_at TIMESTAMPTZ,
verified_at TIMESTAMPTZ,
-- Constraints
CONSTRAINT broker_accounts_unique UNIQUE (tenant_id, broker_type, account_number)
);
COMMENT ON TABLE broker_integration.broker_accounts IS
'Cuentas de broker conectadas para trading real o demo';
COMMENT ON COLUMN broker_integration.broker_accounts.enable_trading IS
'Si TRUE, permite ejecutar ordenes reales en esta cuenta';
-- Indices
CREATE INDEX IF NOT EXISTS idx_broker_accounts_tenant
ON broker_integration.broker_accounts(tenant_id);
CREATE INDEX IF NOT EXISTS idx_broker_accounts_user
ON broker_integration.broker_accounts(user_id);
CREATE INDEX IF NOT EXISTS idx_broker_accounts_type
ON broker_integration.broker_accounts(broker_type);
CREATE INDEX IF NOT EXISTS idx_broker_accounts_status
ON broker_integration.broker_accounts(connection_status);
CREATE INDEX IF NOT EXISTS idx_broker_accounts_active
ON broker_integration.broker_accounts(user_id, is_active, connection_status)
WHERE is_active = TRUE;
CREATE INDEX IF NOT EXISTS idx_broker_accounts_default
ON broker_integration.broker_accounts(user_id, is_default)
WHERE is_default = TRUE;
CREATE INDEX IF NOT EXISTS idx_broker_accounts_sync
ON broker_integration.broker_accounts(last_sync_at)
WHERE is_active = TRUE AND connection_status = 'connected';
-- Funcion de timestamp
CREATE OR REPLACE FUNCTION broker_integration.update_broker_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Trigger para updated_at
DROP TRIGGER IF EXISTS broker_account_updated_at ON broker_integration.broker_accounts;
CREATE TRIGGER broker_account_updated_at
BEFORE UPDATE ON broker_integration.broker_accounts
FOR EACH ROW
EXECUTE FUNCTION broker_integration.update_broker_timestamp();
-- Trigger para asegurar solo una cuenta default
CREATE OR REPLACE FUNCTION broker_integration.ensure_single_default_broker()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.is_default = TRUE THEN
UPDATE broker_integration.broker_accounts
SET is_default = FALSE
WHERE user_id = NEW.user_id
AND id != NEW.id
AND is_default = TRUE;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS broker_account_single_default ON broker_integration.broker_accounts;
CREATE TRIGGER broker_account_single_default
BEFORE INSERT OR UPDATE OF is_default ON broker_integration.broker_accounts
FOR EACH ROW
WHEN (NEW.is_default = TRUE)
EXECUTE FUNCTION broker_integration.ensure_single_default_broker();
-- Vista de cuentas activas
CREATE OR REPLACE VIEW broker_integration.v_active_accounts AS
SELECT
id,
user_id,
broker_type,
broker_name,
account_type,
account_number,
connection_status,
balance,
equity,
leverage,
currency,
open_positions,
is_default,
last_sync_at
FROM broker_integration.broker_accounts
WHERE is_active = TRUE
ORDER BY is_default DESC, broker_name;
-- RLS Policies
ALTER TABLE broker_integration.broker_accounts ENABLE ROW LEVEL SECURITY;
CREATE POLICY broker_accounts_tenant ON broker_integration.broker_accounts
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
CREATE POLICY broker_accounts_user ON broker_integration.broker_accounts
FOR ALL
USING (user_id = current_setting('app.current_user_id', true)::UUID);
-- Grants
GRANT USAGE ON SCHEMA broker_integration TO trading_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON broker_integration.broker_accounts TO trading_app;
GRANT SELECT ON broker_integration.broker_accounts TO trading_readonly;
GRANT SELECT ON broker_integration.v_active_accounts TO trading_app;