Skip to content

Commit

Permalink
Merge pull request #13 from Tinkoff/dev
Browse files Browse the repository at this point in the history
add order book bot example
  • Loading branch information
jstalex authored Jun 28, 2023
2 parents 4e55a5d + 17b3fff commit 28cdfc1
Show file tree
Hide file tree
Showing 15 changed files with 1,218 additions and 30 deletions.
66 changes: 50 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ SDK предназначен для упрощения работы с API Ти
* Перейдите в [настройки](https://www.tinkoff.ru/invest/settings/)
* Проверьте, что функция “Подтверждение сделок кодом” отключена
* Выпустите токен (если не хотите через API выдавать торговые поручения, то надо выпустить токен "только для чтения")
* Скопируйте токен и сохраните, токен отображается только один раз, просмотреть его позже не получится, тем не менее вы можете выпускать неограниченное количество токенов. Токен передается в библиотеку в методе инициализации SDKInit()
* Скопируйте токен и сохраните, токен отображается только один раз, просмотреть его позже не получится, тем не менее вы можете выпускать неограниченное количество токенов.

## Документация

Expand All @@ -24,18 +24,40 @@ SDK предназначен для упрощения работы с API Ти
### Быстрый старт

Для непосредственного взаимодействия с INVEST API нужно создать клиента.
Примеры использования SDK находятся в директории examples:
* `md_stream.go`, orders_stream.go, operations_stream.go - примеры работы со стримами
* `instruments.go` - примеры работы с сервисом инструментов
* `marketdata.go` - примеры работы с сервисом котировок
* `operations.go` - примеры работы с сервисом операций
* `orders.go` - примеры работы с сервисом торговых поручений
* `stop_orders` - примеры работы с сервисом стоп-заявок
* `users.go` - примеры работы с сервисом счетов
* `sandbox.go` - пример работы с песочницей
* `order_book_download/order_book.go` - пример сохранения стаканов из стрима маркетдаты в sqlite или json

#### Конфигурация SDK
Примеры использования SDK находятся в директории examples.

### Запуск примеров

#### 1. Клонирование репозитория

$ git clone https://github.com/tinkoff/invest-api-go-sdk

#### 2. Конфигурация SDK
Перейдите в директорию с примерами

$ cd invest-api-go-sdk/examples

Создайте файл `config.yaml`

$ touch "config.yaml"

И заполните его по примеру `example.yaml`

```yaml
AccountId: ""
APIToken: <your_token>
EndPoint: sandbox-invest-public-api.tinkoff.ru:443
AppName: invest-api-go-sdk
DisableResourceExhaustedRetry: false
DisableAllRetry: false
MaxRetries: 3
```
*Для быстрого старта на песочнице достаточно указать только токен, остальное заполнится по умолчанию.*
Так же вы можете не использовать `.yaml` файлы, а в main функции вместо `investgo.LoadConfig()`
явно создать `investgo.Config`, и заполнить его по описанию:

```go
type Config struct {
// EndPoint - Для работы с реальным контуром и контуром песочницы нужны разные эндпоинты.
Expand All @@ -47,7 +69,7 @@ Token string `yaml:"APIToken"`
// AppName - Название вашего приложения, по умолчанию = tinkoff-api-go-sdk
AppName string `yaml:"AppName"`
// AccountId - Если уже есть аккаунт для апи можно указать напрямую,
// для песочницы создастся и запишется автоматически
// по умолчанию откроется новый счет в песочнице
AccountId string `yaml:"AccountId"`
// DisableResourceExhaustedRetry - Если true, то сдк не пытается ретраить, после получения ошибки об исчерпывании
// лимита запросов, если false, то сдк ждет нужное время и пытается выполнить запрос снова. По умолчанию = false
Expand All @@ -59,7 +81,14 @@ DisableAllRetry bool `yaml:"DisableAllRetry"`
MaxRetries uint `yaml:"MaxRetries"`
}
```
Для проверки достаточно указать токен и запустить пример `sandbox.go`

#### 3. Запуск
Пример использования `MarketDataStreamService`:

$ go run md_stream.go
Загрузка стаканов из стрима:

$ go run order_book_download/order_book.go

### Дополнительные возможности
* **Загрузка исторических данных.** В рамках сервиса `Marketdata`, метод `GetHistoricCandles` возвращает список
Expand All @@ -72,7 +101,9 @@ MaxRetries uint `yaml:"MaxRetries"`
отключить ретраер для ошибки `ResourceExhausted`, по умолчанию он включен и в случае превышения лимитов Unary - запросов,
ретраер ждет нужное время и продолжает выполнение, *при этом никакого сообщения об ошибке для клиента нет*.

#### Пример использования MarketDataStreamService
<details>
<summary> Пример использования MarketDataStreamService </summary>

```go
package main

Expand Down Expand Up @@ -172,6 +203,9 @@ func main() {
}

```

</details>

### У меня есть вопрос

[Основной репозиторий с документацией](https://github.com/Tinkoff/investAPI/) — в нем вы можете задать вопрос в Issues и получать информацию о релизах в Releases.
Expand Down
14 changes: 14 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Examples

В примерах рассмотрены основные сценарии использования investAPI через пакет investgo.

* `md_stream.go`, `orders_stream.go`, `operations_stream.go` - примеры работы со стримами
* `instruments.go` - примеры работы с сервисом инструментов
* `marketdata.go` - примеры работы с сервисом котировок
* `operations.go` - примеры работы с сервисом операций
* `orders.go` - примеры работы с сервисом торговых поручений
* `stop_orders` - примеры работы с сервисом стоп-заявок
* `users.go` - примеры работы с сервисом счетов
* `sandbox.go` - пример работы с песочницей
* `order_book_download/order_book.go` - пример сохранения стаканов из стрима маркетдаты в sqlite или json
* `ob_bot` - пример простейшего бота на стакане
76 changes: 76 additions & 0 deletions examples/ob_bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
## Робот на стакане

### Стратегия
Робот отслеживает "стакан". Если лотов в заявках на покупку больше, чем в лотах на продажу в `BuyRatio` раз,
то поступает сигнал на покупку, в противном случае, если лотов в заявках на продажу больше, чем в лотах на покупку
в `SellRatio` раз - поступает сигнал на продажу

#### Конфигурация
```go
type OrderBookStrategyConfig struct {
// Instruments - слайс идентификаторов инструментов
Instruments []string
// Depth - Глубина стакана
Depth int32
// Если кол-во бид/аск больше чем BuyRatio - покупаем
BuyRatio float64
// Если кол-во аск/бид больше чем SellRatio - продаем
SellRatio float64
// MinProfit - Минимальный процент выгоды, с которым можно совершать сделки
MinProfit float64
// SellOut - Если true, то по достижению дедлайна бот выходит из всех активных позиций
SellOut bool
}
```

### Исполнитель
Под стратегию написан простейший исполнитель, который выставляет рыночные поручения.
Пока реализована возможность открывать только long позиции.

**Покупка**

Заявка на покупку *не* выставляется если:
* Позиция уже открыта
* На счету недостаточно денежных средств

**Продажа**

Заявка на продажу *не* выставляется если:
* Позиция не открыта
* Цена открытия позиции меньше цены последней сделки по этому инструменту

### Режим работы
Данный пример ориентирован на торговлю внутри одного дня. За расписанием торгов следит `investgo.Timer`,
он сигнализирует о начале и завершении основной торговй сессии на сегодня.
При запуске main `investgo.Timer` возвращает канал с событиями, START/STOP - сигналы к запуску и остановке бота,
если выставлен флаг `SellOut` в конфигурации стратеги и время `cancelAhead` при создании таймера, то бот завершит работу и закроет все
позиции за `cancelAhead` до конца торгов текущего дня.

### Запуск

$ git clone https://github.com/tinkoff/invest-api-go-sdk

$ cd invest-api-go-sdk/examples/ob_bot

Создайте файл `config.yaml`

$ touch "config.yaml"

И заполните его по примеру `example.yaml`

```yaml
AccountId: ""
APIToken: <your_token>
EndPoint: sandbox-invest-public-api.tinkoff.ru:443
AppName: invest-api-go-sdk
DisableResourceExhaustedRetry: false
DisableAllRetry: false
MaxRetries: 3
```
*Для быстрого старта на песочнице достаточно указать только токен, остальное заполнится по умолчанию.*
$ go run cmd/main.go
Обратите внимание, что в одной функции main есть возможность создать несколько клиентов для investAPI c разными
токенами и счетами, а с разными клиентами можно создавать разных ботов и запускать их одновременно.
160 changes: 160 additions & 0 deletions examples/ob_bot/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package main

import (
"context"
"github.com/tinkoff/invest-api-go-sdk/examples/ob_bot/internal/bot"
"github.com/tinkoff/invest-api-go-sdk/investgo"
pb "github.com/tinkoff/invest-api-go-sdk/proto"
"go.uber.org/zap"
"log"
"os"
"os/signal"
"sync"
"syscall"
"time"
)

const (
// SHARES_NUM - Количество акций для торгов
SHARES_NUM = 30
// EXCHANGE - Биржа на которой будет работать бот
EXCHANGE = "MOEX"
)

func main() {
// загружаем конфигурацию для сдк из .yaml файла
sdkConfig, err := investgo.LoadConfig("config.yaml")
if err != nil {
log.Fatalf("config loading error %v", err.Error())
}

sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// сдк использует для внутреннего логирования investgo.Logger
// для примера передадим uber.zap
prod := zap.NewExample()
defer func() {
err := prod.Sync()
if err != nil {
log.Printf("Prod.Sync %v", err.Error())
}
}()
if err != nil {
log.Fatalf("logger creating error %v", err)
}
logger := prod.Sugar()
// создаем клиента для investAPI, он позволяет создавать нужные сервисы и уже
// через них вызывать нужные методы
client, err := investgo.NewClient(ctx, sdkConfig, logger)
if err != nil {
logger.Fatalf("client creating error %v", err.Error())
}
defer func() {
logger.Infof("closing client connection")
err := client.Stop()
if err != nil {
logger.Errorf("client shutdown error %v", err.Error())
}
}()

// для создания стратеги нужно ее сконфигурировать, для этого получим список идентификаторов инструментов,
// которыми предстоит торговать
insrtumentsService := client.NewInstrumentsServiceClient()
// получаем список акций доступных для торговли через investAPI
instrumentsResp, err := insrtumentsService.Shares(pb.InstrumentStatus_INSTRUMENT_STATUS_BASE)
if err != nil {
logger.Errorf(err.Error())
}
// слайс идентификаторов торговых инструментов instrument_uid
// акции с московской биржи
instrumentIds := make([]string, 0, 300)
shares := instrumentsResp.GetInstruments()
for _, share := range shares {
if len(instrumentIds) > SHARES_NUM-1 {
break
}
if share.GetExchange() == EXCHANGE {
instrumentIds = append(instrumentIds, share.GetUid())
}
}
logger.Infof("got %v instruments\n", len(instrumentIds))

instruments := instrumentIds
// instruments := []string{"6afa6f80-03a7-4d83-9cf0-c19d7d021f76", "e6123145-9665-43e0-8413-cd61b8aa9b13"}

// конфиг стратегии бота на стакане
orderBookConfig := bot.OrderBookStrategyConfig{
Instruments: instruments,
Depth: 20,
BuyRatio: 2,
SellRatio: 2,
MinProfit: 0.5,
SellOut: true,
}

// создание бота на стакане
botOnOrderBook, err := bot.NewBot(ctx, client, orderBookConfig)
if err != nil {
logger.Fatalf("bot on order book creating fail %v", err.Error())
}

wg := &sync.WaitGroup{}
// Таймер для Московской биржи, отслеживает расписание и дает сигналы, на остановку/запуск бота
// cancelAhead - Событие STOP будет отправлено в канал за cancelAhead до конца торгов
cancelAhead := time.Minute * 5
t := investgo.NewTimer(client, "MOEX", cancelAhead)

// запуск таймера
wg.Add(1)
go func(ctx context.Context) {
defer wg.Done()
err := t.Start(ctx)
if err != nil {
logger.Errorf(err.Error())
}
}(ctx)

// по сигналам останавливаем таймер
go func() {
<-sigs
t.Stop()
}()

// чтение событий от таймера и управление ботом
events := t.Events()
wg.Add(1)
go func(ctx context.Context) {
defer wg.Done()
for {
select {
case <-ctx.Done():
return
case ev, ok := <-events:
if !ok {
return
}
logger.Infof("got event = %v", ev)
switch ev {
case investgo.START:
// запуск бота
wg.Add(1)
go func() {
defer wg.Done()
err = botOnOrderBook.Run()
if err != nil {
logger.Errorf(err.Error())
}
}()
case investgo.STOP:
// остановка бота
botOnOrderBook.Stop()
}
}
}
}(ctx)

wg.Wait()
}
Loading

0 comments on commit 28cdfc1

Please sign in to comment.