ynab-portfolio-monitor/questrade/account.go

115 lines
3.4 KiB
Go
Raw Normal View History

2023-11-12 19:04:22 +00:00
package questrade
import (
"fmt"
"net/url"
"strconv"
)
// Account represents an account associated with the user on behalf
// of which the API client is authorized.
//
// Ref: http://www.questrade.com/api/documentation/rest-operations/account-calls/accounts
type Account struct {
// Type of the account (e.g., "Cash", "Margin").
Type string `json:"type"`
// Eight-digit account number (e.g., "26598145")
// Stored as a string, it's used for making account-related API calls
Number string `json:"number"`
// Status of the account (e.g., Active).
Status string `json:"status"`
// Whether this is a primary account for the holder.
IsPrimary bool `json:"isPrimary"`
// Whether this account is one that gets billed for various expenses such as inactivity fees, market data, etc.
IsBilling bool `json:"isBilling"`
// Type of client holding the account (e.g., "Individual").
ClientAccountType string `json:"clientAccountType"`
}
// Balance belonging to an Account
//
// Ref: http://www.questrade.com/api/documentation/rest-operations/account-calls/accounts-id-balances
type Balance struct {
// Currency of the balance figure(e.g., "USD" or "CAD").
Currency string `json:"currency"`
// Balance amount.
Cash float32 `json:"cash"`
// Market value of all securities in the account in a given currency.
MarketValue float32 `json:"marketValue"`
// Equity as a difference between cash and marketValue properties.
TotalEquity float32 `json:"totalEquity"`
// Buying power for that particular currency side of the account.
BuyingPower float32 `json:"buyingPower"`
// Maintenance excess for that particular side of the account.
MaintenanceExcess float32 `json:"maintenanceExcess"`
// Whether real-time data was used to calculate the above values.
IsRealTime bool `json:"isRealTime"`
}
// AccountBalances represents per-currency and combined balances for a specified account.
//
// Ref: http://www.questrade.com/api/documentation/rest-operations/account-calls/accounts-id-balances
type AccountBalances struct {
PerCurrencyBalances []Balance `json:"perCurrencyBalances"`
CombinedBalances []Balance `json:"combinedBalances"`
SODPerCurrencyBalances []Balance `json:"sodPerCurrencyBalances"`
SODCombinedBalances []Balance `json:"sodCombinedBalances"`
}
// GetAccounts returns the logged-in User ID, and a list of accounts
// belonging to that user.
func (c *Client) GetAccounts() (int, []Account, error) {
list := struct {
UserID int `json:"userId"`
Accounts []Account `json:"accounts"`
}{}
err := c.get("v1/accounts", &list, url.Values{})
if err != nil {
return 0, []Account{}, err
}
return list.UserID, list.Accounts, nil
}
// GetBalances returns the balances for the account with the specified account number
func (c *Client) GetBalances(number string) (AccountBalances, error) {
bal := AccountBalances{}
err := c.get("v1/accounts/"+number+"/balances", &bal, url.Values{})
if err != nil {
return AccountBalances{}, err
}
return bal, nil
}
func (c *Client) GetQuestradeAccountBalance(accountID int) (int, error) {
balances, err := c.GetBalances(strconv.Itoa(accountID))
if err != nil {
return 0, fmt.Errorf("failed to get balances for account ID '%d': %v", accountID, err)
}
for _, balance := range balances.CombinedBalances {
if balance.Currency != "CAD" {
continue
}
return int(balance.TotalEquity) * 1000, nil
}
return 0, fmt.Errorf("could not find a CAD balance for this account in questade response")
}