ynab-portfolio-monitor/providers/questrade/account.go

112 lines
3.4 KiB
Go
Raw Permalink 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 {
2023-11-12 19:04:22 +00:00
// 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 {
2023-11-12 19:04:22 +00:00
// 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"`
2023-11-12 19:04:22 +00:00
}
// GetAccounts returns the logged-in User ID, and a list of accounts
// belonging to that user.
func (c *client) GetAccounts() (int, []account, error) {
2023-11-12 19:04:22 +00:00
list := struct {
UserID int `json:"userId"`
Accounts []account `json:"accounts"`
2023-11-12 19:04:22 +00:00
}{}
err := c.get("v1/accounts", &list, url.Values{})
if err != nil {
return 0, []account{}, err
2023-11-12 19:04:22 +00:00
}
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{}
2023-11-12 19:04:22 +00:00
err := c.get("v1/accounts/"+number+"/balances", &bal, url.Values{})
if err != nil {
return accountBalances{}, err
2023-11-12 19:04:22 +00:00
}
return bal, nil
}
func (c *client) GetQuestradeAccountBalance(accountID int) (int, error) {
2023-11-12 19:04:22 +00:00
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")
}