{"id":38438508,"url":"https://github.com/laustindasauce/tasty-go","last_synced_at":"2026-01-17T04:32:26.791Z","repository":{"id":175979641,"uuid":"646556864","full_name":"laustindasauce/tasty-go","owner":"laustindasauce","description":"TastyTrade API wrapper for Go","archived":false,"fork":false,"pushed_at":"2025-09-16T12:21:13.000Z","size":249,"stargazers_count":11,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2026-01-15T07:32:16.156Z","etag":null,"topics":["api","api-wrapper","go","golang","stocks-api","tastytrade","tastyworks","trading"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/laustindasauce.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-05-28T19:17:16.000Z","updated_at":"2025-11-26T23:01:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"5f56e0ba-4d0f-4d48-9e33-30416e5cc8c4","html_url":"https://github.com/laustindasauce/tasty-go","commit_stats":null,"previous_names":["austinbspencer/tasty-go","laustindasauce/tasty-go"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/laustindasauce/tasty-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laustindasauce%2Ftasty-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laustindasauce%2Ftasty-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laustindasauce%2Ftasty-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laustindasauce%2Ftasty-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/laustindasauce","download_url":"https://codeload.github.com/laustindasauce/tasty-go/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laustindasauce%2Ftasty-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28495167,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T04:31:57.058Z","status":"ssl_error","status_checked_at":"2026-01-17T04:31:45.816Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-wrapper","go","golang","stocks-api","tastytrade","tastyworks","trading"],"created_at":"2026-01-17T04:32:26.712Z","updated_at":"2026-01-17T04:32:26.774Z","avatar_url":"https://github.com/laustindasauce.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tasty-go\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/laustindasauce/tasty-go.svg)](https://pkg.go.dev/github.com/laustindasauce/tasty-go)\n![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/laustindasauce/tasty-go)\n[![Go Report Card](https://goreportcard.com/badge/github.com/laustindasauce/tasty-go)](https://goreportcard.com/report/github.com/laustindasauce/tasty-go)\n[![codecov](https://codecov.io/github/laustindasauce/tasty-go/branch/develop/graph/badge.svg?token=86QF45I5RY)](https://codecov.io/github/laustindasauce/tasty-go)\n\nThis library provides `unofficial` Go clients for [tastytrade API](https://tastytrade.com).\n\n\u003e **Important:** TastyTrade has migrated to OAuth2 authentication. Session-based authentication is deprecated and will be discontinued on December 1st, 2024. Please migrate to OAuth2 authentication as shown in the examples below.\n\n\u003e You will need to opt into tastytrade's API [here](https://developer.tastytrade.com)\n\n## tastytrade\n\n[tastytrade](https://tastytrade.com/about-us/) pioneered options trading technology for retail traders.\n\n[Create your account](https://start.tastytrade.com/#/login?referralCode=MS53QAT6DS) if you don't already have one to begin trading with tastytrade.\n\n## Dependencies\n\nThere are very few direct dependencies for this lightweight API wrapper.\n\n- [decimal](https://github.com/shopspring/decimal)\n- [go-querystring](https://github.com/google/go-querystring)\n- [testify](https://github.com/stretchr/testify) `for testing`\n\n## Untested endpoints\n\n- Order reconfirm\n  - tastytrade API support has informed me that this endpoint is for Equity Offering orders only.\n\n## Installation\n\n```\ngo get github.com/austinbspencer.com/tasty-go\n```\n\n## OAuth2 Authentication Setup\n\nTastyTrade now uses OAuth2 for authentication. The **recommended approach** is to handle OAuth2 authorization in your own application and use this library with pre-existing tokens (\"bring your own tokens\").\n\n### 1. Register Your Application\n\nFirst, register your application with TastyTrade to get your OAuth2 credentials:\n\n- Visit [TastyTrade Developer Portal](https://developer.tastytrade.com)\n- Create a new application\n- Note your `Client ID` and `Client Secret`\n- Configure your redirect URI (e.g., `http://localhost:8080` for development)\n\n### 2. Recommended: \"Bring Your Own Tokens\" Usage\n\nThe primary usage pattern is to obtain OAuth2 tokens through your own authorization flow and initialize the client with those tokens:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\t// Create OAuth2 configuration\n\tconfig := tasty.NewProductionOAuth2Config(\n\t\tos.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tos.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\t\"http://localhost:8080/callback\",\n\t\t[]string{\"read\", \"trade\"},\n\t)\n\n\t// Option 1: Create client with individual token parameters\n\t// (tokens obtained from your external OAuth2 flow)\n\tclient, err := tasty.NewOAuth2ClientWithTokens(\n\t\tconfig,\n\t\t\"your-access-token-from-external-flow\",\n\t\t\"your-refresh-token-from-external-flow\",\n\t\t3600, // expires in 1 hour\n\t\tnil,  // use default HTTP client\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Option 2: Create client with TokenResponse object\n\ttokenResponse := \u0026tasty.TokenResponse{\n\t\tAccessToken:  \"your-access-token\",\n\t\tRefreshToken: \"your-refresh-token\",\n\t\tTokenType:    \"Bearer\",\n\t\tExpiresIn:    3600,\n\t\tScope:        \"read trade\",\n\t}\n\n\tclient2, err := tasty.NewOAuth2ClientWithTokenResponse(config, tokenResponse, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Option 3: Set tokens after client creation\n\tclient3, err := tasty.NewOAuth2Client(config, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\terr = client3.SetTokens(\"access-token\", \"refresh-token\", 3600)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Now use the client for API calls - tokens refresh automatically\n\taccounts, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Found %d accounts\\n\", len(accounts))\n\n\t// Check token status\n\tfmt.Printf(\"Is authenticated: %v\\n\", client.IsAuthenticated())\n\tfmt.Printf(\"Has valid token: %v\\n\", client.HasValidToken())\n\tfmt.Printf(\"Token expires in: %v\\n\", client.GetTimeUntilExpiry())\n}\n```\n\n### 3. Alternative: Built-in OAuth2 Flow (Optional)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\t// Configure OAuth2 for sandbox environment\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"http://localhost:8080\",\n\t\tScopes:       []string{\"read\", \"trade\"},\n\t}\n\n\t// Create OAuth2 client for sandbox\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Check if we already have valid tokens\n\tif client.HasValidToken() {\n\t\tfmt.Println(\"✓ Found existing valid tokens, skipping authentication...\")\n\n\t\t// Test API call with existing tokens\n\t\taccounts, _, err := client.GetMyAccounts()\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Existing tokens invalid, need to re-authenticate: %v\\n\", err)\n\t\t} else {\n\t\t\tfmt.Println(\"✓ Existing tokens work! Making API call...\")\n\t\t\tbalances, _, err := client.GetAccountBalances(accounts[0].AccountNumber)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t\tfmt.Printf(\"Cash balance: $%s\\n\", balances.CashBalance.String())\n\t\t\tfmt.Println(\"Authentication not needed - using saved tokens.\")\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Get authorization URL\n\tauthURL, err := client.GetAuthorizationURL()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Visit this URL to authorize: %s\\n\", authURL)\n\n\t// Start built-in redirect server\n\tserver, err := client.StartRedirectServer(8080)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer server.Shutdown(5 * time.Second)\n\n\t// Wait for authorization code\n\tcode, err := server.WaitForCode(5 * time.Minute)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Exchange code for tokens\n\ttokens, err := client.ExchangeCodeForTokens(code)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Access token obtained: %s...\\n\", tokens.AccessToken[:20])\n\n\t// Debug: Show where tokens are stored\n\thomeDir, _ := os.UserHomeDir()\n\ttokenPath := fmt.Sprintf(\"%s/.tasty-go/tokens.json\", homeDir)\n\tfmt.Printf(\"Tokens stored at: %s\\n\", tokenPath)\n\n\t// Check if token file exists\n\tif _, err := os.Stat(tokenPath); err == nil {\n\t\tfmt.Println(\"✓ Token file created successfully!\")\n\t} else {\n\t\tfmt.Printf(\"✗ Token file not found: %v\\n\", err)\n\t}\n\n\t// Now you can make API calls\n\taccounts, _, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tbalances, _, err := client.GetAccountBalances(accounts[0].AccountNumber)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Cash balance: $%s\\n\", balances.CashBalance.String())\n}\n```\n\n### 4. Production vs Sandbox\n\nFor production, use the production constructors:\n\n```go\n// Production with tokens\nconfig := tasty.NewProductionOAuth2Config(clientID, clientSecret, redirectURI, scopes)\nclient, err := tasty.NewOAuth2ClientWithTokens(config, accessToken, refreshToken, expiresIn, nil)\n\n// Sandbox with tokens\nconfig := tasty.NewSandboxOAuth2Config(clientID, clientSecret, redirectURI, scopes)\nclient, err := tasty.NewCertOAuth2ClientWithTokens(config, accessToken, refreshToken, expiresIn, nil)\n```\n\n### 5. Manual Token Exchange (Without Built-in Server)\n\nIf you prefer to handle the redirect yourself:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"https://yourapp.com/callback\",\n\t\tScopes:       []string{\"read\", \"trade\"},\n\t}\n\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Get authorization URL\n\tauthURL, err := client.GetAuthorizationURL()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Visit this URL: %s\\n\", authURL)\n\tfmt.Print(\"Enter the authorization code: \")\n\n\tvar code string\n\tfmt.Scanln(\u0026code)\n\n\t// Validate state parameter (important for security)\n\t// You should extract this from your callback URL\n\tvar receivedState string\n\tfmt.Print(\"Enter the state parameter: \")\n\tfmt.Scanln(\u0026receivedState)\n\n\tif err := client.ValidateState(receivedState); err != nil {\n\t\tlog.Fatal(\"Invalid state parameter:\", err)\n\t}\n\n\t// Exchange code for tokens\n\ttokens, err := client.ExchangeCodeForTokens(code)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Successfully authenticated! Token expires in %d seconds\\n\", tokens.ExpiresIn)\n\n\t// Make API calls - tokens are automatically refreshed as needed\n\taccounts, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Found %d accounts\\n\", len(accounts))\n}\n```\n\n### 6. Token Management and Status\n\nThe library provides comprehensive token management methods:\n\n```go\n// Check authentication status\nisAuth := client.IsAuthenticated()\nhasValidToken := client.HasValidToken()\nhasRefreshToken := client.HasRefreshToken()\nisExpired := client.IsTokenExpired()\n\n// Get token timing information\nexpiration, err := client.GetTokenExpiration()\ntimeUntilExpiry, err := client.GetTimeUntilExpiry()\n\n// Update tokens at runtime\nerr = client.SetTokens(\"new-access-token\", \"new-refresh-token\", 3600)\nerr = client.SetTokensFromResponse(newTokenResponse)\n\n// Clear authentication\nclient.ClearAuthentication()\n}\n```\n\n## Migration Guide: Session to OAuth2\n\nIf you're migrating from session-based authentication, here are the key changes:\n\n### Before (Session-based - Deprecated)\n\n```go\n// OLD - Session-based authentication (deprecated)\nclient, _ := tasty.NewCertClient(\u0026hClient)\ncreds := tasty.LoginInfo{\n    Login:    os.Getenv(\"username\"),\n    Password: os.Getenv(\"password\"),\n}\n_, err := client.CreateSession(creds, nil)\nif err != nil {\n    log.Fatal(err)\n}\n```\n\n### After (OAuth2)\n\n```go\n// NEW - OAuth2 authentication\nconfig := tasty.OAuth2Config{\n    ClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n    ClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n    RedirectURI:  \"http://localhost:8080\",\n}\nclient, err := tasty.NewCertOAuth2Client(config, \u0026hClient)\nif err != nil {\n    log.Fatal(err)\n}\n\n// Handle OAuth2 flow (see examples above)\n```\n\n### Key Differences\n\n1. **Authentication Method**: OAuth2 uses authorization codes and tokens instead of username/password\n2. **Client Creation**: Use `NewOAuth2Client()` or `NewCertOAuth2Client()` instead of `NewClient()` or `NewCertClient()`\n3. **Configuration**: OAuth2 requires client credentials from TastyTrade developer portal\n4. **Token Management**: Tokens are automatically refreshed - no manual session management needed\n5. **Security**: OAuth2 provides better security with PKCE and state parameters\n\n### Environment Variables\n\nUpdate your environment variables:\n\n```bash\n# Old session-based variables (remove these)\n# export certUsername=\"your_username\"\n# export certPassword=\"your_password\"\n\n# New OAuth2 variables\nexport TASTY_CLIENT_ID=\"your_client_id\"\nexport TASTY_CLIENT_SECRET=\"your_client_secret\"\n```\n\n### Common Migration Patterns\n\n#### Pattern 1: Simple API Calls\n\n**Before:**\n\n```go\nclient, _ := tasty.NewCertClient(\u0026hClient)\nclient.CreateSession(creds, nil)\naccounts, err := client.GetMyAccounts()\n```\n\n**After:**\n\n```go\nclient, _ := tasty.NewCertOAuth2Client(config, \u0026hClient)\n// Complete OAuth2 flow (see examples above)\naccounts, err := client.GetMyAccounts() // Same API call!\n```\n\n#### Pattern 2: Long-running Applications\n\n**Before:**\n\n```go\n// Session validation and refresh\n_, err := client.ValidateSession()\nif err != nil {\n    client.CreateSession(creds, nil)\n}\n```\n\n**After:**\n\n```go\n// OAuth2 tokens are automatically refreshed\n// No manual validation needed!\naccounts, err := client.GetMyAccounts()\n// Token refresh happens automatically if needed\n```\n\n## Basic API Usage\n\nCheck out tastytrade's [documentation](https://developer.tastytrade.com/basic-api-usage/)\n\n\u003cdetails\u003e\n\u003csummary\u003eOAuth2 Token Management\u003c/summary\u003e\n\n\u003e OAuth2 tokens are automatically managed - no manual validation needed!\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"http://localhost:8080\",\n\t}\n\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewCertOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Complete OAuth2 flow (see main examples above)\n\t// ... authorization flow code ...\n\n\t// Check authentication status\n\tif client.IsAuthenticated() {\n\t\tfmt.Println(\"Client is authenticated\")\n\t}\n\n\t// Get token information\n\ttokenManager := client.GetOAuth2Client().GetTokenManager()\n\tif !tokenManager.IsExpired() {\n\t\ttimeLeft := tokenManager.GetTimeUntilExpiry()\n\t\tfmt.Printf(\"Token expires in: %v\\n\", timeLeft)\n\t}\n\n\t// Tokens are automatically refreshed when making API calls\n\taccounts, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Printf(\"Successfully retrieved %d accounts\\n\", len(accounts))\n\n\t// Clear authentication when done (optional)\n\tclient.ClearAuthentication()\n\tfmt.Println(\"Authentication cleared\")\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eUser Management\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/basic-api-usage/#user-management)\n\n\u003e Password Reset (OAuth2)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"http://localhost:8080\",\n\t}\n\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewCertOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Complete OAuth2 authentication first\n\t// ... OAuth2 flow code (see main examples) ...\n\n\t// Get user information\n\tcustomer, err := client.GetMyCustomerInfo()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Request password reset email\n\terr = client.RequestPasswordResetEmail(customer.Email)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Println(\"Password reset email sent!\")\n\n\t// You will get an email with a reset link after the above request\n\t// This link will have a token in the query\n\t// https://developer.tastytrade.com/password/reset/?token=this-is-your-token\n\n\t// Attach the token along with new password in change request\n\t// Password change will invalidate all current OAuth2 tokens\n\terr = client.ChangePassword(tasty.PasswordReset{\n\t\tPassword:             \"newPassword\",\n\t\tPasswordConfirmation: \"newPassword\",\n\t\tResetPasswordToken:   \"this-is-your-token\",\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Println(\"Password changed successfully!\")\n\t// Note: You'll need to re-authenticate after password change\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCustomer Account Information\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/basic-api-usage/#customer-account-information)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"http://localhost:8080\",\n\t}\n\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewCertOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Complete OAuth2 authentication first\n\t// ... OAuth2 flow code (see main examples) ...\n\n\taccounts, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"I have access to %d accounts!\\n\", len(accounts))\n\n\t// Get detailed customer information\n\tcustomer, err := client.GetMyCustomerInfo()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Customer: %s %s\\n\", customer.FirstName, customer.LastName)\n\tfmt.Printf(\"Email: %s\\n\", customer.Email)\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAccount Positions\u003c/summary\u003e\n\nView all current account positions\n\n\u003e [docs](https://developer.tastytrade.com/basic-api-usage/#account-positions)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"http://localhost:8080\",\n\t}\n\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewCertOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Complete OAuth2 authentication first\n\t// ... OAuth2 flow code (see main examples) ...\n\n\t// Get accounts\n\taccounts, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\taccountNumber := accounts[0].AccountNumber\n\n\tpositions, err := client.GetAccountPositions(accountNumber, tasty.AccountPositionQuery{})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"You have %d positions on your account!\\n\", len(positions))\n\n\t// Display position details\n\tfor _, position := range positions {\n\t\tfmt.Printf(\"Symbol: %s, Quantity: %.2f, Market Value: $%.2f\\n\",\n\t\t\tposition.Symbol, position.Quantity, position.MarketValue)\n\t}\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAccount Balances\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/basic-api-usage/#account-balances)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"http://localhost:8080\",\n\t}\n\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewCertOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Complete OAuth2 authentication first\n\t// ... OAuth2 flow code (see main examples) ...\n\n\t// Get accounts\n\taccounts, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\taccountNumber := accounts[0].AccountNumber\n\n\tbalances, err := client.GetAccountBalances(accountNumber)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Account %s balances:\\n\", balances.AccountNumber)\n\tfmt.Printf(\"  Cash Balance: $%.2f\\n\", balances.CashBalance)\n\tfmt.Printf(\"  Net Liquidating Value: $%.2f\\n\", balances.NetLiquidatingValue)\n\tfmt.Printf(\"  Buying Power: $%.2f\\n\", balances.BuyingPower)\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWatchlists\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/basic-api-usage/#watchlists)\n\n\u003e Public Watchlists\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tcountsOnly := false\n\n\twatchlists, err := client.GetPublicWatchlists(countsOnly)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"There are %d public watchlists!\", len(watchlists))\n}\n\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eInstruments\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/basic-api-usage/#instruments) and [Open API Spec](https://developer.tastytrade.com/open-api-spec/instruments/)\n\n\u003e Equity Options\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\teoSymbol := tasty.EquityOptionsSymbology{\n\t\tSymbol:     \"AMD\",\n\t\tOptionType: tasty.Call,\n\t\tStrike:     180,\n\t\tExpiration: time.Date(2023, 06, 23, 0, 0, 0, 0, time.UTC),\n\t}\n\n\tequityOptions, err := client.GetEquityOptions(tasty.EquityOptionsQuery{Symbols: []string{eoSymbol.Build()}})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Your equity option with underlying symbol: %s\", equityOptions[0].UnderlyingSymbol)\n}\n\n```\n\n\u003e Future Options\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfuture := tasty.FutureSymbology{ProductCode: \"ES\", MonthCode: tasty.December, YearDigit: 9}\n\n\texpiry := time.Date(2019, 9, 27, 0, 0, 0, 0, time.Local)\n\tfcc := tasty.FutureOptionsSymbology{\n\t\tOptionContractCode: \"EW4U9\",\n\t\tFutureContractCode: future.Build(),\n\t\tOptionType:         tasty.Put,\n\t\tStrike:             2975,\n\t\tExpiration:         expiry,\n\t}\n\n\tquery := tasty.FutureOptionsQuery{\n\t\tSymbols: []string{fcc.Build()},\n\t}\n\n\tfutureOptions, err := client.GetFutureOptions(query)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Your future option with underlying symbol: %s\", futureOptions[0].UnderlyingSymbol)\n}\n\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTransaction History\u003c/summary\u003e\nAll transactions impacting an accounts balances or positions are available at this endpoint.\n\n\u003e [docs](https://developer.tastytrade.com/basic-api-usage/#transaction-history)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\ttransactions, _, err := client.GetAccountTransactions(accountNumber, tasty.TransactionsQuery{PerPage: 2})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tlatest := transactions[0]\n\n\tfmt.Printf(\"Your latest transaction was a %s of %s!\", latest.TransactionType, latest.UnderlyingSymbol)\n}\n\n```\n\n\u003e With Pagination Handling\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tquery := tasty.TransactionsQuery{PerPage: 25}\n\n\ttransactions, pagination, err := client.GetAccountTransactions(accountNumber, query)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfor pagination.PageOffset \u003c (pagination.TotalPages - 1) {\n\t\tquery.PageOffset += 1\n\t\tmoreTransactions, newPagination, err := client.GetAccountTransactions(accountNumber, query)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t\ttransactions = append(transactions, moreTransactions...)\n\t\tpagination = newPagination\n\t}\n\n\tlatest := transactions[0]\n\n\tfmt.Printf(\"Your latest transaction was a %s of %s!\", latest.TransactionType, latest.UnderlyingSymbol)\n}\n\n```\n\n\u003c/details\u003e\n\n## Order Management\n\nCheck out tastytrade's [documentation](https://developer.tastytrade.com/order-management/)\n\n\u003cdetails\u003e\n\u003csummary\u003eSearch Orders\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/order-management/#search-orders)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"http://localhost:8080\",\n\t}\n\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewCertOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Complete OAuth2 authentication first\n\t// ... OAuth2 flow code (see main examples) ...\n\n\t// Get accounts\n\taccounts, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\taccountNumber := accounts[0].AccountNumber\n\n\t// Query for narrowing search of orders\n\tquery := tasty.OrdersQuery{Status: []tasty.OrderStatus{tasty.Filled}}\n\n\torders, _, err := client.GetAccountOrders(accountNumber, query)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Your account has %d filled orders!\\n\", len(orders))\n\n\t// Display order details\n\tfor _, order := range orders {\n\t\tfmt.Printf(\"Order ID: %d, Status: %s, Symbol: %s\\n\",\n\t\t\torder.ID, order.Status, order.Legs[0].Symbol)\n\t}\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSearch Orders\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/order-management/#live-orders)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tliveOrders, err := client.GetAccountLiveOrders(accountNumber)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Your account has %d live orders!\", len(liveOrders))\n}\n\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eOrder Dry Run\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/order-management/#order-dry-run)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tsymbol := \"AMD\"\n\tquantity := 1\n\taction := tasty.BTO\n\n\torder := tasty.NewOrder{\n\t\tTimeInForce: tasty.Day,\n\t\tOrderType:   tasty.Market,\n\t\tLegs: []tasty.NewOrderLeg{\n\t\t\t{\n\t\t\t\tInstrumentType: tasty.EquityIT,\n\t\t\t\tSymbol:         symbol,\n\t\t\t\tQuantity:       quantity,\n\t\t\t\tAction:         action,\n\t\t\t},\n\t\t},\n\t}\n\n\tresp, orderErr, err := client.SubmitOrderDryRun(accountNumber, order)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t} else if orderErr != nil {\n\t\tlog.Fatal(orderErr)\n\t}\n\n\tfmt.Printf(\"Your dry run order status is %s!\", resp.Order.Status)\n}\n\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSubmit Order\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/order-management/#submit-order)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nfunc main() {\n\tconfig := tasty.OAuth2Config{\n\t\tClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n\t\tClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n\t\tRedirectURI:  \"http://localhost:8080\",\n\t}\n\n\thttpClient := \u0026http.Client{Timeout: 30 * time.Second}\n\tclient, err := tasty.NewCertOAuth2Client(config, httpClient)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Complete OAuth2 authentication first\n\t// ... OAuth2 flow code (see main examples) ...\n\n\t// Get accounts\n\taccounts, err := client.GetMyAccounts()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\taccountNumber := accounts[0].AccountNumber\n\n\tsymbol := \"RIVN\"\n\tquantity := 1\n\taction1 := tasty.BTC\n\n\t// Create option symbol for expiration date\n\texpirationDate := time.Now().AddDate(0, 1, 0) // 1 month from now\n\tsymbol1 := tasty.EquityOptionsSymbology{\n\t\tSymbol:     symbol,\n\t\tOptionType: tasty.Call,\n\t\tStrike:     15,\n\t\tExpiration: expirationDate,\n\t}\n\n\torder := tasty.NewOrder{\n\t\tTimeInForce: tasty.GTC,\n\t\tOrderType:   tasty.Limit,\n\t\tPriceEffect: tasty.Debit,\n\t\tPrice:       0.04,\n\t\tLegs: []tasty.NewOrderLeg{\n\t\t\t{\n\t\t\t\tInstrumentType: tasty.EquityOptionIT,\n\t\t\t\tSymbol:         symbol1.Build(),\n\t\t\t\tQuantity:       quantity,\n\t\t\t\tAction:         action1,\n\t\t\t},\n\t\t},\n\t\tRules: tasty.NewOrderRules{Conditions: []tasty.NewOrderCondition{\n\t\t\t{\n\t\t\t\tAction:         tasty.Route,\n\t\t\t\tSymbol:         symbol,\n\t\t\t\tInstrumentType: \"Equity\",\n\t\t\t\tIndicator:      tasty.Last,\n\t\t\t\tComparator:     tasty.LTE,\n\t\t\t\tThreshold:      0.01,\n\t\t\t},\n\t\t}},\n\t}\n\n\t// Submit order dry run first (recommended)\n\tdryRunResp, orderErr, err := client.SubmitOrderDryRun(accountNumber, order)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t} else if orderErr != nil {\n\t\tlog.Fatal(\"Dry run failed:\", orderErr)\n\t}\n\n\tfmt.Printf(\"Dry run successful! Estimated cost: $%.2f\\n\", dryRunResp.Order.Price)\n\n\t// Submit actual order\n\tresp, orderErr, err := client.SubmitOrder(accountNumber, order)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t} else if orderErr != nil {\n\t\tlog.Fatal(\"Order submission failed:\", orderErr)\n\t}\n\n\tfmt.Printf(\"Order submitted successfully!\\n\")\n\tfmt.Printf(\"Order ID: %d\\n\", resp.Order.ID)\n\tfmt.Printf(\"Status: %s\\n\", resp.Order.Status)\n\tfmt.Printf(\"Symbol: %s\\n\", resp.Order.Legs[0].Symbol)\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCancel Order\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/order-management/#cancel-order)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\nconst orderID = 123456\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tif _, err := client.CancelOrder(accountNumber, orderID); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Println(\"Order has been cancelled!\")\n}\n\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCancel Replace\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/order-management/#cancel-replace)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\torderID := 68678\n\n\torderECR := tasty.NewOrderECR{\n\t\tTimeInForce: tasty.Day,\n\t\tPrice:       185.45,\n\t\tOrderType:   tasty.Limit,\n\t\tPriceEffect: tasty.Debit,\n\t\tValueEffect: tasty.Debit,\n\t}\n\n\tnewOrder, err := client.ReplaceOrder(accountNumber, orderID, orderECR)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Your order was replaced with order with id: %d has a status of %s!\", newOrder.ID, newOrder.Status)\n}\n\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExamples\u003c/summary\u003e\n\n\u003e [docs](https://developer.tastytrade.com/order-management/#example-order-requests)\n\n\u003e Market Order\n\n```go\norder := tasty.NewOrder{\n\tTimeInForce: tasty.Day,\n\tOrderType:   tasty.Market,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.EquityIT,\n\t\t\tSymbol: \"AMD\",\n\t\t\tQuantity: 1,\n\t\t\tAction: tasty.BTO,\n\t\t},\n\t},\n}\n```\n\n\u003e GTC Closing Order\n\n```go\norder := tasty.NewOrder{\n\tTimeInForce: tasty.GTC,\n\tPrice: 150.25,\n\tPriceEffect: tasty.Credit,\n\tOrderType:   tasty.Limit,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.EquityIT,\n\t\t\tSymbol: \"AMD\",\n\t\t\tQuantity: 1,\n\t\t\tAction: tasty.STC,\n\t\t},\n\t},\n}\n```\n\n\u003e Short Futures Limit Order\n\n```go\norder := tasty.NewOrder{\n\tTimeInForce: tasty.Day,\n\tPrice: 90.03,\n\tPriceEffect: tasty.Credit,\n\tOrderType:   tasty.Limit,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.FutureIT,\n\t\t\tSymbol: \"/CLZ2\",\n\t\t\tQuantity: 1,\n\t\t\tAction: tasty.STO,\n\t\t},\n\t},\n}\n```\n\n\u003e Bear Call Spread\n\n```go\neoSymbolShort := tasty.EquityOptionsSymbology{\n\tSymbol:     \"AMD\",\n\tOptionType: tasty.Call,\n\tStrike:     185,\n\tExpiration: time.Date(2023, 06, 23, 0, 0, 0, 0, time.UTC),\n}\n\neoSymbolLong := tasty.EquityOptionsSymbology{\n\tSymbol:     \"AMD\",\n\tOptionType: tasty.Call,\n\tStrike:     187.5,\n\tExpiration: time.Date(2023, 06, 23, 0, 0, 0, 0, time.UTC),\n}\n\norder := tasty.NewOrder{\n\tTimeInForce: tasty.Day,\n\tPrice:       0.85,\n\tPriceEffect: tasty.Credit,\n\tOrderType:   tasty.Limit,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.EquityOptionIT,\n\t\t\tSymbol:         eoSymbolShort.Build(),\n\t\t\tQuantity:       1,\n\t\t\tAction:         tasty.STO,\n\t\t},\n\t\t{\n\t\t\tInstrumentType: tasty.EquityOptionIT,\n\t\t\tSymbol:         eoSymbolLong.Build(),\n\t\t\tQuantity:       1,\n\t\t\tAction:         tasty.BTO,\n\t\t},\n\t},\n}\n```\n\n\u003e GTD Order\n\n```go\norder := tasty.NewOrder{\n\tTimeInForce: tasty.GTD,\n\tGtcDate:     \"2023-06-23\",\n\tPrice:       0.85,\n\tPriceEffect: tasty.Credit,\n\tOrderType:   tasty.Limit,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.EquityIT,\n\t\t\tSymbol:         \"AMD\",\n\t\t\tQuantity:       1,\n\t\t\tAction:         tasty.BTO,\n\t\t},\n\t},\n}\n```\n\n\u003e Stop Limit Order\n\n```go\norder := tasty.NewOrder{\n\tTimeInForce: tasty.Day,\n\tPrice:       180.0,\n\tPriceEffect: tasty.Debit,\n\tOrderType:   tasty.Limit,\n\tStopTrigger: 180.0,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.EquityIT,\n\t\t\tSymbol:         \"AMD\",\n\t\t\tQuantity:       1,\n\t\t\tAction:         tasty.BTO,\n\t\t},\n\t},\n}\n```\n\n\u003e Notional Cryptocurrency Order\n\n```go\norder := tasty.NewOrder{\n\tTimeInForce: tasty.GTC,\n\tOrderType:   tasty.NotionalMarket,\n\tValue:       10.0,\n\tValueEffect: tasty.Debit,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.Crypto,\n\t\t\tSymbol:         string(tasty.Bitcoin),\n\t\t\tAction:         tasty.BTO,\n\t\t},\n\t},\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExample Order Requests\u003c/summary\u003e\n\n**Tastytrade only supports fractional trading of certain equity products.**\n\n- To determine if an equity can be fractionally traded, fetch the equity instrument and check the is-fractional-quantity-eligible field\n\nCheck out tastytrade's [documentation](https://developer.tastytrade.com/order-management/#example-order-requests)\n\n\u003e Fractional Quantity Order\n\n```go\n// Fractional orders must have a minimum monetary value of $5.\n// Buy orders for 0.5 shares of a $1 stock will be rejected.\norder := tasty.NewOrder{\n\tTimeInForce: tasty.Day,\n\tOrderType:   tasty.Market,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.EquityIT,\n\t\t\tSymbol:         \"AMD\",\n\t\t\tQuantity:       0.5,\n\t\t\tAction:         tasty.BTO,\n\t\t},\n\t},\n}\n```\n\n\u003e Notional Amount Order\n\n```go\n// To buy $10 of AMD stock, submit a Notional Market order with a value\n// instead of a price. Omit the quantity field from the legs:\norder := tasty.NewOrder{\n\tTimeInForce: tasty.Day,\n\tOrderType:   tasty.NotionalMarket,\n\tValue: 10.0,\n\tValueEffect: tasty.Debit,\n\tLegs: []tasty.NewOrderLeg{\n\t\t{\n\t\t\tInstrumentType: tasty.EquityIT,\n\t\t\tSymbol:         \"AMD\",\n\t\t\tAction:         tasty.BTO,\n\t\t},\n\t},\n}\n```\n\n\u003c/details\u003e\n\n## Streaming Market Data\n\nCheck out tastytrade's [documentation](https://developer.tastytrade.com/streaming-market-data/)\n\n\u003cdetails\u003e\n\u003csummary\u003eGet a Streamer Token\u003c/summary\u003e\n\n**This requires using the DXFeed Streamer which isn't supported by tastytrade or this unofficial tastytrade API wrapper.**\n\nCheck out tastytrade's [documentation](https://developer.tastytrade.com/streaming-market-data)\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient, _ := tasty.NewCertClient(\u0026hClient)\n\t_, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tdxFeedData, err := client.GetQuoteStreamerTokens()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Do something with the streamer data\n}\n\n```\n\n\u003c/details\u003e\n\n## Streaming Account Data\n\nCheck out tastytrade's [documentation](https://developer.tastytrade.com/streaming-account-data/)\n\n\u003cdetails\u003e\n\u003csummary\u003eSimple Websocket Account Streamer\u003c/summary\u003e\n\n**This is an oversimplified websocket connection example for streaming account data**\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/laustindasauce/tasty-go\"\n\t\"golang.org/x/net/websocket\"\n)\n\nvar (\n\thClient   = http.Client{Timeout: time.Duration(30) * time.Second}\n\tcertCreds = tasty.LoginInfo{\n\t\tLogin:      os.Getenv(\"certUsername\"),\n\t\tPassword:   os.Getenv(\"certPassword\"),\n\t\tRememberMe: true,\n\t}\n)\n\nconst accountNumber = \"5WV48989\"\n\nfunc main() {\n\tclient := tasty.NewCertClient(\u0026hClient)\n\t_, _, err := client.CreateSession(certCreds, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tprotocol := \"\"\n\torigin := \"http://localhost:8080\"\n\n\t// Open Websocket connection\n\tws, err := websocket.Dial(client.GetWebsocketURL(), protocol, origin)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tincomingMessages := make(chan string)\n\tgo readClientMessages(ws, incomingMessages)\n\n\t// Send connect message\n\tresponse := new(tasty.WebsocketMessage)\n\tresponse.Action = \"connect\"\n\tresponse.Value = []string{accountNumber}\n\tresponse.AuthToken = *client.Session.SessionToken\n\terr = websocket.JSON.Send(ws, response)\n\tif err != nil {\n\t\tfmt.Printf(\"Send failed: %s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\n\t// Subscribe to notifications\n\t// Add notification subscription message here\n\t// All available -\u003e https://developer.tastytrade.com/streaming-account-data/#available-actions\n\n\t// Await responses and send heartbeats\n\ti := 0\n\tfor {\n\t\tselect {\n\t\tcase \u003c-time.After(time.Duration(time.Second * 15)):\n\t\t\t// Send heartbeat every 15 seconds to keep connection alive\n\t\t\tfmt.Println(\"sending heartbeat\")\n\t\t\ti++\n\t\t\tresponse := new(tasty.WebsocketMessage)\n\t\t\tresponse.Action = \"heartbeat\"\n\t\t\tresponse.AuthToken = *client.Session.SessionToken\n\t\t\terr = websocket.JSON.Send(ws, response)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"Send failed: %s\\n\", err.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\tcase message := \u003c-incomingMessages:\n\t\t\tfmt.Println(`Message Received:`, message)\n\t\t}\n\t}\n}\n\nfunc readClientMessages(ws *websocket.Conn, incomingMessages chan string) {\n\tfor {\n\t\tvar message string\n\t\terr := websocket.Message.Receive(ws, \u0026message)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Error::: %s\\n\", err.Error())\n\t\t\treturn\n\t\t}\n\t\tincomingMessages \u003c- message\n\t}\n}\n\n```\n\n\u003c/details\u003e\n\n## Testing\n\nNearly 100% code coverage testing.\n\n\u003e Run all tests\n\n```bash\ngo test .\n```\n\n\u003e Run all tests with code coverage information\n\n```bash\ngo test -race -covermode=atomic -coverprofile=coverage.out -v .\n```\n\n## OAuth2 Troubleshooting\n\n### Common Issues and Solutions\n\n#### 1. \"Invalid client credentials\" Error\n\n**Problem:** Your client ID or client secret is incorrect.\n\n**Solution:**\n\n- Verify your credentials in the TastyTrade developer portal\n- Ensure you're using the correct environment (production vs sandbox)\n- Check that your environment variables are set correctly\n\n```bash\necho $TASTY_CLIENT_ID\necho $TASTY_CLIENT_SECRET\n```\n\n#### 2. \"Invalid redirect URI\" Error\n\n**Problem:** The redirect URI doesn't match what's registered with TastyTrade.\n\n**Solution:**\n\n- Ensure the redirect URI in your code exactly matches the one registered in the developer portal\n- For development, use `http://localhost:8080` (HTTP is allowed for localhost)\n- For production, use HTTPS URLs only\n\n#### 3. \"Invalid state parameter\" Error\n\n**Problem:** State parameter mismatch, which could indicate a CSRF attack or implementation error.\n\n**Solution:**\n\n- Ensure you're properly validating the state parameter\n- Don't manually modify the state parameter\n- Make sure the state from the authorization URL matches the one in the callback\n\n```go\n// Always validate state parameter\nif err := client.ValidateState(receivedState); err != nil {\n    log.Fatal(\"Invalid state parameter:\", err)\n}\n```\n\n#### 4. \"Token expired\" Error\n\n**Problem:** Access token has expired and refresh failed.\n\n**Solution:**\n\n- Tokens are automatically refreshed - this usually indicates a refresh token issue\n- Re-authenticate the user through the OAuth2 flow\n- Check that your refresh token hasn't been revoked\n\n```go\n// Check if client is still authenticated\nif !client.IsAuthenticated() {\n    // Need to re-authenticate\n    // ... perform OAuth2 flow again ...\n}\n```\n\n#### 5. \"Authorization code expired\" Error\n\n**Problem:** Too much time passed between getting the authorization code and exchanging it for tokens.\n\n**Solution:**\n\n- Exchange the authorization code for tokens immediately after receiving it\n- Authorization codes typically expire within 10 minutes\n- Don't store authorization codes - exchange them right away\n\n#### 6. Network/Connection Issues\n\n**Problem:** Network timeouts or connection errors during OAuth2 flow.\n\n**Solution:**\n\n- Increase HTTP client timeout\n- Implement retry logic for network errors\n- Check your internet connection and firewall settings\n\n```go\n// Increase timeout for OAuth2 operations\nhttpClient := \u0026http.Client{\n    Timeout: 60 * time.Second, // Increased timeout\n}\n```\n\n#### 7. \"Server temporarily unavailable\" Error\n\n**Problem:** TastyTrade servers are experiencing issues.\n\n**Solution:**\n\n- Wait and retry after a few minutes\n- Check TastyTrade's status page for known issues\n- Implement exponential backoff for retries\n\n### Environment-Specific Issues\n\n#### Sandbox vs Production\n\nMake sure you're using the correct client constructor:\n\n```go\n// For sandbox/testing\nclient, err := tasty.NewCertOAuth2Client(config, httpClient)\n\n// For production\nclient, err := tasty.NewOAuth2Client(config, httpClient)\n```\n\n#### HTTPS Requirements\n\n- Production OAuth2 endpoints require HTTPS\n- Redirect URIs must use HTTPS in production (except localhost for development)\n- Ensure your callback server uses HTTPS in production\n\n### Debugging Tips\n\n#### Enable Detailed Logging\n\n```go\n// Add detailed error logging\nif err != nil {\n    if oauthErr, ok := err.(*tasty.OAuth2DetailedError); ok {\n        log.Printf(\"OAuth2 Error: %s\", oauthErr.Error())\n        log.Printf(\"Error Type: %s\", oauthErr.GetTypeString())\n        log.Printf(\"Severity: %s\", oauthErr.GetSeverityString())\n        if oauthErr.InternalMessage != \"\" {\n            log.Printf(\"Internal: %s\", oauthErr.InternalMessage)\n        }\n    } else {\n        log.Printf(\"General Error: %s\", err.Error())\n    }\n}\n```\n\n#### Check Token Status\n\n```go\ntokenManager := client.GetOAuth2Client().GetTokenManager()\nfmt.Printf(\"Token expired: %v\\n\", tokenManager.IsExpired())\nfmt.Printf(\"Has refresh token: %v\\n\", tokenManager.HasRefreshToken())\nfmt.Printf(\"Time until expiry: %v\\n\", tokenManager.GetTimeUntilExpiry())\n```\n\n#### Validate Configuration\n\n```go\nconfig := tasty.OAuth2Config{\n    ClientID:     os.Getenv(\"TASTY_CLIENT_ID\"),\n    ClientSecret: os.Getenv(\"TASTY_CLIENT_SECRET\"),\n    RedirectURI:  \"http://localhost:8080\",\n}\n\nif err := config.Validate(); err != nil {\n    log.Fatal(\"Invalid configuration:\", err)\n}\n```\n\n### Getting Help\n\nIf you're still experiencing issues:\n\n1. Check the [TastyTrade Developer Documentation](https://developer.tastytrade.com)\n2. Review the OAuth2 specification: [RFC 6749](https://tools.ietf.org/html/rfc6749)\n3. Open an issue on this repository with:\n   - Your Go version\n   - The exact error message\n   - A minimal code example (without credentials)\n   - Whether you're using sandbox or production\n\n## Contributing\n\nPlease consider opening an [issue](https://github.com/laustindasauce/tasty-go/issues) if you notice any bugs or areas of possible improvement. You can also fork this repo and open a pull request with your own changes. Be sure that all changes have adequate testing in a similar fashion to the rest of the repository.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaustindasauce%2Ftasty-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaustindasauce%2Ftasty-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaustindasauce%2Ftasty-go/lists"}