/* # Full Schema Reference — Bytelyst Trading Platform Last updated: 2026-02-07 This file documents ALL 7 tables used by the Trading Dashboard and Bot Service. Run individual CREATE TABLE statements or use migration 004_full_schema_sync.sql in the bot-service/schema/ folder to apply all changes. Tables: 1. users — User accounts (auth trigger-created) 2. entries — Watchlist & manual positions 3. trade_history — Completed trade ledger 4. orders — Active/pending orders 5. trade_profiles — Strategy profiles with per-profile rule config 6. bot_config — Global config key-value store (dashboard-editable) 7. dynamic_config — Runtime config overrides (bot startup) */ -- ════════════════════════════════════════════════════════════ -- 1. USERS TABLE (Created by auth trigger — reference only) -- ════════════════════════════════════════════════════════════ -- CREATE TABLE IF NOT EXISTS users ( -- user_id uuid PRIMARY KEY REFERENCES auth.users, -- first_name text, -- last_name text, -- email text, -- "ALPACA_API_KEY" text, -- "ALPACA_SECRET_KEY" text, -- "REAL_ALPACA_API_KEY" text, -- "REAL_ALPACA_SECRET_KEY" text, -- trade_enable boolean DEFAULT false, -- role text DEFAULT 'user', -- drop_threshold_for_buy numeric, -- gain_threshold_for_sell numeric, -- market_poll_interval_in_seconds integer -- ); -- Auth trigger: auto-create user row on signup CREATE OR REPLACE FUNCTION public.handle_new_user() RETURNS trigger AS $$ BEGIN INSERT INTO public.users (user_id, email, role, trade_enable) VALUES (new.id, new.email, 'user', false); RETURN new; END; $$ LANGUAGE plpgsql SECURITY DEFINER; DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user(); -- ════════════════════════════════════════════════════════════ -- 2. ENTRIES TABLE (Watchlist & Manual Positions) -- ════════════════════════════════════════════════════════════ CREATE TABLE IF NOT EXISTS entries ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), stock_instance_id uuid DEFAULT gen_random_uuid(), user_id uuid REFERENCES auth.users NOT NULL DEFAULT auth.uid(), -- Core symbol text NOT NULL, label text, notes text, -- Config active boolean DEFAULT true, is_real_trade boolean DEFAULT false, is_crypto boolean DEFAULT false, status text DEFAULT 'active', -- 'active', 'sellCompleted', 'closed' -- Trading Data quantity numeric, filled_quantity numeric, entry_price numeric, buy_price numeric, sell_price numeric, buy_time timestamptz, sell_time timestamptz, -- Automation drop_threshold_for_buy numeric, gain_threshold_for_sell numeric, created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now() ); ALTER TABLE entries ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can manage own entries" ON entries USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); -- ════════════════════════════════════════════════════════════ -- 3. TRADE_HISTORY TABLE (Completed Trade Ledger) -- ════════════════════════════════════════════════════════════ CREATE TABLE IF NOT EXISTS trade_history ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid REFERENCES auth.users NOT NULL, profile_id uuid REFERENCES trade_profiles(id) ON DELETE SET NULL, -- Trade Data symbol text NOT NULL, side text NOT NULL, -- 'BUY' or 'SELL' entry_price numeric, exit_price numeric, size numeric, pnl numeric, -- Realized P&L ($) pnl_percent numeric, -- Realized P&L (%) stop_loss numeric, -- SL price at entry take_profit numeric, -- TP price at entry -- Context reason text, -- 'Signal Flip', 'Stop Loss', 'Manual', etc. rules_metadata jsonb, -- Snapshot of rule statuses at trade time timestamp bigint, -- Unix ms from bot execution created_at timestamptz DEFAULT now() ); ALTER TABLE trade_history ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can view own trade history" ON trade_history FOR SELECT USING (auth.uid() = user_id); CREATE INDEX IF NOT EXISTS idx_trade_history_profile_id ON trade_history(profile_id); CREATE INDEX IF NOT EXISTS idx_trade_history_user_symbol ON trade_history(user_id, symbol); -- ════════════════════════════════════════════════════════════ -- 4. ORDERS TABLE (Active/Pending Orders) -- ════════════════════════════════════════════════════════════ CREATE TABLE IF NOT EXISTS orders ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid REFERENCES auth.users NOT NULL, profile_id uuid REFERENCES trade_profiles(id) ON DELETE SET NULL, -- Order Identification order_id text, -- Exchange order ID symbol text NOT NULL, -- Order Details type text NOT NULL, -- 'Market', 'Limit', 'Stop' side text NOT NULL, -- 'BUY' or 'SELL' qty numeric NOT NULL, price numeric, stop_loss numeric, -- SL price at order time take_profit numeric, -- TP price at order time -- Status status text DEFAULT 'pending', -- 'pending', 'filled', 'cancelled', 'rejected' -- Timestamps timestamp bigint, -- Unix ms when order was placed filled_at timestamptz, created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now() ); ALTER TABLE orders ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can view own orders" ON orders FOR SELECT USING (auth.uid() = user_id); CREATE INDEX IF NOT EXISTS idx_orders_user_status ON orders(user_id, status); CREATE INDEX IF NOT EXISTS idx_orders_symbol ON orders(symbol); CREATE INDEX IF NOT EXISTS idx_orders_profile_id ON orders(profile_id); -- ════════════════════════════════════════════════════════════ -- 5. TRADE_PROFILES TABLE (Strategy Profiles) -- ════════════════════════════════════════════════════════════ CREATE TABLE IF NOT EXISTS trade_profiles ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid REFERENCES auth.users NOT NULL, name text NOT NULL, allocated_capital numeric DEFAULT 1000, risk_per_trade_percent numeric DEFAULT 1.0, symbols text DEFAULT 'BTC/USDT, ETH/USDT', is_active boolean DEFAULT true, strategy_config jsonb DEFAULT '{ "rules": [ { "ruleId": "TrendBiasRule", "enabled": true, "params": {} }, { "ruleId": "SessionRule", "enabled": true, "params": {} }, { "ruleId": "ZoneRule", "enabled": true, "params": {} }, { "ruleId": "MomentumRule", "enabled": true, "params": {} }, { "ruleId": "EntryTriggerRule", "enabled": true, "params": {} }, { "ruleId": "RiskManagementRule", "enabled": true, "params": {} }, { "ruleId": "AIAnalysisRule", "enabled": false, "params": {} } ], "riskLimits": { "maxDailyLossUsd": 50, "maxConsecutiveLosses": 2, "maxOpenTrades": 3 }, "execution": { "orderType": "market", "cooldownMinutes": 30 } }'::jsonb, created_at timestamptz DEFAULT now() ); ALTER TABLE trade_profiles ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can manage own profiles" ON trade_profiles USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); CREATE INDEX IF NOT EXISTS idx_trade_profiles_user_id ON trade_profiles(user_id); CREATE INDEX IF NOT EXISTS idx_trade_profiles_is_active ON trade_profiles(is_active); -- ════════════════════════════════════════════════════════════ -- 6. BOT_CONFIG TABLE (Global Config — Dashboard Editable) -- ════════════════════════════════════════════════════════════ CREATE TABLE IF NOT EXISTS bot_config ( key text PRIMARY KEY, value text, description text, updated_at timestamptz DEFAULT now() ); ALTER TABLE bot_config ENABLE ROW LEVEL SECURITY; CREATE POLICY "Authenticated users can read bot_config" ON bot_config FOR SELECT USING (auth.role() = 'authenticated'); -- ════════════════════════════════════════════════════════════ -- 7. DYNAMIC_CONFIG TABLE (Bot Runtime Overrides) -- ════════════════════════════════════════════════════════════ CREATE TABLE IF NOT EXISTS dynamic_config ( key text PRIMARY KEY, value text, updated_at timestamptz DEFAULT now() );