2020-01-22 00:21:03 +00:00
|
|
|
package allyinvest
|
2019-11-15 16:16:56 +00:00
|
|
|
|
|
|
|
import (
|
2019-11-15 16:32:00 +00:00
|
|
|
"encoding/xml"
|
2019-11-15 16:16:56 +00:00
|
|
|
"fmt"
|
|
|
|
"github.com/joho/godotenv"
|
|
|
|
"github.com/mrjones/oauth"
|
2019-11-15 16:32:00 +00:00
|
|
|
"io/ioutil"
|
2019-11-26 20:35:50 +00:00
|
|
|
"log"
|
2019-11-15 16:16:56 +00:00
|
|
|
"net/http"
|
2020-01-20 01:28:58 +00:00
|
|
|
"net/url"
|
2019-11-15 16:16:56 +00:00
|
|
|
"os"
|
2020-01-22 00:11:29 +00:00
|
|
|
"strconv"
|
2020-01-20 01:28:58 +00:00
|
|
|
"strings"
|
2019-11-15 16:16:56 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
/* The trading endpoint for Ally */
|
|
|
|
const endpoint string = "https://api.tradeking.com/v1/"
|
|
|
|
|
|
|
|
type AllyApi struct {
|
|
|
|
consumer *oauth.Consumer
|
|
|
|
Client *http.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *AllyApi) Initialize() {
|
|
|
|
err := godotenv.Load()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up our new oauth consumer
|
|
|
|
c.consumer = oauth.NewConsumer(
|
|
|
|
os.Getenv("CONSUMER_KEY"),
|
|
|
|
os.Getenv("CONSUMER_SECRET"),
|
|
|
|
oauth.ServiceProvider{
|
|
|
|
RequestTokenUrl: "https://developers.tradeking.com/oauth/request_token",
|
|
|
|
AuthorizeTokenUrl: "https://developers.tradeking.com/oauth/authorize",
|
|
|
|
AccessTokenUrl: "https://developers.tradeking.com/oauth/access_token",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
c.Client, err = c.consumer.MakeHttpClient(
|
|
|
|
&oauth.AccessToken{Token: os.Getenv("ACCESS_TOKEN"), Secret: os.Getenv("ACCESS_SECRET")})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-15 16:32:00 +00:00
|
|
|
func (c *AllyApi) get(path string) (resp *http.Response, err error) {
|
2019-11-15 16:16:56 +00:00
|
|
|
resp, err = c.Client.Get(fmt.Sprintf("%s\\%s", endpoint, path))
|
2019-11-27 13:29:14 +00:00
|
|
|
return resp, err
|
2019-11-15 16:16:56 +00:00
|
|
|
}
|
2019-11-15 16:32:00 +00:00
|
|
|
|
2019-11-27 13:29:14 +00:00
|
|
|
//GetAndMarshal call GET on the path, and marshal the resopnse to interface
|
|
|
|
// type
|
|
|
|
func (c *AllyApi) getAndRead(path string) []byte {
|
|
|
|
resp, err := c.get(path)
|
2019-11-15 16:32:00 +00:00
|
|
|
if err != nil {
|
2019-11-27 13:29:14 +00:00
|
|
|
log.Fatal("Could not make request")
|
2019-11-15 16:32:00 +00:00
|
|
|
}
|
|
|
|
|
2019-11-27 13:29:14 +00:00
|
|
|
defer resp.Body.Close()
|
|
|
|
raw, err := ioutil.ReadAll(resp.Body)
|
2019-11-15 16:32:00 +00:00
|
|
|
if err != nil {
|
2019-11-27 13:29:14 +00:00
|
|
|
log.Fatal("Could not read body!")
|
|
|
|
return nil
|
2019-11-15 16:32:00 +00:00
|
|
|
}
|
2019-11-27 13:29:14 +00:00
|
|
|
return raw
|
|
|
|
}
|
2019-11-15 16:32:00 +00:00
|
|
|
|
2020-01-20 01:28:58 +00:00
|
|
|
/**
|
|
|
|
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()))
|
|
|
|
}
|
|
|
|
|
2019-11-27 13:29:14 +00:00
|
|
|
/* The /accounts endpoint of Ally */
|
|
|
|
func (c *AllyApi) Accounts() []AccountSummary {
|
2019-11-15 18:01:19 +00:00
|
|
|
var resp AccountResponse
|
2019-11-27 13:29:14 +00:00
|
|
|
_ = xml.Unmarshal(c.getAndRead("accounts"), &resp)
|
2019-11-15 18:01:19 +00:00
|
|
|
return resp.Accounts.Accountsummary
|
2019-11-15 16:32:00 +00:00
|
|
|
}
|
2019-11-26 20:35:50 +00:00
|
|
|
|
|
|
|
func (c *AllyApi) AccountBalances() (balances []AccountBalance) {
|
|
|
|
var resp AccountBalanceResponse
|
2019-11-27 13:29:14 +00:00
|
|
|
_ = xml.Unmarshal(c.getAndRead("accounts/balances"), &resp)
|
|
|
|
return resp.AccountBalances
|
|
|
|
}
|
2019-11-26 20:35:50 +00:00
|
|
|
|
2020-01-20 00:35:36 +00:00
|
|
|
/**
|
|
|
|
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
|
2019-11-27 13:39:26 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 00:35:36 +00:00
|
|
|
/**
|
|
|
|
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
|
2019-11-27 13:39:26 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 00:35:36 +00:00
|
|
|
/**
|
|
|
|
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
|
2019-11-26 20:35:50 +00:00
|
|
|
}
|
2020-01-20 00:21:32 +00:00
|
|
|
|
2020-01-20 00:35:36 +00:00
|
|
|
/**
|
|
|
|
Return an object representing the market clock response.
|
|
|
|
*/
|
|
|
|
func (c *AllyApi) MarketClock() (resp MarketClockResponse) {
|
|
|
|
c.marshalInterfaceResponse("market/clock", &resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-01-20 19:30:12 +00:00
|
|
|
/**
|
|
|
|
Given a list of symbols, return an result struct of quote history
|
|
|
|
*/
|
|
|
|
func (c *AllyApi) MarketQuotes(symbols ...string) (resp MarketQuotesResponse) {
|
2020-01-20 01:28:58 +00:00
|
|
|
v := url.Values{
|
2020-01-20 19:19:00 +00:00
|
|
|
"symbols": symbols,
|
2020-01-20 01:28:58 +00:00
|
|
|
}
|
|
|
|
fmt.Printf("%s\n", c.getWithParameters("market/ext/quotes", v))
|
2020-01-20 19:30:12 +00:00
|
|
|
c.marshalWithQuery("market/ext/quotes", v, &resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-01-22 00:11:29 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-01-22 00:14:37 +00:00
|
|
|
func (c *AllyApi) MarketNewsGet(id string) (resp MarketNewsGetResponse) {
|
|
|
|
c.marshalInterfaceResponse("market/news/#{id}", &resp)
|
|
|
|
return
|
2020-01-22 00:11:29 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 19:30:12 +00:00
|
|
|
func (c *AllyApi) marshalWithQuery(p string, v url.Values, i interface{}) {
|
|
|
|
resp := c.getWithParameters(p, v)
|
|
|
|
_ = xml.Unmarshal(resp, &i)
|
2020-01-20 01:28:58 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 00:35:36 +00:00
|
|
|
// Why does this work?? wtf??
|
|
|
|
func (c *AllyApi) marshalInterfaceResponse(p string, i interface{}) {
|
|
|
|
resp := c.getAndRead(p)
|
|
|
|
_ = xml.Unmarshal(resp, &i)
|
2020-01-20 00:21:32 +00:00
|
|
|
}
|