trader

package module
v0.1.10-0...-a4ba68a Latest Latest
Warning

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

Go to latest
Published: Nov 22, 2020 License: CC0-1.0 Imports: 3 Imported by: 7

README

Trader

pipeline status wercker status

Go Report Card

A platform to bring automation from TradingView alerts to Binance Futures positions management.

Synopsis

The Trader ecosystem exposes a RESTful(ish) API written in go leveraging part of the go-kit components, and it is strongly oriented to microservices. It is backed by CockroachDB, and notifies to Telegram any action executed against Binance.

It currently exposes only two endpoints /trade and /trade/{uuid}, the first accepting POST requests (create), and GET (read / list), the second allowing GET requests to retrieve a single entity by its uuid.

Requisites

Clone this repository under $GOPATH/src/hyperd.

Both execution and development environment are kept to the bare minimum set of requirements.

Prepare your unix based workstation with:

  • go v1.15.5
  • docker v19.03.13
  • docker-compose v1.27.4
  • gcloud SDK v290.0.1 or greater
  • openssl LibreSSL 2.8.3
  • jq (optional)

You will also need access to the gcloud docker registry gcr.io/hyperd-containers, remember to request the proper roles. Once you have em granted, run gclud login.

Folders structure

Secrets

Before you start, create a .secrets folder and structure it as follows:

.secrets/
├── cockroachdb
│   └── certs
├── gcloud
└── tls

Place your gcloud key under .secrets/gcloud/key.json;

The cocokroachdb secrets folder holds the keys and certs to enable TLS and authentication to the database. The tls folder stores the certificates to encrypt the API transport.

The Go code

The go code is structured in modules and follows the soundcloud specs.

.
├── cmd
│   └── trader
├── cockroachdb
├── implementation
├── inmemory
├── middleware
├── releases
└── transport
    └── http

The core folders modules are:

  • transport, holding any implemented transport (currently http);
  • middleware, holding the middlewares registered in the API;
  • implementation, providing the trader service implementation;
  • cmd/trader, hodling the main package (as per convention).

The cockroachdb folder holds the database logic, implemented with gorm, while the inmemory allows to mock the datastore in memory, useful for testing quickly new features (will probably be deprecated soon).

Run it locally

Once properly set the secrets, and installed the requirements, there are a few steps to follow before you can run the code locally.

  1. setup the gcloud environment: run ./tools/gcloud-setup
  2. generate the TLS certs for the application: run ./tools/generate-certs
  3. edit ./.config/cockroachdb/certs/node.cnf, replacing <ip_address> with your local network adapter IP address
  4. copy the openssl config files from ./.config/cockroachdb/certs to ./.secrets/cockroachdb
  5. generate the cockroachdb certs for TLS and authentication: run ./tools/generate-cockroachdb-certs
  6. rename the example config file: run cp .config/prod/config.yml{.example,}
  7. edit the config file according to your setup

Execute docker-compose up from the repository's root. The API will be listening on port 8443/TCP over TLS / HTTP/2, and 3000/TCP over HTTP in clear text. You can set different ports editing the config file.

Check that it is running:

curl -k https://localhost:8443/ | jq

# response:
{
  "status": "Healthy"
}

Build it

  1. initialize the go modules: run ./tools/init-modules
  2. build it and update the docker image: run ./build

Binance

Getting started

Initialize the object as follows:

var logger log.Logger
logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
logger = log.With(logger, "time", log.DefaultTimestampUTC, "caller", log.DefaultCaller)

hmacSigner := &binance.HmacSigner{
    Key: []byte("API secret"),
}
ctx, _ := context.WithCancel(context.Background())
// use second return value for cancelling request when shutting down the app

binanceService := binance.NewAPIService(
    "https://www.binance.com",
    "API key",
    hmacSigner,
    logger,
    ctx,
)
b := binance.NewBinance(binanceService)

Set new order:

newOrder, err := b.NewOrder(binance.NewOrderRequest{
    Symbol:      "BTCUSDT",
    Quantity:    1,
    Price:       18200,
    Side:        binance.SideSell,
    TimeInForce: binance.GTC,
    Type:        binance.TypeLimit,
    Timestamp:   time.Now(),
})
if err != nil {
    panic(err)
}
fmt.Println(newOrder)

Cancel an existing order:

canceledOrder, err := b.CancelOrder(binance.CancelOrderRequest{
    Symbol:    "BTCUSDT",
    OrderID:   newOrder.OrderID,
    Timestamp: time.Now(),
})
if err != nil {
    panic(err)
}
fmt.Printf("%#v\n", canceledOrder)

Skids below this line

Add a position to the database:

# POST /trade
payload='
{
  "symbol": "WAVESUSDT",
  "side": "BUY",
  "type": "LIMIT",
  "quantity": 1300,
  "leverage": 10,
  "price": 3.25
}
'
curl -k -d "$payload" -H "Content-Type: application/json" -X POST https://localhost:8443/trade | jq
# response:
{
  "id": "c3b92d97-6c43-430c-a231-d38f780d4caa"
}

Interact with a running position (the parent must be the UUID of a running trade):

# POST /trade
payload='
{
  "symbol": "WAVESUSDT",
  "parent": "c3b92d97-6c43-430c-a231-d38f780d4caa",
  "side": "SELL",
  "type": "LIMIT",
  "quantity": 400,
  "leverage": 10,
  "price": 3.5
}
'
curl -k -d "$payload" -H "Content-Type: application/json" -X POST https://localhost:8443/trade | jq
# response:
{
  "id": "0ab5e668-0d36-4600-a2f0-7f56e9be31a1"
}

List the running trades:

# GET /trade
curl -k https://localhost:8443/trade | jq

# response:
{
  "trades": [
    {
      "uuid": "0ab5e668-0d36-4600-a2f0-7f56e9be31a1",
      "parent": "c3b92d97-6c43-430c-a231-d38f780d4caa",
      "symbol": "WAVESUSDT",
      "side": "SELL",
      "type": "LIMIT",
      "quantity": 400,
      "leverage": 10,
      "price": 3.5
    },
    {
      "uuid": "0d7bb062-957c-45bf-a123-408328ab6b7d",
      "parent": "00000000-0000-0000-0000-000000000000",
      "symbol": "WAVESUSDT",
      "side": "BUY",
      "type": "LIMIT",
      "quantity": 1300,
      "leverage": 10,
      "price": 3.25
    },
    {
      "uuid": "c3b92d97-6c43-430c-a231-d38f780d4caa",
      "parent": "00000000-0000-0000-0000-000000000000",
      "symbol": "WAVESUSDT",
      "side": "BUY",
      "type": "LIMIT",
      "quantity": 1300,
      "leverage": 10,
      "price": 3.25
    }
  ]
}

Get a trade:

# GET /trade/{uuid}
curl -k https://localhost:8443/trade/0ab5e668-0d36-4600-a2f0-7f56e9be31a1 | jq

# response:
{
  "trade": {
    "uuid": "0ab5e668-0d36-4600-a2f0-7f56e9be31a1",
    "parent": "c3b92d97-6c43-430c-a231-d38f780d4caa",
    "symbol": "WAVESUSDT",
    "side": "SELL",
    "type": "LIMIT",
    "quantity": 400,
    "leverage": 10,
    "price": 3.5
  }
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInconsistentIDs = errors.New("inconsistent IDs")
	ErrAlreadyExists   = errors.New("already exists")
	ErrNotFound        = errors.New("not found")
	ErrCmdRepository   = errors.New("unable to command repository")
	ErrQueryRepository = errors.New("unable to query repository")
	ErrParentNotFound  = errors.New("unable to retrieve the parent")
	ErrSymbolMismatch  = errors.New("the selecte symbol does not match the parent's")
)

Response errors

Functions

This section is empty.

Types

type Repository

type Repository interface {
	PostTrade(ctx context.Context, t Trade) (string, error)
	GetTrade(ctx context.Context, ID uuid.UUID) (Trade, error)
	GetTrades(ctx context.Context) ([]Trade, error)
}

Repository describes the persistence on trade model

type Service

type Service interface {
	PostTrade(ctx context.Context, t Trade) (string, error)
	GetTrade(ctx context.Context, ID uuid.UUID) (Trade, error)
	GetTrades(ctx context.Context) ([]Trade, error)
}

Service is a CRUD interface for a Trade.

type Trade

type Trade struct {
	// gorm.Model
	ID       uuid.UUID `json:"uuid,omitempty" gorm:"primary_key"`
	Parent   uuid.UUID `json:"parent,omoniempty" valid:"optional"`
	Symbol   string    `json:"symbol,omitempty" valid:"required"`
	Side     string    `json:"side,omitempty" valid:"required,in(BUY|SELL)"`
	Type     string    `json:"type,omitempty" valid:"required,in(LIMIT|MARKET)"`
	Quantity float64   `json:"quantity,omitempty" valid:"required,float"`
	Leverage int       `json:"leverage,omitempty" valid:"required,numeric,range(0|50)"`
	Price    float64   `json:"price,omitempty" valid:"required,float"`
}

Trade represents a single Binance trade.

Directories

Path Synopsis
cmd
trader module
cockroachdb module
config module
inmemory module
middleware module
transport module
http module

Jump to

Keyboard shortcuts

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