Go 1.21+ ~10 min setup
Go Integration
Strongly-typed API access with Go's concurrency primitives for parallel multi-symbol fetching. Zero external dependencies — uses only the standard library.
Struct Definitions
Strongly-typed structs for all API responses:
tickatlas/types.go
package tickatlas
import "time"
// IndicatorResponse represents a single indicator value.
type IndicatorResponse struct {
Symbol string `json:"symbol"`
Indicator string `json:"indicator"`
Timeframe string `json:"timeframe"`
Timestamp time.Time `json:"timestamp"`
Value float64 `json:"value"`
Signal string `json:"signal"`
OHLC OHLC `json:"ohlc"`
Metadata Metadata `json:"metadata"`
}
type OHLC struct {
Open float64 `json:"open"`
High float64 `json:"high"`
Low float64 `json:"low"`
Close float64 `json:"close"`
}
type Metadata struct {
Period int `json:"period"`
Source string `json:"source"`
DataAgeSeconds int `json:"data_age_seconds"`
}
// QuoteResponse represents a live price quote.
type QuoteResponse struct {
Symbol string `json:"symbol"`
Bid float64 `json:"bid"`
Ask float64 `json:"ask"`
Spread float64 `json:"spread"`
Timestamp time.Time `json:"timestamp"`
}
// SummaryResponse represents an AI-powered market summary.
type SummaryResponse struct {
Symbol string `json:"symbol"`
Timeframe string `json:"timeframe"`
Bias string `json:"bias"`
Confidence float64 `json:"confidence"`
Narrative string `json:"narrative"`
}
// ScreenerResult represents one symbol in a screener scan.
type ScreenerResult struct {
Symbol string `json:"symbol"`
Value float64 `json:"value"`
Signal string `json:"signal"`
}
type ScreenerResponse struct {
Indicator string `json:"indicator"`
Timeframe string `json:"timeframe"`
Results []ScreenerResult `json:"results"`
} API Client
A clean client using net/http with proper error handling:
tickatlas/client.go
package tickatlas
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
const baseURL = "https://tickatlas.com/v1"
// Client is the TickAtlas API client.
type Client struct {
apiKey string
httpClient *http.Client
}
// NewClient creates a new TickAtlas client.
func NewClient(apiKey string) *Client {
return &Client{
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 10 * time.Second,
},
}
}
func (c *Client) get(endpoint string, params url.Values) ([]byte, error) {
reqURL := fmt.Sprintf("%s/%s?%s", baseURL, endpoint, params.Encode())
req, err := http.NewRequest("GET", reqURL, nil)
if err != nil {
return nil, err
}
req.Header.Set("X-API-Key", c.apiKey)
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == 429 {
return nil, fmt.Errorf("rate limited: retry after %s", resp.Header.Get("Retry-After"))
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("API error: %d %s", resp.StatusCode, resp.Status)
}
return io.ReadAll(resp.Body)
}
// Indicator fetches a single indicator value.
func (c *Client) Indicator(symbol, indicator, timeframe string) (*IndicatorResponse, error) {
params := url.Values{
"symbol": {symbol}, "indicator": {indicator}, "timeframe": {timeframe},
}
body, err := c.get("indicator", params)
if err != nil {
return nil, err
}
var result IndicatorResponse
return &result, json.Unmarshal(body, &result)
}
// Quote fetches a live price quote.
func (c *Client) Quote(symbol string) (*QuoteResponse, error) {
body, err := c.get("quote", url.Values{"symbol": {symbol}})
if err != nil {
return nil, err
}
var result QuoteResponse
return &result, json.Unmarshal(body, &result)
}
// Summary fetches the AI market summary.
func (c *Client) Summary(symbol, timeframe string) (*SummaryResponse, error) {
body, err := c.get("summary", url.Values{"symbol": {symbol}, "timeframe": {timeframe}})
if err != nil {
return nil, err
}
var result SummaryResponse
return &result, json.Unmarshal(body, &result)
}
// Screener scans symbols by indicator criteria.
func (c *Client) Screener(indicator, timeframe string, maxVal float64) (*ScreenerResponse, error) {
params := url.Values{
"indicator": {indicator}, "timeframe": {timeframe},
"max": {fmt.Sprintf("%.2f", maxVal)},
}
body, err := c.get("screener", params)
if err != nil {
return nil, err
}
var result ScreenerResponse
return &result, json.Unmarshal(body, &result)
} Concurrent Multi-Symbol Fetching
Use goroutines and channels to fetch indicators for multiple symbols simultaneously:
main.go
package main
import (
"fmt"
"os"
"sync"
)
func main() {
client := tickatlas.NewClient(os.Getenv("CLAW_API_KEY"))
symbols := []string{"EURUSD", "GBPUSD", "USDJPY", "XAUUSD", "BTCUSD"}
var wg sync.WaitGroup
type result struct {
Symbol string
RSI float64
Signal string
Err error
}
results := make(chan result, len(symbols))
// Fetch RSI for all symbols concurrently
for _, sym := range symbols {
wg.Add(1)
go func(s string) {
defer wg.Done()
ind, err := client.Indicator(s, "RSI_14", "H1")
if err != nil {
results <- result{Symbol: s, Err: err}
return
}
results <- result{Symbol: s, RSI: ind.Value, Signal: ind.Signal}
}(sym)
}
go func() {
wg.Wait()
close(results)
}()
fmt.Println("Symbol | RSI | Signal")
fmt.Println("----------|---------|--------")
for r := range results {
if r.Err != nil {
fmt.Printf("%-9s | ERROR | %v\n", r.Symbol, r.Err)
} else {
fmt.Printf("%-9s | %6.2f | %s\n", r.Symbol, r.RSI, r.Signal)
}
}
} Error Handling with Retry
go
// Robust error handling pattern
func fetchWithRetry(client *tickatlas.Client, symbol, indicator, tf string) (*tickatlas.IndicatorResponse, error) {
maxRetries := 3
for i := 0; i < maxRetries; i++ {
result, err := client.Indicator(symbol, indicator, tf)
if err == nil {
return result, nil
}
// Don't retry on auth errors
if strings.Contains(err.Error(), "401") || strings.Contains(err.Error(), "403") {
return nil, fmt.Errorf("auth error (not retryable): %w", err)
}
// Exponential backoff
wait := time.Duration(1<<uint(i)) * time.Second
log.Printf("Attempt %d/%d failed: %v. Retrying in %v...", i+1, maxRetries, err, wait)
time.Sleep(wait)
}
return nil, fmt.Errorf("failed after %d retries", maxRetries)
}