trading-platform-database/ddl/schemas/trading/functions/01-calculate_position_pnl.sql

97 lines
3.2 KiB
PL/PgSQL

-- ============================================================================
-- Schema: trading
-- Function: calculate_position_pnl
-- Description: Calcula el PnL (realized y unrealized) de una posición
-- Parameters:
-- p_position_id: ID de la posición
-- p_current_price: Precio actual del mercado (para unrealized PnL)
-- Returns: JSONB con realized_pnl, unrealized_pnl, pnl_percentage
-- ============================================================================
CREATE OR REPLACE FUNCTION trading.calculate_position_pnl(
p_position_id UUID,
p_current_price DECIMAL(20,8) DEFAULT NULL
)
RETURNS JSONB
LANGUAGE plpgsql
AS $$
DECLARE
v_position RECORD;
v_realized_pnl DECIMAL(20,8) := 0;
v_unrealized_pnl DECIMAL(20,8) := 0;
v_pnl_percentage DECIMAL(10,4) := 0;
v_total_commission DECIMAL(20,8) := 0;
v_current_value DECIMAL(20,8);
BEGIN
-- Obtener datos de la posición
SELECT
p.position_side,
p.status,
p.entry_price,
p.entry_quantity,
p.entry_value,
p.entry_commission,
p.exit_price,
p.exit_quantity,
p.exit_value,
p.exit_commission
INTO v_position
FROM trading.positions p
WHERE p.id = p_position_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Position % not found', p_position_id;
END IF;
-- Calcular comisiones totales
v_total_commission := COALESCE(v_position.entry_commission, 0) + COALESCE(v_position.exit_commission, 0);
-- Calcular PnL según el estado
IF v_position.status = 'closed' OR v_position.status = 'liquidated' THEN
-- Posición cerrada: calcular realized PnL
IF v_position.position_side = 'buy' THEN
-- Long position: profit = (exit_price - entry_price) * quantity - commissions
v_realized_pnl := (v_position.exit_value - v_position.entry_value) - v_total_commission;
ELSE
-- Short position: profit = (entry_price - exit_price) * quantity - commissions
v_realized_pnl := (v_position.entry_value - v_position.exit_value) - v_total_commission;
END IF;
-- Calcular porcentaje
IF v_position.entry_value > 0 THEN
v_pnl_percentage := (v_realized_pnl / v_position.entry_value) * 100;
END IF;
ELSIF v_position.status = 'open' AND p_current_price IS NOT NULL THEN
-- Posición abierta: calcular unrealized PnL
v_current_value := p_current_price * v_position.entry_quantity;
IF v_position.position_side = 'buy' THEN
-- Long position
v_unrealized_pnl := (v_current_value - v_position.entry_value) - v_position.entry_commission;
ELSE
-- Short position
v_unrealized_pnl := (v_position.entry_value - v_current_value) - v_position.entry_commission;
END IF;
-- Calcular porcentaje
IF v_position.entry_value > 0 THEN
v_pnl_percentage := (v_unrealized_pnl / v_position.entry_value) * 100;
END IF;
END IF;
-- Retornar resultado
RETURN jsonb_build_object(
'position_id', p_position_id,
'status', v_position.status,
'realized_pnl', v_realized_pnl,
'unrealized_pnl', v_unrealized_pnl,
'pnl_percentage', v_pnl_percentage,
'total_commission', v_total_commission
);
END;
$$;
-- Comentarios
COMMENT ON FUNCTION trading.calculate_position_pnl IS 'Calcula el PnL realizado y no realizado de una posición';