2026-04-01 19:17:09 -05:00
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" / >
< meta name = "viewport" content = "width=device-width,initial-scale=1" / >
< title > IB Dashboard< / title >
2026-04-01 21:44:21 -05:00
< style >
:root { --sort-row-height: 43px; --filter-row-height: 38px; }
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial; margin: 20px; }
input, button { padding: 6px 8px; margin-right: 6px }
.table-wrap {
position: relative;
max-height: calc(100vh - 140px);
overflow: auto;
border: 1px solid #ddd;
margin-top: 12px;
background: #fff;
}
table { border-collapse: separate; border-spacing: 0; width: 100%; margin-top: 0; background: #fff }
th, td {
padding: 8px;
text-align: left;
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
th:first-child, td:first-child { border-left: 1px solid #ddd }
.section-head { display: flex; align-items: center; gap: 10px }
.help-wrap { position: relative; display: inline-flex; align-items: center }
.help-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 999px;
background: #1f9d3a;
color: #fff;
font-size: 22px;
font-weight: 700;
line-height: 1;
cursor: help;
}
.help-pane {
display: none;
position: absolute;
top: 50%;
left: calc(100% + 12px);
right: auto;
transform: translateY(-50%);
width: 340px;
padding: 12px 14px;
border: 1px solid #cfd8cf;
background: #fbfffb;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
z-index: 5;
font-size: 13px;
line-height: 1.45;
}
.help-wrap:hover .help-pane { display: block }
.help-pane p { margin: 0 0 8px 0 }
.help-pane p:last-child { margin-bottom: 0 }
th {
background: #f4f4f4;
position: sticky;
top: 0;
z-index: 4;
cursor: pointer;
user-select: none;
white-space: nowrap;
}
thead tr.sort-row th {
top: 0;
height: var(--sort-row-height);
min-height: var(--sort-row-height);
box-sizing: border-box;
box-shadow: inset 0 -1px 0 #d9d9d9;
}
th:hover { background: #e9e9e9 }
th .sort-meta { color: #666; font-size: 12px; margin-left: 6px }
thead { position: relative; z-index: 2 }
thead tr.filter-row th {
top: var(--sort-row-height);
background: #fafafa;
cursor: default;
z-index: 3;
padding: 6px;
height: var(--filter-row-height);
min-height: var(--filter-row-height);
box-sizing: border-box;
box-shadow: inset 0 -1px 0 #d9d9d9;
}
thead tr.filter-row th:hover { background: #fafafa }
.filter-input {
width: 100%;
min-width: 82px;
margin: 0;
padding: 5px 6px;
box-sizing: border-box;
font-size: 12px;
border: 1px solid #cfd3d7;
background: #fff;
}
tbody tr:nth-child(odd) { background: #ffffff }
tbody tr:nth-child(even) { background: #f3f3f3 }
td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap }
td.icon-cell { text-align: center }
.ltcg-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 4px;
font-size: 14px;
font-weight: 700;
line-height: 1;
}
.ltcg-icon.good {
background: #e5f7e9;
color: #188038;
border: 1px solid #b7e0c1;
}
.ltcg-icon.bad {
background: #fdeaea;
color: #c5221f;
border: 1px solid #efb7b7;
}
< / style >
< / head >
< body >
< h1 > Interactive Brokers Dashboard< / h1 >
< section >
< div class = "section-head" >
< h2 > Open Lots< / h2 >
< div class = "help-wrap" >
< span class = "help-badge" > ?< / span >
< div class = "help-pane" >
< p > < strong > Sorting:< / strong > click a column header to sort ascending, click again for descending, click a third time to remove it. Hold < code > Shift< / code > while clicking to add second and third sort levels.< / p >
< p > < strong > Text filters:< / strong > use wildcards with < code > *< / code > and < code > ?< / code > . Example: < code > AM*< / code > or < code > *2027*< / code > .< / p >
< p > < strong > Numeric filters:< / strong > use values like < code > > 100< / code > , < code > < =0< / code > , < code > 50< / code > .< / p >
< p > < strong > Date filters:< / strong > use either wildcards like < code > 2025-07*< / code > or comparisons like < code > > 2025-01-01< / code > .< / p >
< / div >
< / div >
< / div >
< div class = "table-wrap" >
< table id = "positions-table" >
< thead >
< tr class = "sort-row" >
< th data-column = "security_type" > Type< / th >
< th data-column = "display_symbol" > Security< / th >
< th data-column = "underlying_symbol" > Underlying< / th >
< th data-column = "qty" > Qty< / th >
< th data-column = "avg_price" > Avg Price< / th >
< th data-column = "cost_basis" > Cost Basis< / th >
< th data-column = "market_price" > Market Price< / th >
< th data-column = "market_value" > Market Value< / th >
< th data-column = "gain_dollars" > Gain $< / th >
< th data-column = "gain_percent" > Gain %< / th >
< th data-column = "cagr_percent" > CAGR %< / th >
< th data-column = "days_since_buy" > Days Since Buy< / th >
< th data-column = "ltcg" > LTCG< / th >
< th data-column = "buy_time" > Buy Time< / th >
< / tr >
< tr class = "filter-row" >
< th >
< select class = "filter-input" data-column = "security_type" title = "Filter by security type." >
< option value = "" > All< / option >
< option value = "STK" > STK< / option >
< option value = "OPT" > OPT< / option >
< / select >
< / th >
< th > < input class = "filter-input" data-column = "display_symbol" placeholder = "AM*" title = "Text filter. Use * and ? wildcards." / > < / th >
< th > < input class = "filter-input" data-column = "underlying_symbol" placeholder = "A*" title = "Text filter. Use * and ? wildcards." / > < / th >
< th > < input class = "filter-input" data-column = "qty" placeholder = ">100" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th > < input class = "filter-input" data-column = "avg_price" placeholder = "<20" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th > < input class = "filter-input" data-column = "cost_basis" placeholder = ">1000" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th > < input class = "filter-input" data-column = "market_price" placeholder = "<10" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th > < input class = "filter-input" data-column = "market_value" placeholder = ">5000" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th > < input class = "filter-input" data-column = "gain_dollars" placeholder = ">0" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th > < input class = "filter-input" data-column = "gain_percent" placeholder = ">10" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th > < input class = "filter-input" data-column = "cagr_percent" placeholder = ">5" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th > < input class = "filter-input" data-column = "days_since_buy" placeholder = ">365" title = "Numeric filter. Examples: >100, <=0, 25" / > < / th >
< th >
< select class = "filter-input" data-column = "ltcg" title = "Filter by LTCG eligibility." >
< option value = "" > All< / option >
< option value = "X" > Long-term< / option >
< option value = "!X" > Short-term< / option >
< / select >
< / th >
< th > < input class = "filter-input" data-column = "buy_time" placeholder = ">2025-01-01" title = "Date filter. Use * and ? wildcards or comparisons like >2025-01-01." / > < / th >
< / tr >
< / thead >
< tbody > < / tbody >
< / table >
< / div >
< / section >
< script src = "/static/app.js" > < / script >
< / body >
< / html >