| 5 | | Функција 1: Проверка дали корисникот има доволно баланс за BUY |
| 6 | | |
| 7 | | {{{ |
| 8 | | CREATE OR REPLACE FUNCTION check_portfolio_balance( |
| 9 | | p_portfolio_id BIGINT, |
| 10 | | p_price FLOAT8, |
| 11 | | p_quantity INT |
| 12 | | ) |
| 13 | | RETURNS BOOLEAN AS $$ |
| 14 | | DECLARE |
| 15 | | v_balance NUMERIC(18,2); |
| 16 | | v_total FLOAT8; |
| 17 | | BEGIN |
| 18 | | SELECT balance INTO v_balance |
| 19 | | FROM portfolios |
| 20 | | WHERE id = p_portfolio_id; |
| 21 | | |
| 22 | | v_total := p_price * p_quantity; |
| 23 | | |
| 24 | | IF v_balance >= v_total THEN |
| 25 | | RETURN TRUE; |
| 26 | | ELSE |
| 27 | | RETURN FALSE; |
| 28 | | END IF; |
| | 5 | Функција 1: Ажурира и враќа моменталната добивка/загуба на CFD позицијата врз основа на најновата цена на акциите. |
| | 6 | |
| | 7 | {{{ |
| | 8 | CREATE OR REPLACE FUNCTION calculate_cfd_profit_loss(p_cfd_id BIGINT) |
| | 9 | RETURNS FLOAT8 |
| | 10 | AS $$ |
| | 11 | DECLARE |
| | 12 | v_quantity INT; |
| | 13 | v_open_price FLOAT8; |
| | 14 | v_current_price FLOAT8; |
| | 15 | v_profit_loss FLOAT8; |
| | 16 | BEGIN |
| | 17 | SELECT c.quantity, |
| | 18 | c.open_price, |
| | 19 | s.current_price |
| | 20 | INTO v_quantity, |
| | 21 | v_open_price, |
| | 22 | v_current_price |
| | 23 | FROM CFD_position c |
| | 24 | JOIN stock s ON c.stock_id = s.id |
| | 25 | WHERE c.id = p_cfd_id; |
| | 26 | |
| | 27 | v_profit_loss := |
| | 28 | (v_current_price - v_open_price) * v_quantity; |
| | 29 | |
| | 30 | UPDATE CFD_position |
| | 31 | SET profit_loss = v_profit_loss |
| | 32 | WHERE id = p_cfd_id; |
| | 33 | |
| | 34 | RETURN v_profit_loss; |
| 60 | | Процедура 1: Креирање на trade request од корисник |
| | 94 | Процедура 1: Одобрување на trade request од admin - креира broker order и settlement |
| | 95 | |
| | 96 | |
| | 97 | {{{ |
| | 98 | CREATE OR REPLACE PROCEDURE approve_trade_request( |
| | 99 | p_trade_request_id BIGINT |
| | 100 | ) |
| | 101 | AS $$ |
| | 102 | DECLARE |
| | 103 | v_tr trade_request%ROWTYPE; |
| | 104 | v_portfolio portfolios%ROWTYPE; |
| | 105 | v_broker_order_id BIGINT; |
| | 106 | v_stock_id BIGINT; |
| | 107 | BEGIN |
| | 108 | |
| | 109 | SELECT * INTO v_tr |
| | 110 | FROM trade_request |
| | 111 | WHERE id = p_trade_request_id AND status = 'OPEN'; |
| | 112 | |
| | 113 | IF NOT FOUND THEN |
| | 114 | RAISE EXCEPTION 'Trade request % не постои или не е OPEN', p_trade_request_id; |
| | 115 | END IF; |
| | 116 | |
| | 117 | SELECT * INTO v_portfolio |
| | 118 | FROM portfolios |
| | 119 | WHERE id = v_tr.portfolio_id; |
| | 120 | |
| | 121 | |
| | 122 | SELECT id INTO v_stock_id |
| | 123 | FROM stock |
| | 124 | WHERE symbol = v_tr.stock_symbol; |
| | 125 | |
| | 126 | |
| | 127 | UPDATE trade_request |
| | 128 | SET status = 'CLOSED' |
| | 129 | WHERE id = p_trade_request_id; |
| | 130 | |
| | 131 | |
| | 132 | INSERT INTO broker_order ( |
| | 133 | order_type, limit_price, quantity, status, |
| | 134 | executed_price, broker_fee, mse_reference_number, |
| | 135 | submitted_at, executed_at, trade_request_id |
| | 136 | ) |
| | 137 | VALUES ( |
| | 138 | v_tr.type, v_tr.price_per_unit, v_tr.quantity, 'EXECUTED', |
| | 139 | v_tr.price_per_unit, (random() * 50)::FLOAT8, |
| | 140 | 'MSE-' || p_trade_request_id, |
| | 141 | v_tr.timestamp, NOW(), p_trade_request_id |
| | 142 | ) |
| | 143 | RETURNING id INTO v_broker_order_id; |
| | 144 | |
| | 145 | |
| | 146 | INSERT INTO settlement_record ( |
| | 147 | broker_order_id, user_id, stock_symbol, quantity, |
| | 148 | settled_price, total_cost, settlement_status, settled_at |
| | 149 | ) |
| | 150 | VALUES ( |
| | 151 | v_broker_order_id, v_portfolio.user_id, v_tr.stock_symbol, |
| | 152 | v_tr.quantity, v_tr.price_per_unit, |
| | 153 | v_tr.price_per_unit * v_tr.quantity, |
| | 154 | 'SETTLED', NOW() |
| | 155 | ); |
| | 156 | |
| | 157 | |
| | 158 | INSERT INTO trade_transaction ( |
| | 159 | price, quantity, timestamp, type, origin, user_id, stock_id |
| | 160 | ) |
| | 161 | VALUES ( |
| | 162 | v_tr.price_per_unit, v_tr.quantity, NOW(), |
| | 163 | v_tr.type, 'INTERNAL', v_portfolio.user_id, v_stock_id |
| | 164 | ); |
| | 165 | |
| | 166 | RAISE NOTICE 'Trade request % одобрен — Broker Order % креиран', |
| | 167 | p_trade_request_id, v_broker_order_id; |
| | 168 | END; |
| | 169 | $$ LANGUAGE plpgsql; |
| | 170 | }}} |
| | 171 | |
| | 172 | |
| | 173 | |
| | 174 | Процедура 2: Креирање на trade request од корисник |
| 100 | | Процедура 2: Одобрување на trade request од admin - креира broker order и settlement |
| 101 | | |
| 102 | | |
| 103 | | {{{ |
| 104 | | CREATE OR REPLACE PROCEDURE approve_trade_request( |
| 105 | | p_trade_request_id BIGINT |
| 106 | | ) |
| 107 | | AS $$ |
| 108 | | DECLARE |
| 109 | | v_tr trade_request%ROWTYPE; |
| 110 | | v_portfolio portfolios%ROWTYPE; |
| 111 | | v_broker_order_id BIGINT; |
| 112 | | v_stock_id BIGINT; |
| 113 | | BEGIN |
| 114 | | |
| 115 | | SELECT * INTO v_tr |
| 116 | | FROM trade_request |
| 117 | | WHERE id = p_trade_request_id AND status = 'OPEN'; |
| 118 | | |
| 119 | | IF NOT FOUND THEN |
| 120 | | RAISE EXCEPTION 'Trade request % не постои или не е OPEN', p_trade_request_id; |
| 121 | | END IF; |
| 122 | | |
| 123 | | SELECT * INTO v_portfolio |
| 124 | | FROM portfolios |
| 125 | | WHERE id = v_tr.portfolio_id; |
| 126 | | |
| 127 | | |
| 128 | | SELECT id INTO v_stock_id |
| 129 | | FROM stock |
| 130 | | WHERE symbol = v_tr.stock_symbol; |
| 131 | | |
| 132 | | |
| 133 | | UPDATE trade_request |
| 134 | | SET status = 'CLOSED' |
| 135 | | WHERE id = p_trade_request_id; |
| 136 | | |
| 137 | | |
| 138 | | INSERT INTO broker_order ( |
| 139 | | order_type, limit_price, quantity, status, |
| 140 | | executed_price, broker_fee, mse_reference_number, |
| 141 | | submitted_at, executed_at, trade_request_id |
| 142 | | ) |
| 143 | | VALUES ( |
| 144 | | v_tr.type, v_tr.price_per_unit, v_tr.quantity, 'EXECUTED', |
| 145 | | v_tr.price_per_unit, (random() * 50)::FLOAT8, |
| 146 | | 'MSE-' || p_trade_request_id, |
| 147 | | v_tr.timestamp, NOW(), p_trade_request_id |
| 148 | | ) |
| 149 | | RETURNING id INTO v_broker_order_id; |
| 150 | | |
| 151 | | |
| 152 | | INSERT INTO settlement_record ( |
| 153 | | broker_order_id, user_id, stock_symbol, quantity, |
| 154 | | settled_price, total_cost, settlement_status, settled_at |
| 155 | | ) |
| 156 | | VALUES ( |
| 157 | | v_broker_order_id, v_portfolio.user_id, v_tr.stock_symbol, |
| 158 | | v_tr.quantity, v_tr.price_per_unit, |
| 159 | | v_tr.price_per_unit * v_tr.quantity, |
| 160 | | 'SETTLED', NOW() |
| 161 | | ); |
| 162 | | |
| 163 | | |
| 164 | | INSERT INTO trade_transaction ( |
| 165 | | price, quantity, timestamp, type, origin, user_id, stock_id |
| 166 | | ) |
| 167 | | VALUES ( |
| 168 | | v_tr.price_per_unit, v_tr.quantity, NOW(), |
| 169 | | v_tr.type, 'INTERNAL', v_portfolio.user_id, v_stock_id |
| 170 | | ); |
| 171 | | |
| 172 | | RAISE NOTICE 'Trade request % одобрен — Broker Order % креиран', |
| 173 | | p_trade_request_id, v_broker_order_id; |
| 174 | | END; |
| 175 | | $$ LANGUAGE plpgsql; |
| 176 | | }}} |
| 177 | | |
| 178 | | |
| 182 | | Тригер 1: При SELL - провери дали корисникот има доволно акции во holdings |
| 183 | | |
| 184 | | |
| 185 | | {{{ |
| 186 | | CREATE OR REPLACE FUNCTION check_holdings_before_sell() |
| 187 | | RETURNS TRIGGER AS $$ |
| 188 | | DECLARE |
| 189 | | v_stock_id BIGINT; |
| 190 | | v_available_qty INT; |
| 191 | | BEGIN |
| 192 | | IF NEW.type = 'SELL' THEN |
| 193 | | SELECT id INTO v_stock_id |
| 194 | | FROM stock WHERE symbol = NEW.stock_symbol; |
| 195 | | |
| 196 | | SELECT quantity INTO v_available_qty |
| 197 | | FROM portfolio_holdings |
| 198 | | WHERE portfolio_id = NEW.portfolio_id |
| 199 | | AND stock_id = v_stock_id; |
| 200 | | |
| 201 | | IF NOT FOUND OR v_available_qty < NEW.quantity THEN |
| 202 | | RAISE EXCEPTION 'Недоволно акции за продажба. Достапно: %, Побарано: %', |
| 203 | | COALESCE(v_available_qty, 0), NEW.quantity; |
| 204 | | END IF; |
| 205 | | END IF; |
| 206 | | |
| 207 | | RETURN NEW; |
| 208 | | END; |
| 209 | | $$ LANGUAGE plpgsql; |
| 210 | | |
| 211 | | CREATE TRIGGER trg_check_holdings_before_sell |
| 212 | | BEFORE INSERT ON trade_request |
| 213 | | FOR EACH ROW |
| 214 | | EXECUTE FUNCTION check_holdings_before_sell(); |
| 215 | | }}} |
| 216 | | |
| 217 | | |
| 218 | | Тригер 2: цената треба автоматски да се менува кога ќе се случи нова трансакција |
| | 217 | Тригер 1: Цената треба автоматски да се менува кога ќе се случи нова трансакција |
| | 248 | Тригер 2: При SELL - провери дали корисникот има доволно акции во holdings |
| | 249 | |
| | 250 | |
| | 251 | {{{ |
| | 252 | CREATE OR REPLACE FUNCTION check_holdings_before_sell() |
| | 253 | RETURNS TRIGGER AS $$ |
| | 254 | DECLARE |
| | 255 | v_stock_id BIGINT; |
| | 256 | v_available_qty INT; |
| | 257 | BEGIN |
| | 258 | IF NEW.type = 'SELL' THEN |
| | 259 | SELECT id INTO v_stock_id |
| | 260 | FROM stock WHERE symbol = NEW.stock_symbol; |
| | 261 | |
| | 262 | SELECT quantity INTO v_available_qty |
| | 263 | FROM portfolio_holdings |
| | 264 | WHERE portfolio_id = NEW.portfolio_id |
| | 265 | AND stock_id = v_stock_id; |
| | 266 | |
| | 267 | IF NOT FOUND OR v_available_qty < NEW.quantity THEN |
| | 268 | RAISE EXCEPTION 'Недоволно акции за продажба. Достапно: %, Побарано: %', |
| | 269 | COALESCE(v_available_qty, 0), NEW.quantity; |
| | 270 | END IF; |
| | 271 | END IF; |
| | 272 | |
| | 273 | RETURN NEW; |
| | 274 | END; |
| | 275 | $$ LANGUAGE plpgsql; |
| | 276 | |
| | 277 | CREATE TRIGGER trg_check_holdings_before_sell |
| | 278 | BEFORE INSERT ON trade_request |
| | 279 | FOR EACH ROW |
| | 280 | EXECUTE FUNCTION check_holdings_before_sell(); |
| | 281 | }}} |
| | 282 | |
| | 283 | |
| | 284 | |
| | 285 | |