zricethezav / go-tdameritrade Goto Github PK
View Code? Open in Web Editor NEWgo client for the tdameritrade api
License: MIT License
go client for the tdameritrade api
License: MIT License
It looks like all of these functions are not 100% implemented, is there a reason for that other than time/bandwidth? The response body gets read, which makes it unreadable by the caller, so the functions are affectively useless.
https://github.com/zricethezav/go-tdameritrade/blob/master/tdameritrade/accounts.go#L416-L487
You have is listed here:
Line 92 in 5bc7914
But literally no example of what format the date should be... (I have no clue what "short format" means either...)
Thanks for the fantastic library! I'm thinking about using it to add a TD integration to ticker
. I'm trying to get the webauth example working locally but keep running into this SSL issue:
I'm running the example with:
TDAMERITRADE_CLIENT_ID=<my_client_id> go run webauth.go
Are you doing SSL termination in front of your webauth? I tried switching the callback URL from https to http but it looks like the TD API forces it to https irrespective of your callback URL settings.
Everything works great if I toss in an ngrok secure tunnel (and adjust the callback URL accordingly), but would like to avoid making users set up ngrok if possible.
I'm integrating your library into my application and discovered that I can't access an individual candle from the PriceHistory API. I can only access the list of Candles
which references an anonymous struct. In my code it would be convenient if I could pass around the individual candle. I can make the PR if this is acceptable to you.
PriceHistory delivers volume and datetime as int64, rather than float64 and int respectively.
Volume shouldn't be a float because fractional shares aren't a thing on the exchange.
Datetime should be int64 because that is what is used for time.Unix.
The call to PriceHistory
fails with a 400 Bad Request because StartDate
and EndDate
options contain empty time.Time
which the API rejects.
Fixed by: #23
Example call.
ph, resp, err := client.PriceHistory.PriceHistory(ctx, symbol, &tdameritrade.PriceHistoryOptions{
PeriodType: "year",
Period: 20,
FrequencyType: "daily",
Frequency: 1,
})
Fix:
type PriceHistoryOptions struct {
...
EndDate time.Time `url:"endDate, omitempty"` <-- Include omitempty
StartDate time.Time `url:"startDate, omitempty"` <--
...
}
func main() {
c, ctx := NewClient()
q := url.Values{}
q.Set("symbol", "AAPL")
chains, _, err := c.Chains.GetChains(ctx, q)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%v\n", chains)
}
this results in:
2022/11/27 21:41:08 json: cannot unmarshal string into Go struct field ExpDateOption.putExpDateMap.volatility of type float64
Hello I was wondering how you normally go about testing your applications seeing as there does not seem to be a Paper Trading option for TD's api. Thanks
go-tdameritrade/tdameritrade/tdameritrade.go
Line 166 in d02f32a
I'm wrote some code to place an options order, using the Single Option example here. The example they give is this:
{
"complexOrderStrategyType": "NONE",
"orderType": "LIMIT",
"session": "NORMAL",
"price": "6.45",
"duration": "DAY",
"orderStrategyType": "SINGLE",
"orderLegCollection": [
{
"instruction": "BUY_TO_OPEN",
"quantity": 10,
"instrument": {
"symbol": "XYZ_032015C49",
"assetType": "OPTION"
}
}
]
}
It seem like the library's Instrument
struct only has Symbol
and Data
, not AssetType
. Has TDA changed their interface and this library is old? TIA!
The 0Auth token only lasts 30 minutes, but the refresh token lasts 90 days. I don't see any code to handle automatically refreshing the access token. How should I handle that?
Is there a way to get a refresh token through the code? I tried several times with their API and couldn't get a refresh token, kept getting a 400 error.
TIA!
Thanks very much for your work on this.
It's unclear to me how to format a trigger order with two child orders that are bound by OCO.
(Shown as the second-to-last example in https://developer.tdameritrade.com/content/place-order-samples)
In the example, they compose the OCO as two children of an order with a single attribute orderStrategyType
.
However, if I submit an instance of *tdameritrade.Order
with only the attribute OrderStrategyType: "OCO"
and two child orders, the api complains that I am missing attributes on the order.
I tested the example by marshaling a map to json and it did in fact work as expected. I think the fix should just be a matter of using the right type to wrap the two OCO child orders?
Hi,
I've used the code from the examples
, this is what I get when trying to go to http://localhost:8080/authenticate
:
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"github.com/zricethezav/go-tdameritrade"
"golang.org/x/oauth2"
)
type HTTPHeaderStore struct{}
func (s *HTTPHeaderStore) StoreToken(token *oauth2.Token, w http.ResponseWriter, req *http.Request) error {
// DO NOT DO THIS IN A PRODUCTION ENVIRONMENT!
// This is just an example.
// Used signed cookies like those provided by https://github.com/gorilla/securecookie
http.SetCookie(
w,
&http.Cookie{
Name: "refreshToken",
Value: token.RefreshToken,
Expires: token.Expiry,
},
)
http.SetCookie(
w,
&http.Cookie{
Name: "accessToken",
Value: token.AccessToken,
Expires: token.Expiry,
},
)
return nil
}
func (s HTTPHeaderStore) GetToken(req *http.Request) (*oauth2.Token, error) {
// DO NOT DO THIS IN A PRODUCTION ENVIRONMENT!
// This is just an example.
// Used signed cookies like those provided by https://github.com/gorilla/securecookie
refreshToken, err := req.Cookie("refreshToken")
if err != nil {
return nil, err
}
accessToken, err := req.Cookie("accessToken")
if err != nil {
return nil, err
}
return &oauth2.Token{
AccessToken: accessToken.Value,
RefreshToken: refreshToken.Value,
Expiry: refreshToken.Expires,
}, nil
}
func (s HTTPHeaderStore) StoreState(state string, w http.ResponseWriter, req *http.Request) error {
// DO NOT DO THIS IN A PRODUCTION ENVIRONMENT!
// This is just an example.
// Used signed cookies like those provided by https://github.com/gorilla/securecookie
http.SetCookie(
w,
&http.Cookie{
Name: "state",
Value: state,
},
)
return nil
}
func (s HTTPHeaderStore) GetState(req *http.Request) (string, error) {
// DO NOT DO THIS IN A PRODUCTION ENVIRONMENT!
// This is just an example.
// Used signed cookies like those provided by https://github.com/gorilla/securecookie
cookie, err := req.Cookie("state")
if err != nil {
return "", err
}
return cookie.Value, nil
}
type TDHandlers struct {
authenticator *tdameritrade.Authenticator
}
func (h *TDHandlers) Authenticate(w http.ResponseWriter, req *http.Request) {
redirectURL, err := h.authenticator.StartOAuth2Flow(w, req)
if err != nil {
w.Write([]byte(err.Error()))
w.WriteHeader(http.StatusInternalServerError)
return
}
http.Redirect(w, req, redirectURL, http.StatusTemporaryRedirect)
}
func (h *TDHandlers) Callback(w http.ResponseWriter, req *http.Request) {
ctx := context.Background()
_, err := h.authenticator.FinishOAuth2Flow(ctx, w, req)
if err != nil {
w.Write([]byte(err.Error()))
w.WriteHeader(http.StatusInternalServerError)
return
}
http.Redirect(w, req, "/quote?ticker=SPY", http.StatusTemporaryRedirect)
}
func (h *TDHandlers) Quote(w http.ResponseWriter, req *http.Request) {
ctx := context.Background()
client, err := h.authenticator.AuthenticatedClient(ctx, req)
if err != nil {
w.Write([]byte(err.Error()))
w.WriteHeader(http.StatusInternalServerError)
return
}
ticker, ok := req.URL.Query()["ticker"]
if !ok || len(ticker) == 0 {
w.Write([]byte("ticker is required"))
w.WriteHeader(http.StatusBadRequest)
return
}
quote, resp, err := client.Quotes.GetQuotes(ctx, ticker[0])
if err != nil {
w.Write([]byte(err.Error()))
w.WriteHeader(http.StatusInternalServerError)
return
}
body, err := json.Marshal(quote)
if err != nil {
w.Write([]byte(err.Error()))
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Write(body)
w.WriteHeader(resp.StatusCode)
}
func main() {
clientID := os.Getenv("TDAMERITRADE_CLIENT_ID")
if clientID == "" {
log.Fatal("Unauthorized: No client ID present")
}
authenticator := tdameritrade.NewAuthenticator(
&HTTPHeaderStore{},
oauth2.Config{
ClientID: clientID,
Endpoint: oauth2.Endpoint{
TokenURL: "https://api.tdameritrade.com/v1/oauth2/token",
AuthURL: "https://auth.tdameritrade.com/auth",
},
RedirectURL: "http://localhost:8080/callback",
},
)
handlers := &TDHandlers{authenticator: authenticator}
http.HandleFunc("/authenticate", handlers.Authenticate)
http.HandleFunc("/callback", handlers.Callback)
http.HandleFunc("/quote", handlers.Quote)
log.Fatal(http.ListenAndServe(":8080", nil))
}
I've configured my callback URL to be: http://localhost:8080/callback
(http, not https)
Is there something I else I would need to configure?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.