source: docs/P4-Prototype/wiki/UseCase0006Implementation.md

main
Last change on this file was ef1c1c7, checked in by Stefan <trsunovstefan@…>, 6 days ago

Wiki docs, phase 6 and phase 7 added

  • Property mode set to 100644
File size: 8.6 KB
Line 
1= Use-case 0006 Implementation - View portfolio and transaction history =
2
3'''Initiating actor:''' Trader
4
5'''Other actors:''' —
6
7A logged-in Trader inspects the current state of their account. The portfolio view
8lists every cryptocurrency the Trader holds with the quantity (also split into the
9part reserved by open sell orders and the part that is free to sell), the average
10buy price, the current market price, the market value and the unrealised
11profit/loss, followed by a totals row and a cash summary (cash available, portfolio
12value, net worth). The transaction history lists the Trader's last 20 ledger
13entries — deposits, buys and sells — newest first. Both are read-only: nothing in
14the database is changed.
15
16Original use-case description (P3): [wiki:UseCase0006].
17Implementation: `server/portfolio.go`, function `ShowPortfolio`, and
18`server/account.go`, function `ShowTransactions` (the code is shown at the end of
19this page).
20
21Precondition: the Trader is logged in ([wiki:UseCase0002Implementation UseCase0002]).
22The run below is alice's, after she bought 0.01 BTC
23([wiki:UseCase0004Implementation UseCase0004]) and sold 0.2 ETH
24([wiki:UseCase0005Implementation UseCase0005]) on top of the seed data (0.5 ETH,
258250.00 USD cash).
26
27== Scenario ==
28
29=== Portfolio ===
30
31 1. '''Trader''' chooses `[6] View portfolio` in the authenticated menu (types `6`).
32 The menu is the one shown in [wiki:UseCase0002Implementation UseCase0002], step 7.
33 2. '''System''' queries the `v_portfolio` view (`$1` = user id):
34
35{{{
36SELECT symbol,
37 quantity,
38 COALESCE(reserved_quantity, 0),
39 COALESCE(available_quantity, quantity),
40 COALESCE(avg_price, 0),
41 COALESCE(current_price, 0),
42 COALESCE(market_value, 0),
43 COALESCE(unrealized_pnl, 0)
44 FROM v_portfolio
45 WHERE user_id = $1 AND quantity > 0
46 ORDER BY symbol
47}}}
48
49 3. '''System''' displays the rows and a `TOTAL` row (sums of the value and P/L columns,
50 computed in Go), then reads the cash balance for the summary (`$1` = user id):
51
52{{{
53SELECT available_balance, invested_balance FROM users WHERE id = $1
54}}}
55
56and prints `Cash available` (= `available_balance`), `Portfolio value`
57(= the total market value) and `Net worth` (= their sum).
58
59The screenshot shows the result of steps 2–3. The table is wider than the
60terminal window, so each long line wraps and the header row has scrolled out of
61the top of the window; the complete output of this run is reproduced below it.
62
63[[Image(uc0006_portfolio.png)]]
64
65{{{
66 Symbol Quantity Reserved Available Avg buy Current Value Unrealised P/L
67 ------------------------------------------------------------------------------------------------------------------
68 BTC 0.0100 0.0000 0.0100 67140.000000 67140.000000 671.4000 +0.0000
69 ETH 0.3000 0.0000 0.3000 3500.000000 3520.000000 1056.0000 +6.0000
70 ------------------------------------------------------------------------------------------------------------------
71 TOTAL 1727.4000 +6.0000
72
73 Cash available : 8282.6000 USD
74 Portfolio value: 1727.4000 USD
75 Net worth : 10010.0000 USD
76}}}
77
78Checking the figures: ETH is 0.5 − 0.2 = 0.3 at an average buy price of 3500 and
79a current price of 3520, so value 1056.00 and P/L 0.3 × 20 = +6.00; BTC was just
80bought at the current price 67140, so its P/L is 0. Cash is
818250.00 − 671.40 (buy) + 704.00 (sell) = 8282.60. `Reserved` is 0.0000 for both
82because the market orders executed immediately — a quantity is reserved only
83while a sell order is still open.
84
85=== Transaction history ===
86
87 1. '''Trader''' chooses `[7] View transaction history` in the authenticated menu
88 (types `7`).
89 2. '''System''' queries the last 20 ledger entries of the Trader (`$1` = user id) and
90 prints them, newest first (the time is shown as the first 19 characters of
91 `created_at`):
92
93{{{
94SELECT created_at, type, amount, currency, COALESCE(description, '')
95 FROM transactions
96 WHERE user_id = $1
97 ORDER BY created_at DESC
98 LIMIT 20
99}}}
100
101The screenshot shows steps 1–2: the choice `7` and the four ledger rows of this
102run — the sell of 0.2 ETH (+704.0000 USD), the buy of 0.01 BTC (−671.4000 USD),
103and the two seed rows, the initial deposit of 10000.0000 USD and the seed buy of
1040.5 ETH (−1750.0000 USD). Buys are stored with a negative amount, deposits and
105sells with a positive one. The two seed rows were inserted by `data_load.sql` in
106one statement and have the same `created_at`, so their relative order is not
107determined by the `ORDER BY`.
108
109[[Image(uc0006_history.png)]]
110
111All statements run on the `project` schema (the connection sets
112`search_path=project,public`).
113
114=== Reference — how v_portfolio is defined ===
115
116From `server/db/schema_creation.sql`:
117
118{{{
119CREATE OR REPLACE VIEW project.v_portfolio AS
120SELECT h.user_id,
121 c.symbol,
122 h.quantity,
123 h.reserved_quantity,
124 (h.quantity - h.reserved_quantity) AS available_quantity,
125 h.avg_price,
126 lp.price AS current_price,
127 (h.quantity * lp.price) AS market_value,
128 (h.quantity * (lp.price - h.avg_price)) AS unrealized_pnl
129FROM project.holdings h
130JOIN project.crypto c ON c.id = h.crypto_id
131LEFT JOIN project.markets m ON m.crypto_id = c.id AND m.quote_currency = 'USD'
132LEFT JOIN project.v_latest_prices lp ON lp.market_id = m.id;
133}}}
134
135== How to reproduce ==
136
137{{{
138./eduberza -init
139./eduberza
140# [2] Login: alice / test123
141# [4] buy 0.01 BTC, [5] sell 0.2 ETH (UseCase0004 / UseCase0005)
142# [6] View portfolio
143# [7] View transaction history
144}}}
145
146Both screenshots come from one real run (portfolio and history taken right after
147the buy and sell runs).
148
149== Source code ==
150
151`server/portfolio.go` — `ShowPortfolio`:
152
153{{{
154// ShowPortfolio - UC0006
155// Uses the v_portfolio view to list holdings with current market value and P&L.
156func ShowPortfolio(s *Session) {
157 rows, err := db.DB.Query(
158 `SELECT symbol,
159 quantity,
160 COALESCE(reserved_quantity, 0),
161 COALESCE(available_quantity, quantity),
162 COALESCE(avg_price, 0),
163 COALESCE(current_price, 0),
164 COALESCE(market_value, 0),
165 COALESCE(unrealized_pnl, 0)
166 FROM v_portfolio
167 WHERE user_id = $1 AND quantity > 0
168 ORDER BY symbol`,
169 s.UserID,
170 )
171 if err != nil {
172 fmt.Println("Error:", err)
173 return
174 }
175 defer rows.Close()
176
177 header := fmt.Sprintf(" %-8s %12s %12s %12s %14s %14s %14s %14s",
178 "Symbol", "Quantity", "Reserved", "Available", "Avg buy", "Current", "Value", "Unrealised P/L")
179 fmt.Println()
180 fmt.Println(header)
181 fmt.Println(" " + strings.Repeat("-", len(header)-2))
182
183 var totalValue, totalPnL float64
184 empty := true
185 for rows.Next() {
186 var sym string
187 var qty, reserved, avail, avg, cur, val, pnl float64
188 if err := rows.Scan(&sym, &qty, &reserved, &avail, &avg, &cur, &val, &pnl); err != nil {
189 fmt.Println("scan error:", err)
190 return
191 }
192 fmt.Printf(" %-8s %12.4f %12.4f %12.4f %14.6f %14.6f %14.4f %+14.4f\n",
193 sym, qty, reserved, avail, avg, cur, val, pnl)
194 totalValue += val
195 totalPnL += pnl
196 empty = false
197 }
198 if empty {
199 fmt.Println(" (no holdings yet)")
200 return
201 }
202 fmt.Println(" " + strings.Repeat("-", len(header)-2))
203 fmt.Printf(" %-8s %12s %12s %12s %14s %14s %14.4f %+14.4f\n",
204 "TOTAL", "", "", "", "", "", totalValue, totalPnL)
205
206 // cash summary
207 var avail, invested float64
208 _ = db.DB.QueryRow(
209 `SELECT available_balance, invested_balance FROM users WHERE id = $1`,
210 s.UserID,
211 ).Scan(&avail, &invested)
212 fmt.Printf("\n Cash available : %.4f USD\n", avail)
213 fmt.Printf(" Portfolio value: %.4f USD\n", totalValue)
214 fmt.Printf(" Net worth : %.4f USD\n", avail+totalValue)
215}
216}}}
217
218`server/account.go` — `ShowTransactions`:
219
220{{{
221// ShowTransactions lists the last 20 ledger entries for the user.
222func ShowTransactions(s *Session) {
223 rows, err := db.DB.Query(
224 `SELECT created_at, type, amount, currency, COALESCE(description, '')
225 FROM transactions
226 WHERE user_id = $1
227 ORDER BY created_at DESC
228 LIMIT 20`,
229 s.UserID,
230 )
231 if err != nil {
232 fmt.Println("Error:", err)
233 return
234 }
235 defer rows.Close()
236
237 fmt.Println()
238 fmt.Printf(" %-20s %-8s %12s %-3s %s\n", "When", "Type", "Amount", "Cur", "Description")
239 fmt.Println(" " + strings.Repeat("-", 70))
240 for rows.Next() {
241 var when, typ, cur, desc string
242 var amt float64
243 if err := rows.Scan(&when, &typ, &amt, &cur, &desc); err != nil {
244 fmt.Println("scan error:", err)
245 return
246 }
247 fmt.Printf(" %-20s %-8s %12.4f %-3s %s\n", when[:19], typ, amt, cur, desc)
248 }
249}
250}}}
Note: See TracBrowser for help on using the repository browser.