Last active
January 23, 2026 16:00
-
-
Save Niek/5e1f9101221fd93175e8df22107d248c to your computer and use it in GitHub Desktop.
Interactive Brokers tracker report
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Configuration | |
| # Run IBeam (https://github.com/Voyz/ibeam) on the same host to keep the Client Portal Gateway logged in | |
| API_BASE="https://localhost:5000/v1/api" | |
| CURL_OPTS="-sk -X GET" | |
| POST_OPTS="-sk -X POST" | |
| # Set locale to ensure printf uses commas for thousands separators | |
| export LC_NUMERIC=en_US.UTF-8 | |
| # Colors for UI | |
| GREEN='\033[0;32m' | |
| RED='\033[0;31m' | |
| BLUE='\033[0;34m' | |
| YELLOW='\033[1;33m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' # No Color | |
| BOLD='\033[1m' | |
| DIM='\033[2m' | |
| echo -e "${BOLD}${CYAN}IBKR Portfolio Status Report${NC}" | |
| echo -e "${BLUE}========================================================================================================${NC}" | |
| # 1. Fetch all accounts | |
| ACCOUNTS_JSON=$(curl $CURL_OPTS "$API_BASE/portfolio/accounts") | |
| if [[ -z "$ACCOUNTS_JSON" || "$ACCOUNTS_JSON" == "null" ]]; then | |
| echo -e "${RED}Error: Could not fetch accounts. Is the Client Portal Gateway running?${NC}" | |
| exit 1 | |
| fi | |
| # Parse account IDs into an array | |
| ACCOUNT_IDS=$(echo "$ACCOUNTS_JSON" | jq -r '.[].accountId') | |
| for ACCT in $ACCOUNT_IDS; do | |
| # Get Account Metadata (Title) | |
| ACCT_TITLE=$(echo "$ACCOUNTS_JSON" | jq -r ".[] | select(.accountId==\"$ACCT\") | .accountTitle") | |
| echo -e "\n${YELLOW}Account: ${BOLD}$ACCT ($ACCT_TITLE)${NC}" | |
| # 2. Get Net Liquidation Value | |
| SUMMARY=$(curl $CURL_OPTS "$API_BASE/portfolio/$ACCT/summary") | |
| NET_LIQ=$(echo "$SUMMARY" | jq -r '.netliquidation.amount // "0"') | |
| CURRENCY=$(echo "$SUMMARY" | jq -r '.netliquidation.currency // ""') | |
| # Format Net Liquidation as a whole number with thousand separators | |
| FORMATTED_LIQ=$(printf "%'d" "${NET_LIQ%.*}" 2>/dev/null || echo "$NET_LIQ") | |
| echo -e "${BOLD}Net Liquidation:${NC} ${GREEN}$FORMATTED_LIQ $CURRENCY${NC}" | |
| echo -e "${BLUE}--------------------------------------------------------------------------------------------------------${NC}" | |
| # 3. Get and Display Positions | |
| printf "${BOLD}%-20s %12s %20s %16s %16s %12s${NC}\n" "Symbol" "Pos" "Value" "Realized" "Unrealized" "Perf %" | |
| POSITIONS=$(curl $CURL_OPTS "$API_BASE/portfolio/$ACCT/positions") | |
| # Iterate through positions | |
| echo "$POSITIONS" | jq -r '.[] | | |
| .contractDesc as $desc | | |
| .position as $pos | | |
| .mktValue as $val | | |
| .currency as $cur | | |
| .realizedPnl as $rpnl | | |
| .unrealizedPnl as $upnl | | |
| (if ($val - $upnl) != 0 then ($upnl / ($val - $upnl)) * 100 else 0 end) as $perc | | |
| "\($desc)|\($pos)|\($val)|\($cur)|\($rpnl)|\($upnl)|\($perc)"' | while IFS='|' read -r DESC POS VAL CUR RPNL UPNL PERC; do | |
| # Determine color for PnL | |
| COLOR=$NC | |
| if (( $(echo "$UPNL > 0" | bc -l) )); then COLOR=$GREEN; fi | |
| if (( $(echo "$UPNL < 0" | bc -l) )); then COLOR=$RED; fi | |
| # Format output | |
| printf "%-20s %'12.2f %'16.2f %-3s ${COLOR}%'16.2f %'16.2f %11.2f%%${NC}\n" \ | |
| "$DESC" "$POS" "$VAL" "$CUR" "$RPNL" "$UPNL" "$PERC" | |
| done | |
| # 4. Performance History (Last 7 Days) | |
| echo -e "\n${BOLD}Performance History (Last 7 Days):${NC}" | |
| PERF_DATA=$(curl $POST_OPTS "$API_BASE/pa/performance" -d "{\"acctIds\": [\"$ACCT\"],\"period\":\"7D\"}" -H 'Content-Type: application/json') | |
| if [[ ! -z "$PERF_DATA" && "$PERF_DATA" != "null" ]]; then | |
| printf "%-12s %18s %12s\n" "Date" "NAV ($CURRENCY)" "Return %" | |
| echo -e "${BLUE}---------------------------------------------${NC}" | |
| # Extract Start NAV baseline | |
| START_DATE=$(echo "$PERF_DATA" | jq -r '.nav.data[0].startNAV.date') | |
| START_VAL=$(echo "$PERF_DATA" | jq -r '.nav.data[0].startNAV.val') | |
| if [[ "$START_DATE" != "null" ]]; then | |
| F_START_DATE="${START_DATE:0:4}-${START_DATE:4:2}-${START_DATE:6:2}" | |
| printf "%-12s %'18.2f %12s ${DIM}(Start)${NC}\n" "$F_START_DATE" "$START_VAL" "---" | |
| fi | |
| # Use jq to zip dates, returns, and navs into a processable format | |
| echo "$PERF_DATA" | jq -r ' | |
| .nav.dates as $dates | | |
| .cps.data[0].returns as $rets | | |
| .nav.data[0].navs as $navs | | |
| range(0; $dates | length) | | |
| "\($dates[.])|\($navs[.])|\($rets[.])" | |
| ' | while IFS='|' read -r P_DATE P_NAV P_RET; do | |
| # Format date from YYYYMMDD to YYYY-MM-DD | |
| F_DATE="${P_DATE:0:4}-${P_DATE:4:2}-${P_DATE:6:2}" | |
| # Format return to percentage | |
| P_RET_PERC=$(echo "$P_RET * 100" | bc -l) | |
| # Color logic for performance | |
| P_COLOR=$NC | |
| if (( $(echo "$P_RET > 0" | bc -l) )); then P_COLOR=$GREEN; fi | |
| if (( $(echo "$P_RET < 0" | bc -l) )); then P_COLOR=$RED; fi | |
| printf "%-12s %'18.2f ${P_COLOR}%11.2f%%${NC}\n" "$F_DATE" "$P_NAV" "$P_RET_PERC" | |
| done | |
| else | |
| echo -e "${RED}No performance data available.${NC}" | |
| fi | |
| done | |
| echo -e "\n${BLUE}========================================================================================================${NC}" | |
| echo -e "${CYAN}Report Complete.${NC}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment