Help us shape the future of Go! Take the 2026 Go Developer Survey

go-etoro

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 16, 2026 License: MIT

README

go-etoro

Go Reference CI version license imported by

Go clients for the eToro public API, generated from the specs eToro publishes.

Directory Package API Generator
rest/ etororest REST, https://public-api.etoro.com oapi-codegen
ws/ etorows WebSocket, wss://ws.etoro.com/ws gen-asyncapi

Everything in rest/ and ws/ except gen.go and the generator configs is generated. Don't edit it by hand, regenerate it.

Install

go get github.com/psyb0t/go-etoro

REST

Every endpoint in the spec has a typed method on ClientWithResponses. eToro authenticates with two headers, x-api-key and x-user-key, taken from the API key section of your account settings.

package main

import (
	"context"
	"net/http"
	"os"

	"github.com/psyb0t/go-etoro/rest"
)

func main() {
	auth := etororest.WithRequestEditorFn(func(_ context.Context, req *http.Request) error {
		req.Header.Set("x-api-key", os.Getenv("ETORO_API_KEY"))
		req.Header.Set("x-user-key", os.Getenv("ETORO_USER_KEY"))

		return nil
	})

	client, err := etororest.NewClientWithResponses("https://public-api.etoro.com", auth)
	if err != nil {
		panic(err)
	}

	res, err := client.GetBalancesWithResponse(context.Background())
	if err != nil {
		panic(err)
	}

	_ = res.JSON200
}

WebSocket

etorows is a typed client for the WebSocket API. It sends the authenticate, subscribe and unsubscribe requests, and decodes the instrument rate, candle and market status pushes, the order and position updates, and the operation responses. Every message is checked against eToro's schema both ways.

package main

import (
	"context"
	"crypto/rand"
	"log/slog"
	"os"

	"github.com/psyb0t/gen-asyncapi/asyncapirt/gorillaws"
	"github.com/psyb0t/go-etoro/ws"
)

type handler struct {
	etorows.UnimplementedHandler
}

func (handler) HandleInstrumentRateMessage(
	ctx context.Context,
	envelope etorows.InstrumentRateEnvelope,
) error {
	for _, item := range envelope.Messages {
		rate, err := item.ContentValue()
		if err != nil {
			return err
		}

		slog.InfoContext(ctx, "rate", "topic", item.Topic, "ask", *rate.Ask)
	}

	return nil
}

func main() {
	ctx := context.Background()

	conn, err := gorillaws.Dial(ctx, etorows.ServerEtoroURL)
	if err != nil {
		panic(err)
	}

	client := etorows.NewClient(conn)
	defer client.Close()

	err = client.Authenticate(ctx, etorows.AuthenticateEnvelope{
		ID:        rand.Text(),
		Operation: "Authenticate",
		Data: etorows.AuthenticateData{
			APIKey:  os.Getenv("ETORO_API_KEY"),
			UserKey: os.Getenv("ETORO_USER_KEY"),
		},
	})
	if err != nil {
		panic(err)
	}

	err = client.Subscribe(ctx, etorows.SubscribeEnvelope{
		ID:        rand.Text(),
		Operation: "Subscribe",
		Data:      etorows.SubscribeData{Topics: []string{"instrument:1001"}},
	})
	if err != nil {
		panic(err)
	}

	if err := client.Run(ctx, handler{}); err != nil {
		panic(err)
	}
}

Push frames are envelopes whose items carry a type and a JSON-encoded content string; ContentValue() decodes and checks it. Rate values arrive as strings on the wire ("1.25"), so the InstrumentRate fields are strings too.

Pushes are told apart by their type; both order update types go to HandleOrderUpdateMessage. An envelope with a type the spec doesn't list fails with asyncapirt.ErrUnknownMessage.

Regenerating

eToro's specs are not committed. make generate downloads the current OpenAPI and AsyncAPI documents into the gitignored api/ directory and regenerates both packages:

make generate

Each package's gen.go declares its own steps, so make generate is just go generate ./...:

  • rest/gen.go downloads openapi.json and runs oapi-codegen with rest/config.yaml.
  • ws/gen.go downloads asyncapi.json, runs scripts/match-wire-types.sh, and runs gen-asyncapi with ws/config.yaml. The match step turns the number fields of every schema whose description says its values arrive as JSON strings, such as InstrumentRate, into strings, so real frames decode.

Development

Every command runs in the Dockerfile.dev image. The host needs only Docker and Make.

make help      # list targets
make dep       # go mod tidy + vendor
make lint      # shellcheck, shfmt, go fix, golangci-lint
make test      # go test -race
make sec       # gitleaks, govulncheck, semgrep
make generate  # download the specs and regenerate

License

MIT. The generated code comes from eToro's published API specifications. The specifications themselves are not redistributed here.

Directories

Path Synopsis
Package etororest provides primitives to interact with the openapi HTTP API.
Package etororest provides primitives to interact with the openapi HTTP API.
Package etorows is a Go client for the eToro public WebSocket API, generated with gen-asyncapi from the AsyncAPI document eToro publishes.
Package etorows is a Go client for the eToro public WebSocket API, generated with gen-asyncapi from the AsyncAPI document eToro publishes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL