trading-platform-database-v2/ddl/schemas/trading/tables/001_symbols.sql

230 lines
7.1 KiB
PL/PgSQL

-- ============================================================================
-- SCHEMA: trading
-- TABLE: symbols
-- DESCRIPTION: Simbolos/instrumentos habilitados para trading por tenant
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 3 - DDL Implementation Roadmap Q1-2026
-- ============================================================================
-- Crear schema si no existe
CREATE SCHEMA IF NOT EXISTS trading;
-- Grant usage
GRANT USAGE ON SCHEMA trading TO trading_app;
GRANT USAGE ON SCHEMA trading TO trading_readonly;
-- ============================================================================
-- ENUMS COMPARTIDOS DEL SCHEMA TRADING
-- ============================================================================
-- Enum para direccion de trade
DO $$ BEGIN
CREATE TYPE trading.trade_direction AS ENUM (
'long', -- Compra / Buy
'short' -- Venta / Sell
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Enum para estado de posicion
DO $$ BEGIN
CREATE TYPE trading.position_status AS ENUM (
'pending', -- Pendiente de ejecucion
'open', -- Posicion abierta
'closed', -- Cerrada manualmente
'stopped', -- Cerrada por stop loss
'target_hit', -- Cerrada por take profit
'expired', -- Expirada
'cancelled' -- Cancelada antes de ejecutar
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Enum para tipo de orden
DO $$ BEGIN
CREATE TYPE trading.order_type AS ENUM (
'market', -- Orden de mercado
'limit', -- Orden limitada
'stop', -- Stop order
'stop_limit' -- Stop limit order
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Enum para timeframe
DO $$ BEGIN
CREATE TYPE trading.timeframe AS ENUM (
'M1', 'M5', 'M15', 'M30', -- Minutos
'H1', 'H4', -- Horas
'D1', 'W1', 'MN' -- Dia, Semana, Mes
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- ============================================================================
-- TABLA: symbols
-- Simbolos habilitados por tenant (copia de market_data.tickers con config tenant)
-- ============================================================================
CREATE TABLE IF NOT EXISTS trading.symbols (
-- Identificadores
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
ticker_id UUID NOT NULL REFERENCES market_data.tickers(id),
-- Datos del simbolo (denormalizados para performance)
symbol VARCHAR(20) NOT NULL,
name VARCHAR(200),
type market_data.instrument_type,
-- Configuracion por tenant
is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
is_tradeable BOOLEAN NOT NULL DEFAULT TRUE,
is_visible BOOLEAN NOT NULL DEFAULT TRUE,
-- Limites especificos del tenant
min_lot_size DECIMAL(10, 4),
max_lot_size DECIMAL(10, 4),
max_position_size DECIMAL(15, 2), -- Tamaño maximo de posicion en USD
max_daily_volume DECIMAL(15, 2), -- Volumen diario maximo
-- Spread markup (si el tenant agrega spread)
spread_markup_pips DECIMAL(10, 4) DEFAULT 0,
-- Comisiones del tenant
commission_per_lot DECIMAL(10, 4) DEFAULT 0,
commission_percent DECIMAL(5, 4) DEFAULT 0,
-- Margen requerido (override del default)
margin_required_percent DECIMAL(5, 2),
max_leverage INTEGER,
-- Trading hours override
custom_trading_hours JSONB,
-- Categorias/tags del tenant
categories VARCHAR(50)[],
tags VARCHAR(50)[],
display_order INTEGER DEFAULT 999,
-- Estadisticas del tenant
trade_count INTEGER NOT NULL DEFAULT 0,
total_volume DECIMAL(20, 4) NOT NULL DEFAULT 0,
last_trade_at TIMESTAMPTZ,
-- Metadata
metadata JSONB DEFAULT '{}'::JSONB,
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Constraints
CONSTRAINT symbols_unique_per_tenant UNIQUE (tenant_id, ticker_id),
CONSTRAINT symbols_unique_symbol_per_tenant UNIQUE (tenant_id, symbol)
);
COMMENT ON TABLE trading.symbols IS
'Simbolos habilitados para trading por tenant con configuracion personalizada';
COMMENT ON COLUMN trading.symbols.spread_markup_pips IS
'Pips adicionales de spread que el tenant agrega al precio del broker';
-- Indices
CREATE INDEX IF NOT EXISTS idx_symbols_tenant
ON trading.symbols(tenant_id);
CREATE INDEX IF NOT EXISTS idx_symbols_ticker
ON trading.symbols(ticker_id);
CREATE INDEX IF NOT EXISTS idx_symbols_symbol
ON trading.symbols(tenant_id, symbol);
CREATE INDEX IF NOT EXISTS idx_symbols_enabled
ON trading.symbols(tenant_id, is_enabled, is_tradeable)
WHERE is_enabled = TRUE AND is_tradeable = TRUE;
CREATE INDEX IF NOT EXISTS idx_symbols_type
ON trading.symbols(tenant_id, type);
-- GIN index para categorias
CREATE INDEX IF NOT EXISTS idx_symbols_categories_gin
ON trading.symbols USING GIN (categories);
-- Trigger para updated_at
CREATE OR REPLACE FUNCTION trading.update_trading_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS symbol_updated_at ON trading.symbols;
CREATE TRIGGER symbol_updated_at
BEFORE UPDATE ON trading.symbols
FOR EACH ROW
EXECUTE FUNCTION trading.update_trading_timestamp();
-- Trigger para sincronizar datos del ticker
CREATE OR REPLACE FUNCTION trading.sync_symbol_from_ticker()
RETURNS TRIGGER AS $$
BEGIN
SELECT symbol, name, type
INTO NEW.symbol, NEW.name, NEW.type
FROM market_data.tickers
WHERE id = NEW.ticker_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS symbol_sync ON trading.symbols;
CREATE TRIGGER symbol_sync
BEFORE INSERT OR UPDATE OF ticker_id ON trading.symbols
FOR EACH ROW
EXECUTE FUNCTION trading.sync_symbol_from_ticker();
-- Vista de simbolos activos
CREATE OR REPLACE VIEW trading.v_active_symbols AS
SELECT
s.id,
s.tenant_id,
s.ticker_id,
s.symbol,
s.name,
s.type,
t.pip_size,
t.typical_spread_pips,
s.spread_markup_pips,
(COALESCE(t.typical_spread_pips, 0) + COALESCE(s.spread_markup_pips, 0)) AS total_spread_pips,
t.current_bid,
t.current_ask,
s.min_lot_size,
s.max_lot_size,
COALESCE(s.max_leverage, t.max_leverage) AS max_leverage,
s.display_order
FROM trading.symbols s
JOIN market_data.tickers t ON s.ticker_id = t.id
WHERE s.is_enabled = TRUE
AND s.is_tradeable = TRUE
AND t.status = 'active'
ORDER BY s.display_order, s.symbol;
-- RLS Policy para multi-tenancy
ALTER TABLE trading.symbols ENABLE ROW LEVEL SECURITY;
CREATE POLICY symbols_tenant_isolation ON trading.symbols
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
-- Grants
GRANT SELECT, INSERT, UPDATE, DELETE ON trading.symbols TO trading_app;
GRANT SELECT ON trading.symbols TO trading_readonly;
GRANT SELECT ON trading.v_active_symbols TO trading_app;