market-api-calls #2
9
Gopkg.lock
generated
9
Gopkg.lock
generated
@ -1,9 +0,0 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
|
||||||
|
|
||||||
|
|
||||||
[solve-meta]
|
|
||||||
analyzer-name = "dep"
|
|
||||||
analyzer-version = 1
|
|
||||||
input-imports = []
|
|
||||||
solver-name = "gps-cdcl"
|
|
||||||
solver-version = 1
|
|
30
Gopkg.toml
30
Gopkg.toml
@ -1,30 +0,0 @@
|
|||||||
# Gopkg.toml example
|
|
||||||
#
|
|
||||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
|
||||||
# for detailed Gopkg.toml documentation.
|
|
||||||
#
|
|
||||||
# required = ["github.com/user/thing/cmd/thing"]
|
|
||||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project"
|
|
||||||
# version = "1.0.0"
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project2"
|
|
||||||
# branch = "dev"
|
|
||||||
# source = "github.com/myfork/project2"
|
|
||||||
#
|
|
||||||
# [[override]]
|
|
||||||
# name = "github.com/x/y"
|
|
||||||
# version = "2.4.0"
|
|
||||||
#
|
|
||||||
# [prune]
|
|
||||||
# non-go = false
|
|
||||||
# go-tests = true
|
|
||||||
# unused-packages = true
|
|
||||||
|
|
||||||
|
|
||||||
[prune]
|
|
||||||
go-tests = true
|
|
||||||
unused-packages = true
|
|
11
README.md
11
README.md
@ -6,9 +6,6 @@ This project is intended to be an API wrapper for the Ally Invest API written in
|
|||||||
|
|
||||||
The different endpoints that are currently covered by this API
|
The different endpoints that are currently covered by this API
|
||||||
|
|
||||||
### /accounts
|
|
||||||
|
|
||||||
Get all account information for a user.
|
|
||||||
|
|
||||||
## Running This Project
|
## Running This Project
|
||||||
|
|
||||||
@ -17,3 +14,11 @@ For now, just build this directory and run the executable.
|
|||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
You must within the .env file define your Ally Invest credentials
|
You must within the .env file define your Ally Invest credentials
|
||||||
|
|
||||||
|
## Vision
|
||||||
|
|
||||||
|
My vision is to build out an automated suite of tools for managing my portfolio in Ally Invest. I hope to have a software platform
|
||||||
|
where I can backtest any strategies I come
|
||||||
|
up with and an informational dashboard I can use to track the different symbols and quotes
|
||||||
|
which i care about. I would also love to make this software open source so that anyone can
|
||||||
|
use it.
|
||||||
|
91
client.go
91
client.go
@ -8,7 +8,10 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* The trading endpoint for Ally */
|
/* The trading endpoint for Ally */
|
||||||
@ -66,6 +69,19 @@ func (c *AllyApi) getAndRead(path string) []byte {
|
|||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Build up our query string, then send it along.
|
||||||
|
*/
|
||||||
|
func (c *AllyApi) getWithParameters(path string, values url.Values) []byte {
|
||||||
|
b := strings.Builder{}
|
||||||
|
for k, v := range values {
|
||||||
|
b.WriteString(fmt.Sprintf("?%s=%s", k, strings.Join(v, ",")))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Path built: %s%s\n", path, b.String())
|
||||||
|
return c.getAndRead(fmt.Sprintf("%s%s", path, b.String()))
|
||||||
|
}
|
||||||
|
|
||||||
/* The /accounts endpoint of Ally */
|
/* The /accounts endpoint of Ally */
|
||||||
func (c *AllyApi) Accounts() []AccountSummary {
|
func (c *AllyApi) Accounts() []AccountSummary {
|
||||||
var resp AccountResponse
|
var resp AccountResponse
|
||||||
@ -79,20 +95,71 @@ func (c *AllyApi) AccountBalances() (balances []AccountBalance) {
|
|||||||
return resp.AccountBalances
|
return resp.AccountBalances
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AllyApi) AccountDetail(accountId string) AccountDetailResponse {
|
/**
|
||||||
var resp AccountDetailResponse
|
Return an object representing account detail of a given string
|
||||||
_ = xml.Unmarshal(c.getAndRead(fmt.Sprintf("accounts/%s", accountId)), &resp)
|
*/
|
||||||
return resp
|
func (c *AllyApi) AccountDetail(accountId string) (resp AccountDetailResponse) {
|
||||||
|
c.marshalInterfaceResponse(fmt.Sprintf("accounts/%s", accountId), &resp)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AllyApi) AccountBalance(accountId string) AccountDetailBalanceResponse {
|
/**
|
||||||
var resp AccountDetailBalanceResponse
|
Return an object representing account balances of a given string ID
|
||||||
_ = xml.Unmarshal(c.getAndRead(fmt.Sprintf("accounts/%s/balances", accountId)), &resp)
|
*/
|
||||||
return resp
|
func (c *AllyApi) AccountBalance(accountId string) (resp AccountDetailBalanceResponse) {
|
||||||
|
c.marshalInterfaceResponse(fmt.Sprintf("accounts/%s/balances", accountId), &resp)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AllyApi) AccountHoldings(accountId string) AccountDetailHoldingsResponse {
|
/**
|
||||||
var resp AccountDetailHoldingsResponse
|
Return an object representing the account holdings of a given string.
|
||||||
_ = xml.Unmarshal(c.getAndRead(fmt.Sprintf("accounts/%s/holdings", accountId)), &resp)
|
*/
|
||||||
return resp
|
func (c *AllyApi) AccountHoldings(accountId string) (resp AccountDetailHoldingsResponse) {
|
||||||
|
c.marshalInterfaceResponse(fmt.Sprintf("accounts/%s/holdings", accountId), &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return an object representing the market clock response.
|
||||||
|
*/
|
||||||
|
func (c *AllyApi) MarketClock() (resp MarketClockResponse) {
|
||||||
|
c.marshalInterfaceResponse("market/clock", &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given a list of symbols, return an result struct of quote history
|
||||||
|
*/
|
||||||
|
func (c *AllyApi) MarketQuotes(symbols ...string) (resp MarketQuotesResponse) {
|
||||||
|
v := url.Values{
|
||||||
|
"symbols": symbols,
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\n", c.getWithParameters("market/ext/quotes", v))
|
||||||
|
c.marshalWithQuery("market/ext/quotes", v, &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AllyApi) MarketNewsSearch(maxhits int, symbols ...string) (resp MarketNewsResponse) {
|
||||||
|
v := url.Values{
|
||||||
|
"symbols": symbols,
|
||||||
|
"maxhits": []string{strconv.Itoa(maxhits)},
|
||||||
|
}
|
||||||
|
c.marshalWithQuery("market/news/search", v, &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AllyApi) MarketNewsGet(id string) (resp MarketNewsGetResponse) {
|
||||||
|
c.marshalInterfaceResponse("market/news/#{id}", &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AllyApi) marshalWithQuery(p string, v url.Values, i interface{}) {
|
||||||
|
resp := c.getWithParameters(p, v)
|
||||||
|
_ = xml.Unmarshal(resp, &i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Why does this work?? wtf??
|
||||||
|
func (c *AllyApi) marshalInterfaceResponse(p string, i interface{}) {
|
||||||
|
resp := c.getAndRead(p)
|
||||||
|
_ = xml.Unmarshal(resp, &i)
|
||||||
}
|
}
|
||||||
|
8
go.mod
Normal file
8
go.mod
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module github.com/jaketothepast/investmentally
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/joho/godotenv v1.3.0
|
||||||
|
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450
|
||||||
|
)
|
4
go.sum
Normal file
4
go.sum
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
|
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM=
|
||||||
|
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
|
9
main.go
9
main.go
@ -1,16 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Load our environment variables
|
// Load our environment variables
|
||||||
var api AllyApi
|
var api AllyApi
|
||||||
api.Initialize()
|
api.Initialize()
|
||||||
|
|
||||||
acctId, _ := strconv.Atoi(api.Accounts()[0].Account)
|
api.MarketQuotes("FEYE", "IBM")
|
||||||
|
|
||||||
fmt.Printf("AccountDetail: %s\n", api.AccountDetail(acctId).AccountHoldings.Holding[0].Displaydata.Change)
|
|
||||||
}
|
}
|
||||||
|
72
types.go
72
types.go
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Accountbalance struct {
|
type Accountbalance struct {
|
||||||
@ -138,3 +139,74 @@ type AccountDetailHoldingsResponse struct {
|
|||||||
XMLName xml.Name `xml:"response"`
|
XMLName xml.Name `xml:"response"`
|
||||||
AccountHoldings Accountholdings `xml:"accountholdings"`
|
AccountHoldings Accountholdings `xml:"accountholdings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MarketClockResponse struct {
|
||||||
|
XMLName xml.Name `xml:"response"`
|
||||||
|
Date string `xml:"date"`
|
||||||
|
CurrentStatus string `xml:"status>current"`
|
||||||
|
Message string `xml:"message"`
|
||||||
|
UnixTime time.Time `xml:"unixtime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MarketQuotesResponse struct {
|
||||||
|
XMLName xml.Name `xml:"response"`
|
||||||
|
Quotes []Quote `xml:"quotes"'`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
TODO: Finish this page https://www.ally.com/api/invest/documentation/market-ext-quotes-get-post/
|
||||||
|
*/
|
||||||
|
type Quote struct {
|
||||||
|
XMLName xml.Name `xml:"quote"`
|
||||||
|
AverageDailyPrice100 float64 `xml:"adp_100"`
|
||||||
|
AverageDailyPrice200 float64 `xml:"adp_200"`
|
||||||
|
AverageDailyPrice50 float64 `xml:"adp_50"`
|
||||||
|
AverageDailyVolume21 float64 `xml:"adv_21"`
|
||||||
|
AverageDailyVolume30 float64 `xml:"adv_30"`
|
||||||
|
AverageDailyVolume90 float64 `xml:"adv_90"`
|
||||||
|
AskPrice float64 `xml:"ask"`
|
||||||
|
AskTime time.Time `xml:"ask_time"`
|
||||||
|
AskSize int `xml:"asksz"`
|
||||||
|
Basis float64 `xml:"basis"`
|
||||||
|
Beta float64 `xml:"beta"`
|
||||||
|
Bid float64 `xml:"bid"`
|
||||||
|
BidTime time.Time `xml:"bid_time"`
|
||||||
|
BidSize int `xml:"bidsz"`
|
||||||
|
BidTick int `xml:"bidtick"`
|
||||||
|
Change float64 `xml:"chg"`
|
||||||
|
ChangeSign string `xml:"chg_sign"`
|
||||||
|
ChangeText string `xml:"chg_t"`
|
||||||
|
Close float64 `xml:"cl"`
|
||||||
|
Cusip float64 `xml:"cusip"`
|
||||||
|
Date string `xml:"date"`
|
||||||
|
Datetime string `xml:"datetime"`
|
||||||
|
Dividend float64 `xml:"div"`
|
||||||
|
DividendFrequency string `xml:"divfreq"`
|
||||||
|
DollarValue float64 `xml:"dollar_value"`
|
||||||
|
EarningsPerShare float64 `xml:"eps"`
|
||||||
|
CompanyName string `xml:"name"`
|
||||||
|
PercentChangeSinceClose float64 `xml:"pchg"`
|
||||||
|
PriorDayClose float64 `xml:"pcls"`
|
||||||
|
PriceEarningRatio float64 `xml:"pe"`
|
||||||
|
PriorDayHigh float64 `xml:"phi"`
|
||||||
|
PriorDayLow float64 `xml:"plo"`
|
||||||
|
PriorDayOpen float64 `xml:"popn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MarketNewsResponse struct {
|
||||||
|
XMLName xml.Name `xml:"response"`
|
||||||
|
Articles []Article `xml:"articles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Article struct {
|
||||||
|
XMLName xml.Name `xml:"article"`
|
||||||
|
Date string `xml:"date"`
|
||||||
|
Headline string `xml:"headline"`
|
||||||
|
Id string `xml:"id"`
|
||||||
|
Story string `xml:"story"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MarketNewsGetResponse struct {
|
||||||
|
XMLName xml.Name `xml:"response"`
|
||||||
|
Article Article `xml:"article"`
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user