Merge branch 'market-api-calls' of jacob.windle/allyinvest into master

This commit is contained in:
Jacob Windle 2020-01-22 00:15:38 +00:00 committed by Gitea
commit 1fb0009ccb
8 changed files with 172 additions and 62 deletions

9
Gopkg.lock generated
View File

@ -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

View File

@ -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

View File

@ -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
### /accounts
Get all account information for a user.
## Running This Project
@ -17,3 +14,11 @@ For now, just build this directory and run the executable.
### Requirements
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.

View File

@ -8,7 +8,10 @@ import (
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"strconv"
"strings"
)
/* The trading endpoint for Ally */
@ -66,6 +69,19 @@ func (c *AllyApi) getAndRead(path string) []byte {
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 */
func (c *AllyApi) Accounts() []AccountSummary {
var resp AccountResponse
@ -79,20 +95,71 @@ func (c *AllyApi) AccountBalances() (balances []AccountBalance) {
return resp.AccountBalances
}
func (c *AllyApi) AccountDetail(accountId string) AccountDetailResponse {
var resp AccountDetailResponse
_ = xml.Unmarshal(c.getAndRead(fmt.Sprintf("accounts/%s", accountId)), &resp)
return resp
/**
Return an object representing account detail of a given string
*/
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
_ = xml.Unmarshal(c.getAndRead(fmt.Sprintf("accounts/%s/balances", accountId)), &resp)
return resp
/**
Return an object representing account balances of a given string ID
*/
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
_ = xml.Unmarshal(c.getAndRead(fmt.Sprintf("accounts/%s/holdings", accountId)), &resp)
return resp
/**
Return an object representing the account holdings of a given string.
*/
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
View 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
View 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=

View File

@ -1,16 +1,9 @@
package main
import (
"fmt"
"strconv"
)
func main() {
// Load our environment variables
var api AllyApi
api.Initialize()
acctId, _ := strconv.Atoi(api.Accounts()[0].Account)
fmt.Printf("AccountDetail: %s\n", api.AccountDetail(acctId).AccountHoldings.Holding[0].Displaydata.Change)
api.MarketQuotes("FEYE", "IBM")
}

View File

@ -2,6 +2,7 @@ package main
import (
"encoding/xml"
"time"
)
type Accountbalance struct {
@ -138,3 +139,74 @@ type AccountDetailHoldingsResponse struct {
XMLName xml.Name `xml:"response"`
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"`
}