diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6156a57..856889d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,4 +10,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: v1.54.2 + version: v1.59.1 diff --git a/.gitignore b/.gitignore index 398baf2..fc1b400 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ # vendor/ .idea +.DS_Store diff --git a/Makefile b/Makefile index 4f168e5..a6f6aa4 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ lint: ## Run golang lint using docker -v ${GOPATH}/pkg/mod:/go/pkg/mod \ -v ${PWD}:/app \ -w /app \ - golangci/golangci-lint:v1.54.2 \ + golangci/golangci-lint:v1.59.1 \ golangci-lint run -v --modules-download-mode=readonly test: ## Run tests @@ -26,3 +26,6 @@ doc: ## Run doc server using docker -w /go/src/github.com/evsamsonov/tinkoff-broker \ golang:latest \ bash -c "go install golang.org/x/tools/cmd/godoc@latest && /go/bin/godoc -http=:6060" + +generate: ## Run go generate + go generate ./... diff --git a/README.md b/README.md index b3e05cd..3c89e18 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,7 @@ for creating automated trading robots. ## How to use -Create a new `Tinkoff` object using constructor `New`. Pass [full-access token](https://tinkoff.github.io/investAPI/token/), -user account identifier, [FIGI](https://tinkoff.github.io/investAPI/faq_identification/) of a trading instrument. +Create a new `Tinkoff` object using constructor `New`. Pass Tinkoff Client and user account identifier. ```go package main @@ -26,21 +25,35 @@ import ( "context" "log" - tnkbroker "github.com/evsamsonov/tinkoff-broker" "github.com/evsamsonov/trengin/v2" + "github.com/russianinvestments/invest-api-go-sdk/investgo" + "go.uber.org/zap" + + tnkbroker "github.com/evsamsonov/tinkoff-broker/v2" ) func main() { + ctx := context.Background() + + tinkoffConfig := investgo.Config{ + EndPoint: "invest-public-api.tinkoff.ru:443", + Token: "[tinkoff-token]", + AccountId: "[account-id]", + } + tinkoffClient, err := investgo.NewClient(ctx, tinkoffConfig, zap.NewNop().Sugar()) + if err != nil { + log.Fatal("Failed to create tinkoff client", zap.Error(err)) + } + tinkoffBroker, err := tnkbroker.New( - "tinkoff-token", - "123", - "BBG004730N88", + tinkoffClient, + "[account-id]", // options... ) if err != nil { log.Fatal("Failed to create tinkoff broker") } - + tradingEngine := trengin.New(&Strategy{}, tinkoffBroker) if err = tradingEngine.Run(context.Background()); err != nil { log.Fatal("Trading engine crashed") @@ -48,7 +61,8 @@ func main() { } type Strategy struct{} -func (s *Strategy) Run(ctx context.Context, actions Actions) error { panic("implement me") } +func (s *Strategy) Run(ctx context.Context, actions trengin.Actions) error { panic("implement me") } + ``` See more details in [trengin documentation](http://github.com/evsamsonov/trengin). @@ -60,7 +74,6 @@ You can configure `Tinkoff` to use `Option` | Methods | Returns Option which | |-----------------------------------|----------------------------------------------------------------------------------| | `WithLogger` | Sets logger. The default logger is no-op Logger. | -| `WithAppName` | Sets [x-app-name](https://tinkoff.github.io/investAPI/grpc/#appname). | | `WithProtectiveSpread` | Sets protective spread in percent for executing orders. The default value is 1%. | | `WithTradeStreamRetryTimeout` | Defines retry timeout on trade stream error. | | `WithTradeStreamPingWaitDuration` | Defines duration how long we wait for ping before reconnection. | @@ -97,4 +110,5 @@ doc Run doc server using docker lint Run golang lint using docker pre-push Run golang lint and test test Run tests +generate Run go generate ``` diff --git a/clients.go b/clients.go new file mode 100644 index 0000000..7688e02 --- /dev/null +++ b/clients.go @@ -0,0 +1,44 @@ +package tnkbroker + +import ( + "github.com/russianinvestments/invest-api-go-sdk/investgo" + pb "github.com/russianinvestments/invest-api-go-sdk/proto" +) + +// nolint: lll +// +//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name ordersServiceClient --inpackage --case snake +type ordersServiceClient interface { + PostOrder(req *investgo.PostOrderRequest) (*investgo.PostOrderResponse, error) + GetOrderState(accountID, orderID string, priceType pb.PriceType) (*investgo.GetOrderStateResponse, error) +} + +// nolint: lll +// +//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name stopOrdersServiceClient --inpackage --case snake +type stopOrdersServiceClient interface { + PostStopOrder(req *investgo.PostStopOrderRequest) (*investgo.PostStopOrderResponse, error) + CancelStopOrder(accountID, stopOrderID string) (*investgo.CancelStopOrderResponse, error) + GetStopOrders(accountID string) (*investgo.GetStopOrdersResponse, error) +} + +// nolint: lll +// +//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name ordersStreamClient --inpackage --case snake +type ordersStreamClient interface { + TradesStream(accounts []string) (*investgo.TradesStream, error) +} + +// nolint: lll +// +//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name marketDataServiceClient --inpackage --case snake +type marketDataServiceClient interface { + GetLastPrices(instrumentIds []string) (*investgo.GetLastPricesResponse, error) +} + +// nolint: lll +// +//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name instrumentsServiceClient --inpackage --case snake +type instrumentsServiceClient interface { + InstrumentByFigi(id string) (*investgo.InstrumentResponse, error) +} diff --git a/cmd/tinkoff-checkup/main.go b/cmd/tinkoff-checkup/main.go index 6c74252..2e075e9 100644 --- a/cmd/tinkoff-checkup/main.go +++ b/cmd/tinkoff-checkup/main.go @@ -29,17 +29,20 @@ import ( "os" "syscall" - tnkbroker "github.com/evsamsonov/tinkoff-broker" "github.com/evsamsonov/trengin/v2" + "github.com/russianinvestments/invest-api-go-sdk/investgo" "go.uber.org/zap" "golang.org/x/sync/errgroup" "golang.org/x/term" + + tnkbroker "github.com/evsamsonov/tinkoff-broker/v2" ) func main() { if len(os.Args) < 3 { fmt.Println( "This command checks all methods of Tinkoff Broker.\n" + + "This command checks all methods of Tinkoff Broker.\n" + "It opens position, changes conditional orders, closes position.", ) fmt.Println("\nUsage: tinkoff-checkup [ACCOUNT_ID] [INSTRUMENT_FIGI] [-v]") @@ -140,8 +143,24 @@ func NewTinkoffCheckuper(verbose bool) (*TinkoffCheckuper, error) { } func (t *TinkoffCheckuper) CheckUp(params CheckUpArgs) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + g, ctx := errgroup.WithContext(ctx) + + tinkoffConfig := investgo.Config{ + EndPoint: "invest-public-api.tinkoff.ru:443", + Token: params.tinkoffToken, + AppName: "evsamsonov.gdealer", + AccountId: params.accountID, + } + tinkoffClient, err := investgo.NewClient(ctx, tinkoffConfig, t.logger.Sugar()) + if err != nil { + return fmt.Errorf("create tinkoff client: %w", err) + } + tinkoffBroker, err := tnkbroker.New( - params.tinkoffToken, + tinkoffClient, params.accountID, tnkbroker.WithLogger(t.logger), ) @@ -149,9 +168,6 @@ func (t *TinkoffCheckuper) CheckUp(params CheckUpArgs) error { return fmt.Errorf("create tinkoff broker: %w", err) } - ctx, cancel := context.WithCancel(context.Background()) - g, ctx := errgroup.WithContext(ctx) - g.Go(func() error { defer cancel() if err := tinkoffBroker.Run(ctx); err != nil { diff --git a/go.mod b/go.mod index 8ecd894..a3e4c79 100644 --- a/go.mod +++ b/go.mod @@ -1,29 +1,35 @@ -module github.com/evsamsonov/tinkoff-broker +module github.com/evsamsonov/tinkoff-broker/v2 -go 1.19 +go 1.21 + +toolchain go1.22.5 require ( - github.com/evsamsonov/trengin/v2 v2.0.2 - github.com/google/uuid v1.3.1 - github.com/stretchr/testify v1.8.4 - github.com/tinkoff/invest-api-go-sdk v1.0.2 + github.com/evsamsonov/trengin/v2 v2.2.0 + github.com/google/uuid v1.6.0 + github.com/russianinvestments/invest-api-go-sdk v1.23.1 + github.com/stretchr/testify v1.9.0 github.com/undefinedlabs/go-mpatch v1.0.6 - go.uber.org/zap v1.25.0 - golang.org/x/sync v0.3.0 - golang.org/x/term v0.12.0 - google.golang.org/grpc v1.58.0 + go.uber.org/zap v1.27.0 + golang.org/x/sync v0.7.0 + golang.org/x/term v0.22.0 ) require ( + cloud.google.com/go/compute/metadata v0.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/stretchr/objx v0.5.2 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/text v0.11.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect - google.golang.org/protobuf v1.31.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index cf673b9..5266c68 100644 --- a/go.sum +++ b/go.sum @@ -1,58 +1,59 @@ -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/evsamsonov/trengin/v2 v2.0.2 h1:WWcL7QJVcbt78IXV1/aBQfRCcYAQFDCd6cB74mrV21o= -github.com/evsamsonov/trengin/v2 v2.0.2/go.mod h1:merjbl+KOokEz3PlhaDlQXoupTKgjF3kJxUMQVYRLCc= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/evsamsonov/trengin/v2 v2.2.0 h1:LMkjq7s+BpYbTnpv/O+EEA02tyzSTv8Sxpj4pszK+sA= +github.com/evsamsonov/trengin/v2 v2.2.0/go.mod h1:merjbl+KOokEz3PlhaDlQXoupTKgjF3kJxUMQVYRLCc= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.5 h1:3IZOAnD058zZllQTZNBioTlrzrBG/IjpiZ133IEtusM= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.5/go.mod h1:xbKERva94Pw2cPen0s79J3uXmGzbbpDYFBFDlZ4mV/w= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tinkoff/invest-api-go-sdk v1.0.2 h1:N/NAc3ZIUP6xFtu9d1r2f7fgjr5pI5UQYkUn/XO5QLo= -github.com/tinkoff/invest-api-go-sdk v1.0.2/go.mod h1:yFp0cKksK9mtMixJWYmylx/V7I2YIgUAapsmqb3bLf0= +github.com/russianinvestments/invest-api-go-sdk v1.23.1 h1:iLWwYyzEKfH9ottN/v7Dvz/u4a8IU/iWDiGtmPNnk08= +github.com/russianinvestments/invest-api-go-sdk v1.23.1/go.mod h1:rOu2P3GMTQEkQxRpQfp+wK5k71c3SUDHIke3Ijr8cOU= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/undefinedlabs/go-mpatch v1.0.6 h1:h8q5ORH/GaOE1Se1DMhrOyljXZEhRcROO7agMqWXCOY= github.com/undefinedlabs/go-mpatch v1.0.6/go.mod h1:TyJZDQ/5AgyN7FSLiBJ8RO9u2c6wbtRvK827b6AVqY4= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= -google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/tnkposition/position.go b/internal/tnkposition/position.go index c524991..a222981 100644 --- a/internal/tnkposition/position.go +++ b/internal/tnkposition/position.go @@ -4,7 +4,7 @@ import ( "sync" "time" - investapi "github.com/tinkoff/invest-api-go-sdk" + pb "github.com/russianinvestments/invest-api-go-sdk/proto" "github.com/evsamsonov/trengin/v2" ) @@ -15,13 +15,13 @@ type Position struct { closed chan trengin.Position stopLossID string takeProfitID string - orderTrades []*investapi.OrderTrade - instrument *investapi.Instrument + orderTrades []*pb.OrderTrade + instrument *pb.Instrument } func NewPosition( pos *trengin.Position, - instrument *investapi.Instrument, + instrument *pb.Instrument, stopLossID string, takeProfitID string, closed chan trengin.Position, @@ -45,7 +45,7 @@ func (p *Position) SetTakeProfitID(id string, takeProfit float64) { p.position.TakeProfit = takeProfit } -func (p *Position) AddOrderTrade(orderTrades ...*investapi.OrderTrade) { +func (p *Position) AddOrderTrade(orderTrades ...*pb.OrderTrade) { p.orderTrades = append(p.orderTrades, orderTrades...) } @@ -65,12 +65,12 @@ func (p *Position) Position() trengin.Position { return *p.position } -func (p *Position) Instrument() *investapi.Instrument { +func (p *Position) Instrument() *pb.Instrument { return p.instrument } -func (p *Position) OrderTrades() []*investapi.OrderTrade { - result := make([]*investapi.OrderTrade, len(p.orderTrades)) +func (p *Position) OrderTrades() []*pb.OrderTrade { + result := make([]*pb.OrderTrade, len(p.orderTrades)) copy(result, p.orderTrades) return p.orderTrades } diff --git a/mock.go b/mock.go deleted file mode 100644 index 53b1468..0000000 --- a/mock.go +++ /dev/null @@ -1,31 +0,0 @@ -package tnkbroker - -import investapi "github.com/tinkoff/invest-api-go-sdk" - -// nolint: lll,unused -// -//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name ordersServiceClient --inpackage --case snake -type ordersServiceClient interface { - investapi.OrdersServiceClient -} - -// nolint: lll,unused -// -//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name stopOrdersServiceClient --inpackage --case snake -type stopOrdersServiceClient interface { - investapi.StopOrdersServiceClient -} - -// nolint: lll,unused -// -//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name marketDataServiceClient --inpackage --case snake -type marketDataServiceClient interface { - investapi.MarketDataServiceClient -} - -// nolint: lll,unused -// -//go:generate docker run --rm -v ${PWD}:/app -w /app vektra/mockery --name instrumentServiceClient --inpackage --case snake -type instrumentServiceClient interface { - investapi.InstrumentsServiceClient -} diff --git a/mock_instrument_service_client.go b/mock_instrument_service_client.go deleted file mode 100644 index ec4d84e..0000000 --- a/mock_instrument_service_client.go +++ /dev/null @@ -1,573 +0,0 @@ -// Code generated by mockery v2.13.0-beta.1. DO NOT EDIT. - -package tnkbroker - -import ( - context "context" - - grpc "google.golang.org/grpc" - - investapi "github.com/tinkoff/invest-api-go-sdk" - - mock "github.com/stretchr/testify/mock" -) - -// mockInstrumentServiceClient is an autogenerated mock type for the instrumentServiceClient type -type mockInstrumentServiceClient struct { - mock.Mock -} - -// BondBy provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) BondBy(ctx context.Context, in *investapi.InstrumentRequest, opts ...grpc.CallOption) (*investapi.BondResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.BondResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) *investapi.BondResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.BondResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Bonds provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) Bonds(ctx context.Context, in *investapi.InstrumentsRequest, opts ...grpc.CallOption) (*investapi.BondsResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.BondsResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) *investapi.BondsResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.BondsResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Currencies provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) Currencies(ctx context.Context, in *investapi.InstrumentsRequest, opts ...grpc.CallOption) (*investapi.CurrenciesResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.CurrenciesResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) *investapi.CurrenciesResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.CurrenciesResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CurrencyBy provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) CurrencyBy(ctx context.Context, in *investapi.InstrumentRequest, opts ...grpc.CallOption) (*investapi.CurrencyResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.CurrencyResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) *investapi.CurrencyResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.CurrencyResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EtfBy provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) EtfBy(ctx context.Context, in *investapi.InstrumentRequest, opts ...grpc.CallOption) (*investapi.EtfResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.EtfResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) *investapi.EtfResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.EtfResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Etfs provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) Etfs(ctx context.Context, in *investapi.InstrumentsRequest, opts ...grpc.CallOption) (*investapi.EtfsResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.EtfsResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) *investapi.EtfsResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.EtfsResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// FutureBy provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) FutureBy(ctx context.Context, in *investapi.InstrumentRequest, opts ...grpc.CallOption) (*investapi.FutureResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.FutureResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) *investapi.FutureResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.FutureResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Futures provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) Futures(ctx context.Context, in *investapi.InstrumentsRequest, opts ...grpc.CallOption) (*investapi.FuturesResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.FuturesResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) *investapi.FuturesResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.FuturesResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAccruedInterests provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) GetAccruedInterests(ctx context.Context, in *investapi.GetAccruedInterestsRequest, opts ...grpc.CallOption) (*investapi.GetAccruedInterestsResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetAccruedInterestsResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetAccruedInterestsRequest, ...grpc.CallOption) *investapi.GetAccruedInterestsResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetAccruedInterestsResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetAccruedInterestsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAssetBy provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) GetAssetBy(ctx context.Context, in *investapi.AssetRequest, opts ...grpc.CallOption) (*investapi.AssetResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.AssetResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.AssetRequest, ...grpc.CallOption) *investapi.AssetResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.AssetResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.AssetRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetAssets provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) GetAssets(ctx context.Context, in *investapi.AssetsRequest, opts ...grpc.CallOption) (*investapi.AssetsResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.AssetsResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.AssetsRequest, ...grpc.CallOption) *investapi.AssetsResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.AssetsResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.AssetsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetBondCoupons provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) GetBondCoupons(ctx context.Context, in *investapi.GetBondCouponsRequest, opts ...grpc.CallOption) (*investapi.GetBondCouponsResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetBondCouponsResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetBondCouponsRequest, ...grpc.CallOption) *investapi.GetBondCouponsResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetBondCouponsResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetBondCouponsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDividends provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) GetDividends(ctx context.Context, in *investapi.GetDividendsRequest, opts ...grpc.CallOption) (*investapi.GetDividendsResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetDividendsResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetDividendsRequest, ...grpc.CallOption) *investapi.GetDividendsResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetDividendsResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetDividendsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetFuturesMargin provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) GetFuturesMargin(ctx context.Context, in *investapi.GetFuturesMarginRequest, opts ...grpc.CallOption) (*investapi.GetFuturesMarginResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetFuturesMarginResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetFuturesMarginRequest, ...grpc.CallOption) *investapi.GetFuturesMarginResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetFuturesMarginResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetFuturesMarginRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetInstrumentBy provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) GetInstrumentBy(ctx context.Context, in *investapi.InstrumentRequest, opts ...grpc.CallOption) (*investapi.InstrumentResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.InstrumentResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) *investapi.InstrumentResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.InstrumentResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ShareBy provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) ShareBy(ctx context.Context, in *investapi.InstrumentRequest, opts ...grpc.CallOption) (*investapi.ShareResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.ShareResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) *investapi.ShareResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.ShareResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Shares provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) Shares(ctx context.Context, in *investapi.InstrumentsRequest, opts ...grpc.CallOption) (*investapi.SharesResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.SharesResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) *investapi.SharesResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.SharesResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.InstrumentsRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// TradingSchedules provides a mock function with given fields: ctx, in, opts -func (_m *mockInstrumentServiceClient) TradingSchedules(ctx context.Context, in *investapi.TradingSchedulesRequest, opts ...grpc.CallOption) (*investapi.TradingSchedulesResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.TradingSchedulesResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.TradingSchedulesRequest, ...grpc.CallOption) *investapi.TradingSchedulesResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.TradingSchedulesResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.TradingSchedulesRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -type newMockInstrumentServiceClientT interface { - mock.TestingT - Cleanup(func()) -} - -// newMockInstrumentServiceClient creates a new instance of mockInstrumentServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func newMockInstrumentServiceClient(t newMockInstrumentServiceClientT) *mockInstrumentServiceClient { - mock := &mockInstrumentServiceClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/mock_instruments_service_client.go b/mock_instruments_service_client.go new file mode 100644 index 0000000..7a72f70 --- /dev/null +++ b/mock_instruments_service_client.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package tnkbroker + +import ( + investgo "github.com/russianinvestments/invest-api-go-sdk/investgo" + mock "github.com/stretchr/testify/mock" +) + +// mockInstrumentsServiceClient is an autogenerated mock type for the instrumentsServiceClient type +type mockInstrumentsServiceClient struct { + mock.Mock +} + +// InstrumentByFigi provides a mock function with given fields: id +func (_m *mockInstrumentsServiceClient) InstrumentByFigi(id string) (*investgo.InstrumentResponse, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for InstrumentByFigi") + } + + var r0 *investgo.InstrumentResponse + var r1 error + if rf, ok := ret.Get(0).(func(string) (*investgo.InstrumentResponse, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) *investgo.InstrumentResponse); ok { + r0 = rf(id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*investgo.InstrumentResponse) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// newMockInstrumentsServiceClient creates a new instance of mockInstrumentsServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockInstrumentsServiceClient(t interface { + mock.TestingT + Cleanup(func()) +}) *mockInstrumentsServiceClient { + mock := &mockInstrumentsServiceClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mock_market_data_service_client.go b/mock_market_data_service_client.go index 44c44f4..5ef4aa6 100644 --- a/mock_market_data_service_client.go +++ b/mock_market_data_service_client.go @@ -1,14 +1,9 @@ -// Code generated by mockery v2.13.0-beta.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package tnkbroker import ( - context "context" - - grpc "google.golang.org/grpc" - - investapi "github.com/tinkoff/invest-api-go-sdk" - + investgo "github.com/russianinvestments/invest-api-go-sdk/investgo" mock "github.com/stretchr/testify/mock" ) @@ -17,59 +12,29 @@ type mockMarketDataServiceClient struct { mock.Mock } -// GetCandles provides a mock function with given fields: ctx, in, opts -func (_m *mockMarketDataServiceClient) GetCandles(ctx context.Context, in *investapi.GetCandlesRequest, opts ...grpc.CallOption) (*investapi.GetCandlesResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) +// GetLastPrices provides a mock function with given fields: instrumentIds +func (_m *mockMarketDataServiceClient) GetLastPrices(instrumentIds []string) (*investgo.GetLastPricesResponse, error) { + ret := _m.Called(instrumentIds) - var r0 *investapi.GetCandlesResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetCandlesRequest, ...grpc.CallOption) *investapi.GetCandlesResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetCandlesResponse) - } + if len(ret) == 0 { + panic("no return value specified for GetLastPrices") } + var r0 *investgo.GetLastPricesResponse var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetCandlesRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) + if rf, ok := ret.Get(0).(func([]string) (*investgo.GetLastPricesResponse, error)); ok { + return rf(instrumentIds) } - - return r0, r1 -} - -// GetLastPrices provides a mock function with given fields: ctx, in, opts -func (_m *mockMarketDataServiceClient) GetLastPrices(ctx context.Context, in *investapi.GetLastPricesRequest, opts ...grpc.CallOption) (*investapi.GetLastPricesResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetLastPricesResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetLastPricesRequest, ...grpc.CallOption) *investapi.GetLastPricesResponse); ok { - r0 = rf(ctx, in, opts...) + if rf, ok := ret.Get(0).(func([]string) *investgo.GetLastPricesResponse); ok { + r0 = rf(instrumentIds) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetLastPricesResponse) + r0 = ret.Get(0).(*investgo.GetLastPricesResponse) } } - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetLastPricesRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) + if rf, ok := ret.Get(1).(func([]string) error); ok { + r1 = rf(instrumentIds) } else { r1 = ret.Error(1) } @@ -77,103 +42,12 @@ func (_m *mockMarketDataServiceClient) GetLastPrices(ctx context.Context, in *in return r0, r1 } -// GetLastTrades provides a mock function with given fields: ctx, in, opts -func (_m *mockMarketDataServiceClient) GetLastTrades(ctx context.Context, in *investapi.GetLastTradesRequest, opts ...grpc.CallOption) (*investapi.GetLastTradesResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetLastTradesResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetLastTradesRequest, ...grpc.CallOption) *investapi.GetLastTradesResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetLastTradesResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetLastTradesRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetOrderBook provides a mock function with given fields: ctx, in, opts -func (_m *mockMarketDataServiceClient) GetOrderBook(ctx context.Context, in *investapi.GetOrderBookRequest, opts ...grpc.CallOption) (*investapi.GetOrderBookResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetOrderBookResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetOrderBookRequest, ...grpc.CallOption) *investapi.GetOrderBookResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetOrderBookResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetOrderBookRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetTradingStatus provides a mock function with given fields: ctx, in, opts -func (_m *mockMarketDataServiceClient) GetTradingStatus(ctx context.Context, in *investapi.GetTradingStatusRequest, opts ...grpc.CallOption) (*investapi.GetTradingStatusResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetTradingStatusResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetTradingStatusRequest, ...grpc.CallOption) *investapi.GetTradingStatusResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetTradingStatusResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetTradingStatusRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -type newMockMarketDataServiceClientT interface { +// newMockMarketDataServiceClient creates a new instance of mockMarketDataServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockMarketDataServiceClient(t interface { mock.TestingT Cleanup(func()) -} - -// newMockMarketDataServiceClient creates a new instance of mockMarketDataServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func newMockMarketDataServiceClient(t newMockMarketDataServiceClientT) *mockMarketDataServiceClient { +}) *mockMarketDataServiceClient { mock := &mockMarketDataServiceClient{} mock.Mock.Test(t) diff --git a/mock_orders_service_client.go b/mock_orders_service_client.go index a4ee994..2e567b3 100644 --- a/mock_orders_service_client.go +++ b/mock_orders_service_client.go @@ -1,13 +1,10 @@ -// Code generated by mockery v2.13.0-beta.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package tnkbroker import ( - context "context" - - grpc "google.golang.org/grpc" - - investapi "github.com/tinkoff/invest-api-go-sdk" + investgo "github.com/russianinvestments/invest-api-go-sdk/investgo" + investapi "github.com/russianinvestments/invest-api-go-sdk/proto" mock "github.com/stretchr/testify/mock" ) @@ -17,59 +14,29 @@ type mockOrdersServiceClient struct { mock.Mock } -// CancelOrder provides a mock function with given fields: ctx, in, opts -func (_m *mockOrdersServiceClient) CancelOrder(ctx context.Context, in *investapi.CancelOrderRequest, opts ...grpc.CallOption) (*investapi.CancelOrderResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.CancelOrderResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.CancelOrderRequest, ...grpc.CallOption) *investapi.CancelOrderResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.CancelOrderResponse) - } - } +// GetOrderState provides a mock function with given fields: accountId, orderId, priceType +func (_m *mockOrdersServiceClient) GetOrderState(accountId string, orderId string, priceType investapi.PriceType) (*investgo.GetOrderStateResponse, error) { + ret := _m.Called(accountId, orderId, priceType) - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.CancelOrderRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) + if len(ret) == 0 { + panic("no return value specified for GetOrderState") } - return r0, r1 -} - -// GetOrderState provides a mock function with given fields: ctx, in, opts -func (_m *mockOrdersServiceClient) GetOrderState(ctx context.Context, in *investapi.GetOrderStateRequest, opts ...grpc.CallOption) (*investapi.OrderState, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] + var r0 *investgo.GetOrderStateResponse + var r1 error + if rf, ok := ret.Get(0).(func(string, string, investapi.PriceType) (*investgo.GetOrderStateResponse, error)); ok { + return rf(accountId, orderId, priceType) } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.OrderState - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetOrderStateRequest, ...grpc.CallOption) *investapi.OrderState); ok { - r0 = rf(ctx, in, opts...) + if rf, ok := ret.Get(0).(func(string, string, investapi.PriceType) *investgo.GetOrderStateResponse); ok { + r0 = rf(accountId, orderId, priceType) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.OrderState) + r0 = ret.Get(0).(*investgo.GetOrderStateResponse) } } - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetOrderStateRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) + if rf, ok := ret.Get(1).(func(string, string, investapi.PriceType) error); ok { + r1 = rf(accountId, orderId, priceType) } else { r1 = ret.Error(1) } @@ -77,59 +44,29 @@ func (_m *mockOrdersServiceClient) GetOrderState(ctx context.Context, in *invest return r0, r1 } -// GetOrders provides a mock function with given fields: ctx, in, opts -func (_m *mockOrdersServiceClient) GetOrders(ctx context.Context, in *investapi.GetOrdersRequest, opts ...grpc.CallOption) (*investapi.GetOrdersResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetOrdersResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetOrdersRequest, ...grpc.CallOption) *investapi.GetOrdersResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetOrdersResponse) - } - } +// PostOrder provides a mock function with given fields: req +func (_m *mockOrdersServiceClient) PostOrder(req *investgo.PostOrderRequest) (*investgo.PostOrderResponse, error) { + ret := _m.Called(req) - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetOrdersRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) + if len(ret) == 0 { + panic("no return value specified for PostOrder") } - return r0, r1 -} - -// PostOrder provides a mock function with given fields: ctx, in, opts -func (_m *mockOrdersServiceClient) PostOrder(ctx context.Context, in *investapi.PostOrderRequest, opts ...grpc.CallOption) (*investapi.PostOrderResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] + var r0 *investgo.PostOrderResponse + var r1 error + if rf, ok := ret.Get(0).(func(*investgo.PostOrderRequest) (*investgo.PostOrderResponse, error)); ok { + return rf(req) } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.PostOrderResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.PostOrderRequest, ...grpc.CallOption) *investapi.PostOrderResponse); ok { - r0 = rf(ctx, in, opts...) + if rf, ok := ret.Get(0).(func(*investgo.PostOrderRequest) *investgo.PostOrderResponse); ok { + r0 = rf(req) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.PostOrderResponse) + r0 = ret.Get(0).(*investgo.PostOrderResponse) } } - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.PostOrderRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) + if rf, ok := ret.Get(1).(func(*investgo.PostOrderRequest) error); ok { + r1 = rf(req) } else { r1 = ret.Error(1) } @@ -137,13 +74,12 @@ func (_m *mockOrdersServiceClient) PostOrder(ctx context.Context, in *investapi. return r0, r1 } -type newMockOrdersServiceClientT interface { +// newMockOrdersServiceClient creates a new instance of mockOrdersServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockOrdersServiceClient(t interface { mock.TestingT Cleanup(func()) -} - -// newMockOrdersServiceClient creates a new instance of mockOrdersServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func newMockOrdersServiceClient(t newMockOrdersServiceClientT) *mockOrdersServiceClient { +}) *mockOrdersServiceClient { mock := &mockOrdersServiceClient{} mock.Mock.Test(t) diff --git a/mock_orders_stream_client.go b/mock_orders_stream_client.go new file mode 100644 index 0000000..4a6a55e --- /dev/null +++ b/mock_orders_stream_client.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package tnkbroker + +import ( + investgo "github.com/russianinvestments/invest-api-go-sdk/investgo" + mock "github.com/stretchr/testify/mock" +) + +// mockOrdersStreamClient is an autogenerated mock type for the ordersStreamClient type +type mockOrdersStreamClient struct { + mock.Mock +} + +// TradesStream provides a mock function with given fields: accounts +func (_m *mockOrdersStreamClient) TradesStream(accounts []string) (*investgo.TradesStream, error) { + ret := _m.Called(accounts) + + if len(ret) == 0 { + panic("no return value specified for TradesStream") + } + + var r0 *investgo.TradesStream + var r1 error + if rf, ok := ret.Get(0).(func([]string) (*investgo.TradesStream, error)); ok { + return rf(accounts) + } + if rf, ok := ret.Get(0).(func([]string) *investgo.TradesStream); ok { + r0 = rf(accounts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*investgo.TradesStream) + } + } + + if rf, ok := ret.Get(1).(func([]string) error); ok { + r1 = rf(accounts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// newMockOrdersStreamClient creates a new instance of mockOrdersStreamClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockOrdersStreamClient(t interface { + mock.TestingT + Cleanup(func()) +}) *mockOrdersStreamClient { + mock := &mockOrdersStreamClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mock_stop_orders_service_client.go b/mock_stop_orders_service_client.go index 6720eed..bb0a252 100644 --- a/mock_stop_orders_service_client.go +++ b/mock_stop_orders_service_client.go @@ -1,14 +1,9 @@ -// Code generated by mockery v2.13.0-beta.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package tnkbroker import ( - context "context" - - grpc "google.golang.org/grpc" - - investapi "github.com/tinkoff/invest-api-go-sdk" - + investgo "github.com/russianinvestments/invest-api-go-sdk/investgo" mock "github.com/stretchr/testify/mock" ) @@ -17,29 +12,29 @@ type mockStopOrdersServiceClient struct { mock.Mock } -// CancelStopOrder provides a mock function with given fields: ctx, in, opts -func (_m *mockStopOrdersServiceClient) CancelStopOrder(ctx context.Context, in *investapi.CancelStopOrderRequest, opts ...grpc.CallOption) (*investapi.CancelStopOrderResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] +// CancelStopOrder provides a mock function with given fields: accountId, stopOrderId +func (_m *mockStopOrdersServiceClient) CancelStopOrder(accountId string, stopOrderId string) (*investgo.CancelStopOrderResponse, error) { + ret := _m.Called(accountId, stopOrderId) + + if len(ret) == 0 { + panic("no return value specified for CancelStopOrder") } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.CancelStopOrderResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.CancelStopOrderRequest, ...grpc.CallOption) *investapi.CancelStopOrderResponse); ok { - r0 = rf(ctx, in, opts...) + + var r0 *investgo.CancelStopOrderResponse + var r1 error + if rf, ok := ret.Get(0).(func(string, string) (*investgo.CancelStopOrderResponse, error)); ok { + return rf(accountId, stopOrderId) + } + if rf, ok := ret.Get(0).(func(string, string) *investgo.CancelStopOrderResponse); ok { + r0 = rf(accountId, stopOrderId) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.CancelStopOrderResponse) + r0 = ret.Get(0).(*investgo.CancelStopOrderResponse) } } - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.CancelStopOrderRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(accountId, stopOrderId) } else { r1 = ret.Error(1) } @@ -47,29 +42,29 @@ func (_m *mockStopOrdersServiceClient) CancelStopOrder(ctx context.Context, in * return r0, r1 } -// GetStopOrders provides a mock function with given fields: ctx, in, opts -func (_m *mockStopOrdersServiceClient) GetStopOrders(ctx context.Context, in *investapi.GetStopOrdersRequest, opts ...grpc.CallOption) (*investapi.GetStopOrdersResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] +// GetStopOrders provides a mock function with given fields: accountId +func (_m *mockStopOrdersServiceClient) GetStopOrders(accountId string) (*investgo.GetStopOrdersResponse, error) { + ret := _m.Called(accountId) + + if len(ret) == 0 { + panic("no return value specified for GetStopOrders") + } + + var r0 *investgo.GetStopOrdersResponse + var r1 error + if rf, ok := ret.Get(0).(func(string) (*investgo.GetStopOrdersResponse, error)); ok { + return rf(accountId) } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.GetStopOrdersResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.GetStopOrdersRequest, ...grpc.CallOption) *investapi.GetStopOrdersResponse); ok { - r0 = rf(ctx, in, opts...) + if rf, ok := ret.Get(0).(func(string) *investgo.GetStopOrdersResponse); ok { + r0 = rf(accountId) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.GetStopOrdersResponse) + r0 = ret.Get(0).(*investgo.GetStopOrdersResponse) } } - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.GetStopOrdersRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(accountId) } else { r1 = ret.Error(1) } @@ -77,29 +72,29 @@ func (_m *mockStopOrdersServiceClient) GetStopOrders(ctx context.Context, in *in return r0, r1 } -// PostStopOrder provides a mock function with given fields: ctx, in, opts -func (_m *mockStopOrdersServiceClient) PostStopOrder(ctx context.Context, in *investapi.PostStopOrderRequest, opts ...grpc.CallOption) (*investapi.PostStopOrderResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] +// PostStopOrder provides a mock function with given fields: req +func (_m *mockStopOrdersServiceClient) PostStopOrder(req *investgo.PostStopOrderRequest) (*investgo.PostStopOrderResponse, error) { + ret := _m.Called(req) + + if len(ret) == 0 { + panic("no return value specified for PostStopOrder") + } + + var r0 *investgo.PostStopOrderResponse + var r1 error + if rf, ok := ret.Get(0).(func(*investgo.PostStopOrderRequest) (*investgo.PostStopOrderResponse, error)); ok { + return rf(req) } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *investapi.PostStopOrderResponse - if rf, ok := ret.Get(0).(func(context.Context, *investapi.PostStopOrderRequest, ...grpc.CallOption) *investapi.PostStopOrderResponse); ok { - r0 = rf(ctx, in, opts...) + if rf, ok := ret.Get(0).(func(*investgo.PostStopOrderRequest) *investgo.PostStopOrderResponse); ok { + r0 = rf(req) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*investapi.PostStopOrderResponse) + r0 = ret.Get(0).(*investgo.PostStopOrderResponse) } } - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *investapi.PostStopOrderRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) + if rf, ok := ret.Get(1).(func(*investgo.PostStopOrderRequest) error); ok { + r1 = rf(req) } else { r1 = ret.Error(1) } @@ -107,13 +102,12 @@ func (_m *mockStopOrdersServiceClient) PostStopOrder(ctx context.Context, in *in return r0, r1 } -type newMockStopOrdersServiceClientT interface { +// newMockStopOrdersServiceClient creates a new instance of mockStopOrdersServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockStopOrdersServiceClient(t interface { mock.TestingT Cleanup(func()) -} - -// newMockStopOrdersServiceClient creates a new instance of mockStopOrdersServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func newMockStopOrdersServiceClient(t newMockStopOrdersServiceClientT) *mockStopOrdersServiceClient { +}) *mockStopOrdersServiceClient { mock := &mockStopOrdersServiceClient{} mock.Mock.Test(t) diff --git a/tinkoff.go b/tinkoff.go index c8cc765..9deb714 100644 --- a/tinkoff.go +++ b/tinkoff.go @@ -7,31 +7,24 @@ package tnkbroker import ( "context" - "crypto/tls" "errors" "fmt" - "io" "math" "time" "github.com/evsamsonov/trengin/v2" "github.com/google/uuid" - investapi "github.com/tinkoff/invest-api-go-sdk" + "github.com/russianinvestments/invest-api-go-sdk/investgo" + pb "github.com/russianinvestments/invest-api-go-sdk/proto" "go.uber.org/zap" "golang.org/x/sync/errgroup" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "github.com/evsamsonov/tinkoff-broker/internal/tnkposition" + "github.com/evsamsonov/tinkoff-broker/v2/internal/tnkposition" ) var _ trengin.Broker = &Tinkoff{} const ( - tinkoffHost = "invest-public-api.tinkoff.ru:443" defaultProtectiveSpread = 1 // In percent defaultTradeStreamRetryTimeout = 1 * time.Minute defaultTradeStreamPingTimeout = 6 * time.Minute @@ -39,13 +32,11 @@ const ( type Tinkoff struct { accountID string - token string - appName string - orderClient investapi.OrdersServiceClient - stopOrderClient investapi.StopOrdersServiceClient - tradeStreamClient investapi.OrdersStreamServiceClient - marketDataClient investapi.MarketDataServiceClient - instrumentClient investapi.InstrumentsServiceClient + orderClient ordersServiceClient + stopOrderClient stopOrdersServiceClient + tradeStreamClient ordersStreamClient + marketDataClient marketDataServiceClient + instrumentClient instrumentsServiceClient tradeStreamRetryTimeout time.Duration tradeStreamPingWaitDuration time.Duration protectiveSpread float64 @@ -62,15 +53,6 @@ func WithLogger(logger *zap.Logger) Option { } } -// WithAppName returns Option which sets [x-app-name] -// -// [x-app-name]: https://tinkoff.github.io/investAPI/grpc/#appname -func WithAppName(appName string) Option { - return func(t *Tinkoff) { - t.appName = appName - } -} - // WithProtectiveSpread returns Option which sets protective spread // in percent for executing orders. The default value is 1% func WithProtectiveSpread(protectiveSpread float64) Option { @@ -95,33 +77,17 @@ func WithTradeStreamPingWaitDuration(duration time.Duration) Option { } } -// New creates a new Tinkoff object. It takes [full-access token], +// New creates a new Tinkoff object. It takes Tinkoff Client and // user account identifier. -// -// [full-access token]: https://tinkoff.github.io/investAPI/token/ -func New(token, accountID string, opts ...Option) (*Tinkoff, error) { - conn, err := grpc.Dial( - tinkoffHost, - grpc.WithTransportCredentials( - credentials.NewTLS(&tls.Config{ - InsecureSkipVerify: true, //nolint: gosec - }), - ), - grpc.WithBlock(), - ) - if err != nil { - return nil, fmt.Errorf("grpc dial: %w", err) - } - +func New(client *investgo.Client, accountID string, opts ...Option) (*Tinkoff, error) { tinkoff := &Tinkoff{ accountID: accountID, - token: token, protectiveSpread: defaultProtectiveSpread, - orderClient: investapi.NewOrdersServiceClient(conn), - stopOrderClient: investapi.NewStopOrdersServiceClient(conn), - tradeStreamClient: investapi.NewOrdersStreamServiceClient(conn), - marketDataClient: investapi.NewMarketDataServiceClient(conn), - instrumentClient: investapi.NewInstrumentsServiceClient(conn), + orderClient: client.NewOrdersServiceClient(), + stopOrderClient: client.NewStopOrdersServiceClient(), + tradeStreamClient: client.NewOrdersStreamClient(), + marketDataClient: client.NewMarketDataServiceClient(), + instrumentClient: client.NewInstrumentsServiceClient(), tradeStreamRetryTimeout: defaultTradeStreamRetryTimeout, tradeStreamPingWaitDuration: defaultTradeStreamPingTimeout, positionStorage: tnkposition.NewStorage(), @@ -159,8 +125,7 @@ func (t *Tinkoff) OpenPosition( ctx context.Context, action trengin.OpenPositionAction, ) (trengin.Position, trengin.PositionClosed, error) { - ctx = t.ctxWithMetadata(ctx) - instrument, err := t.getInstrument(ctx, action.FIGI) + instrument, err := t.getInstrument(action.FIGI) if err != nil { return trengin.Position{}, nil, fmt.Errorf("get instrument: %w", err) } @@ -178,14 +143,14 @@ func (t *Tinkoff) OpenPosition( var stopLossID, takeProfitID string if action.StopLossOffset != 0 { stopLoss := t.stopLossPriceByOpen(openPrice, action, instrument.MinPriceIncrement) - stopLossID, err = t.setStopLoss(ctx, instrument, stopLoss, *position) + stopLossID, err = t.setStopLoss(instrument, stopLoss, *position) if err != nil { return trengin.Position{}, nil, fmt.Errorf("set stop order: %w", err) } } if action.TakeProfitOffset != 0 { takeProfit := t.takeProfitPriceByOpen(openPrice, action, instrument.MinPriceIncrement) - takeProfitID, err = t.setTakeProfit(ctx, instrument, takeProfit, *position) + takeProfitID, err = t.setTakeProfit(instrument, takeProfit, *position) if err != nil { return trengin.Position{}, nil, fmt.Errorf("set stop order: %w", err) } @@ -200,7 +165,7 @@ func (t *Tinkoff) OpenPosition( // ChangeConditionalOrder changes stop loss and take profit of current position. // It returns updated position. func (t *Tinkoff) ChangeConditionalOrder( - ctx context.Context, + _ context.Context, action trengin.ChangeConditionalOrderAction, ) (trengin.Position, error) { tinkoffPosition, unlockPosition, err := t.positionStorage.Load(action.PositionID) @@ -209,15 +174,14 @@ func (t *Tinkoff) ChangeConditionalOrder( } defer unlockPosition() - ctx = t.ctxWithMetadata(ctx) instrument := tinkoffPosition.Instrument() if action.StopLoss != 0 { - if err := t.cancelStopOrder(ctx, tinkoffPosition.StopLossID()); err != nil { + if err := t.cancelStopOrder(tinkoffPosition.StopLossID()); err != nil { return trengin.Position{}, fmt.Errorf("cancel stop loss: %w", err) } stopLoss := t.convertFloatToQuotation(instrument.MinPriceIncrement, action.StopLoss) - stopLossID, err := t.setStopLoss(ctx, instrument, stopLoss, tinkoffPosition.Position()) + stopLossID, err := t.setStopLoss(instrument, stopLoss, tinkoffPosition.Position()) if err != nil { return trengin.Position{}, fmt.Errorf("set stop loss: %w", err) } @@ -225,12 +189,12 @@ func (t *Tinkoff) ChangeConditionalOrder( } if action.TakeProfit != 0 { - if err := t.cancelStopOrder(ctx, tinkoffPosition.TakeProfitID()); err != nil { + if err := t.cancelStopOrder(tinkoffPosition.TakeProfitID()); err != nil { return trengin.Position{}, fmt.Errorf("cancel take profit: %w", err) } takeProfit := t.convertFloatToQuotation(instrument.MinPriceIncrement, action.TakeProfit) - takeProfitID, err := t.setTakeProfit(ctx, instrument, takeProfit, tinkoffPosition.Position()) + takeProfitID, err := t.setTakeProfit(instrument, takeProfit, tinkoffPosition.Position()) if err != nil { return trengin.Position{}, fmt.Errorf("set take profit: %w", err) } @@ -247,11 +211,10 @@ func (t *Tinkoff) ClosePosition(ctx context.Context, action trengin.ClosePositio } defer unlockPosition() - ctx = t.ctxWithMetadata(ctx) - if err := t.cancelStopOrder(ctx, tinkoffPosition.StopLossID()); err != nil { + if err := t.cancelStopOrder(tinkoffPosition.StopLossID()); err != nil { return trengin.Position{}, fmt.Errorf("cancel stop loss: %w", err) } - if err := t.cancelStopOrder(ctx, tinkoffPosition.TakeProfitID()); err != nil { + if err := t.cancelStopOrder(tinkoffPosition.TakeProfitID()); err != nil { return trengin.Position{}, fmt.Errorf("cancel take profit: %w", err) } @@ -275,68 +238,44 @@ func (t *Tinkoff) readTradesStream(ctx context.Context) error { ctx, cancel := context.WithCancel(ctx) defer cancel() - ctx = t.ctxWithMetadata(ctx) - stream, err := t.tradeStreamClient.TradesStream(ctx, &investapi.TradesStreamRequest{}) + tradesStream, err := t.tradeStreamClient.TradesStream([]string{t.accountID}) if err != nil { return fmt.Errorf("trades stream: %w", err) } g := errgroup.Group{} - heartbeat := make(chan struct{}) g.Go(func() error { + t.logger.Debug("Trades stream is connected") + + trades := tradesStream.Trades() for { select { case <-ctx.Done(): return nil - case <-heartbeat: - continue - case <-time.After(t.tradeStreamPingWaitDuration): - cancel() - return fmt.Errorf("ping timed out") - } - } - }) - - g.Go(func() error { - t.logger.Debug("Trades stream is connected") - for { - resp, err := stream.Recv() - if err != nil { - if err == io.EOF { - t.logger.Info("Trades stream connection is closed") - break - } - if status.Code(err) == codes.Canceled { - t.logger.Info("Trades stream connection is canceled") - break + case orderTrades, ok := <-trades: + if !ok { + return nil } - return fmt.Errorf("stream recv: %w", err) - } - - switch v := resp.Payload.(type) { - case *investapi.TradesStreamResponse_Ping: - t.logger.Debug("Trade stream ping was received", zap.Any("ping", v)) + t.logger.Info("Order trades were received", zap.Any("orderTrades", orderTrades)) - select { - case heartbeat <- struct{}{}: - default: - } - case *investapi.TradesStreamResponse_OrderTrades: - t.logger.Info("Order trades were received", zap.Any("orderTrades", v)) - - if err := t.processOrderTrades(ctx, v.OrderTrades); err != nil { + if err := t.processOrderTrades(ctx, orderTrades); err != nil { return fmt.Errorf("process order trades: %w", err) } - default: - return errors.New("unexpected payload") } } + }) + + g.Go(func() error { + if err := tradesStream.Listen(); err != nil { + return fmt.Errorf("listen: %w", err) + } return nil }) + return g.Wait() } -func (t *Tinkoff) processOrderTrades(ctx context.Context, orderTrades *investapi.OrderTrades) error { +func (t *Tinkoff) processOrderTrades(ctx context.Context, orderTrades *pb.OrderTrades) error { if orderTrades.AccountId != t.accountID { return nil } @@ -345,13 +284,13 @@ func (t *Tinkoff) processOrderTrades(ctx context.Context, orderTrades *investapi if orderTrades.Figi != position.FIGI { return nil } - longClosed := position.IsLong() && orderTrades.Direction == investapi.OrderDirection_ORDER_DIRECTION_SELL - shortClosed := position.IsShort() && orderTrades.Direction == investapi.OrderDirection_ORDER_DIRECTION_BUY + longClosed := position.IsLong() && orderTrades.Direction == pb.OrderDirection_ORDER_DIRECTION_SELL + shortClosed := position.IsShort() && orderTrades.Direction == pb.OrderDirection_ORDER_DIRECTION_BUY if !longClosed && !shortClosed { return nil } - conditionalOrdersFound, err := t.conditionalOrdersFound(ctx, tinkoffPosition) + conditionalOrdersFound, err := t.conditionalOrdersFound(tinkoffPosition) if err != nil { return fmt.Errorf("conditional orders found: %w", err) } @@ -371,7 +310,7 @@ func (t *Tinkoff) processOrderTrades(ctx context.Context, orderTrades *investapi return nil } - if err := t.cancelStopOrders(ctx, tinkoffPosition); err != nil { + if err := t.cancelStopOrders(tinkoffPosition); err != nil { return fmt.Errorf("cancel stop orders: %w", err) } @@ -397,39 +336,31 @@ func (t *Tinkoff) processOrderTrades(ctx context.Context, orderTrades *investapi return err } -func (t *Tinkoff) ctxWithMetadata(ctx context.Context) context.Context { - md := metadata.New(map[string]string{ - "Authorization": "Bearer " + t.token, - "x-app-name": t.appName, - }) - return metadata.NewOutgoingContext(ctx, md) -} - func (t *Tinkoff) openMarketOrder( ctx context.Context, - instrument *investapi.Instrument, + instrument *pb.Instrument, positionType trengin.PositionType, quantity int64, ) (*MoneyValue, *MoneyValue, error) { - direction := investapi.OrderDirection_ORDER_DIRECTION_BUY + direction := pb.OrderDirection_ORDER_DIRECTION_BUY if positionType.IsShort() { - direction = investapi.OrderDirection_ORDER_DIRECTION_SELL + direction = pb.OrderDirection_ORDER_DIRECTION_SELL } - price, err := t.getLastPrice(ctx, instrument.Figi) + price, err := t.getLastPrice(instrument.Figi) if err != nil { return nil, nil, fmt.Errorf("get last price: %w", err) } - orderRequest := &investapi.PostOrderRequest{ - Figi: instrument.Figi, - Quantity: quantity, - Direction: direction, - AccountId: t.accountID, - Price: t.addProtectedSpread(positionType.Inverse(), price, instrument.MinPriceIncrement), - OrderType: investapi.OrderType_ORDER_TYPE_LIMIT, - OrderId: uuid.New().String(), + orderRequest := &investgo.PostOrderRequest{ + InstrumentId: instrument.Figi, + Quantity: quantity, + Direction: direction, + AccountId: t.accountID, + Price: t.addProtectedSpread(positionType.Inverse(), price, instrument.MinPriceIncrement), + OrderType: pb.OrderType_ORDER_TYPE_LIMIT, + OrderId: uuid.New().String(), } - order, err := t.orderClient.PostOrder(ctx, orderRequest) + order, err := t.orderClient.PostOrder(orderRequest) if err != nil { t.logger.Error("Failed to execute order", zap.Error(err), zap.Any("orderRequest", orderRequest)) return nil, nil, fmt.Errorf("post order: %w", err) @@ -455,52 +386,49 @@ const ( ) func (t *Tinkoff) setStopLoss( - ctx context.Context, - instrument *investapi.Instrument, - price *investapi.Quotation, + instrument *pb.Instrument, + price *pb.Quotation, position trengin.Position, ) (string, error) { - return t.setStopOrder(ctx, instrument, price, position, stopLossStopOrderType) + return t.setStopOrder(instrument, price, position, stopLossStopOrderType) } func (t *Tinkoff) setTakeProfit( - ctx context.Context, - instrument *investapi.Instrument, - price *investapi.Quotation, + instrument *pb.Instrument, + price *pb.Quotation, position trengin.Position, ) (string, error) { - return t.setStopOrder(ctx, instrument, price, position, takeProfitStopOrderType) + return t.setStopOrder(instrument, price, position, takeProfitStopOrderType) } func (t *Tinkoff) setStopOrder( - ctx context.Context, - instrument *investapi.Instrument, - stopPrice *investapi.Quotation, + instrument *pb.Instrument, + stopPrice *pb.Quotation, position trengin.Position, orderType stopOrderType, ) (string, error) { - stopOrderDirection := investapi.StopOrderDirection_STOP_ORDER_DIRECTION_BUY + stopOrderDirection := pb.StopOrderDirection_STOP_ORDER_DIRECTION_BUY if position.Type.IsLong() { - stopOrderDirection = investapi.StopOrderDirection_STOP_ORDER_DIRECTION_SELL + stopOrderDirection = pb.StopOrderDirection_STOP_ORDER_DIRECTION_SELL } - reqStopOrderType := investapi.StopOrderType_STOP_ORDER_TYPE_STOP_LIMIT + reqStopOrderType := pb.StopOrderType_STOP_ORDER_TYPE_STOP_LIMIT if orderType == takeProfitStopOrderType { - reqStopOrderType = investapi.StopOrderType_STOP_ORDER_TYPE_TAKE_PROFIT + reqStopOrderType = pb.StopOrderType_STOP_ORDER_TYPE_TAKE_PROFIT } price := t.addProtectedSpread(position.Type, stopPrice, instrument.MinPriceIncrement) - stopOrderRequest := &investapi.PostStopOrderRequest{ - Figi: position.FIGI, + stopOrderRequest := &investgo.PostStopOrderRequest{ + InstrumentId: position.FIGI, Quantity: position.Quantity, Price: price, StopPrice: stopPrice, Direction: stopOrderDirection, AccountId: t.accountID, - ExpirationType: investapi.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, + ExpirationType: pb.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, StopOrderType: reqStopOrderType, } - stopOrder, err := t.stopOrderClient.PostStopOrder(ctx, stopOrderRequest) + stopOrder, err := t.stopOrderClient.PostStopOrder(stopOrderRequest) if err != nil { t.logger.Info( "Failed to set stop order", @@ -518,17 +446,13 @@ func (t *Tinkoff) setStopOrder( return stopOrder.StopOrderId, nil } -func (t *Tinkoff) cancelStopOrder(ctx context.Context, id string) error { +func (t *Tinkoff) cancelStopOrder(id string) error { if id == "" { return nil } - cancelStopOrderRequest := &investapi.CancelStopOrderRequest{ - AccountId: t.accountID, - StopOrderId: id, - } - logger := t.logger.With(zap.Any("cancelStopOrderRequest", cancelStopOrderRequest)) - if _, err := t.stopOrderClient.CancelStopOrder(ctx, cancelStopOrderRequest); err != nil { + logger := t.logger.With(zap.String("id", id)) + if _, err := t.stopOrderClient.CancelStopOrder(t.accountID, id); err != nil { return fmt.Errorf("cancel stop order: %w", err) } @@ -539,8 +463,8 @@ func (t *Tinkoff) cancelStopOrder(ctx context.Context, id string) error { func (t *Tinkoff) stopLossPriceByOpen( openPrice *MoneyValue, action trengin.OpenPositionAction, - minPriceIncrement *investapi.Quotation, -) *investapi.Quotation { + minPriceIncrement *pb.Quotation, +) *pb.Quotation { stopLoss := openPrice.ToFloat() - action.StopLossOffset*action.Type.Multiplier() return t.convertFloatToQuotation(minPriceIncrement, stopLoss) } @@ -548,16 +472,16 @@ func (t *Tinkoff) stopLossPriceByOpen( func (t *Tinkoff) takeProfitPriceByOpen( openPrice *MoneyValue, action trengin.OpenPositionAction, - minPriceIncrement *investapi.Quotation, -) *investapi.Quotation { + minPriceIncrement *pb.Quotation, +) *pb.Quotation { takeProfit := openPrice.ToFloat() + action.TakeProfitOffset*action.Type.Multiplier() return t.convertFloatToQuotation(minPriceIncrement, takeProfit) } func (t *Tinkoff) convertFloatToQuotation( - minPriceIncrement *investapi.Quotation, + minPriceIncrement *pb.Quotation, stopLoss float64, -) *investapi.Quotation { +) *pb.Quotation { stopOrderUnits, stopOrderNano := math.Modf(stopLoss) var roundStopOrderNano int32 @@ -565,7 +489,7 @@ func (t *Tinkoff) convertFloatToQuotation( roundStopOrderNano = int32(math.Round(stopOrderNano*10e8/float64(minPriceIncrement.GetNano()))) * minPriceIncrement.GetNano() } - return &investapi.Quotation{ + return &pb.Quotation{ Units: int64(stopOrderUnits), Nano: roundStopOrderNano, } @@ -573,20 +497,16 @@ func (t *Tinkoff) convertFloatToQuotation( func (t *Tinkoff) addProtectedSpread( positionType trengin.PositionType, - price *investapi.Quotation, - minPriceIncrement *investapi.Quotation, -) *investapi.Quotation { + price *pb.Quotation, + minPriceIncrement *pb.Quotation, +) *pb.Quotation { priceFloat := NewMoneyValue(price).ToFloat() protectiveSpread := priceFloat * t.protectiveSpread / 100 return t.convertFloatToQuotation(minPriceIncrement, priceFloat-positionType.Multiplier()*protectiveSpread) } -func (t *Tinkoff) cancelStopOrders(ctx context.Context, tinkoffPosition *tnkposition.Position) error { - ctx = t.ctxWithMetadata(ctx) - - resp, err := t.stopOrderClient.GetStopOrders(ctx, &investapi.GetStopOrdersRequest{ - AccountId: t.accountID, - }) +func (t *Tinkoff) cancelStopOrders(tinkoffPosition *tnkposition.Position) error { + resp, err := t.stopOrderClient.GetStopOrders(t.accountID) if err != nil { return err } @@ -599,22 +519,20 @@ func (t *Tinkoff) cancelStopOrders(ctx context.Context, tinkoffPosition *tnkposi stopLossID := tinkoffPosition.StopLossID() takeProfitID := tinkoffPosition.TakeProfitID() if _, ok := orders[stopLossID]; ok { - if err := t.cancelStopOrder(ctx, stopLossID); err != nil { + if err := t.cancelStopOrder(stopLossID); err != nil { return fmt.Errorf("cancel stop loss: %w", err) } } if _, ok := orders[takeProfitID]; ok { - if err := t.cancelStopOrder(ctx, takeProfitID); err != nil { + if err := t.cancelStopOrder(takeProfitID); err != nil { return fmt.Errorf("cancel take profit: %w", err) } } return nil } -func (t *Tinkoff) getLastPrice(ctx context.Context, figi string) (*investapi.Quotation, error) { - prices, err := t.marketDataClient.GetLastPrices(ctx, &investapi.GetLastPricesRequest{ - Figi: []string{figi}, - }) +func (t *Tinkoff) getLastPrice(figi string) (*pb.Quotation, error) { + prices, err := t.marketDataClient.GetLastPrices([]string{figi}) if err != nil { t.logger.Error("Failed to get last prices", zap.Error(err), zap.Any("figi", figi)) return nil, fmt.Errorf("last prices: %w", err) @@ -632,14 +550,14 @@ func (t *Tinkoff) getLastPrice(ctx context.Context, figi string) (*investapi.Quo func (t *Tinkoff) getExecutedOrderState( ctx context.Context, orderID string, -) (orderState *investapi.OrderState, err error) { +) (orderState *investgo.GetOrderStateResponse, err error) { ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() for { - orderState, err = t.getOrderState(ctx, orderID) + orderState, err = t.getOrderState(orderID) //nolint: lll - isFullFilled := orderState.GetExecutionReportStatus() == investapi.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL && + isFullFilled := orderState.GetExecutionReportStatus() == pb.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL && orderState.LotsExecuted == orderState.LotsRequested if isFullFilled { break @@ -653,36 +571,25 @@ func (t *Tinkoff) getExecutedOrderState( return orderState, nil } -func (t *Tinkoff) getOrderState(ctx context.Context, orderID string) (orderState *investapi.OrderState, err error) { - orderStateRequest := &investapi.GetOrderStateRequest{ - AccountId: t.accountID, - OrderId: orderID, - } - orderState, err = t.orderClient.GetOrderState(ctx, orderStateRequest) +func (t *Tinkoff) getOrderState(orderID string) (orderState *investgo.GetOrderStateResponse, err error) { + orderState, err = t.orderClient.GetOrderState(t.accountID, orderID, pb.PriceType_PRICE_TYPE_CURRENCY) if err != nil { - t.logger.Error( - "Failed to get order state", - zap.Error(err), - zap.Any("orderStateRequest", orderStateRequest), - ) + t.logger.Error("Failed to get order state", zap.Error(err), zap.Any("orderID", orderID)) return nil, fmt.Errorf("get order state: %w", err) } return orderState, nil } -func (t *Tinkoff) getInstrument(ctx context.Context, figi string) (*investapi.Instrument, error) { - instrumentResponse, err := t.instrumentClient.GetInstrumentBy(t.ctxWithMetadata(ctx), &investapi.InstrumentRequest{ - IdType: investapi.InstrumentIdType_INSTRUMENT_ID_TYPE_FIGI, - Id: figi, - }) +func (t *Tinkoff) getInstrument(figi string) (*pb.Instrument, error) { + instrumentResponse, err := t.instrumentClient.InstrumentByFigi(figi) if err != nil { return nil, fmt.Errorf("get instrument by %s: %w", figi, err) } return instrumentResponse.GetInstrument(), nil } -func (t *Tinkoff) conditionalOrdersFound(ctx context.Context, position *tnkposition.Position) (bool, error) { - resp, err := t.stopOrderClient.GetStopOrders(ctx, &investapi.GetStopOrdersRequest{AccountId: t.accountID}) +func (t *Tinkoff) conditionalOrdersFound(position *tnkposition.Position) (bool, error) { + resp, err := t.stopOrderClient.GetStopOrders(t.accountID) if err != nil { return false, fmt.Errorf("get stop orders: %w", err) } diff --git a/tinkoff_test.go b/tinkoff_test.go index 806f9ef..7bebfb6 100644 --- a/tinkoff_test.go +++ b/tinkoff_test.go @@ -7,13 +7,14 @@ import ( "github.com/evsamsonov/trengin/v2" "github.com/google/uuid" + "github.com/russianinvestments/invest-api-go-sdk/investgo" + pb "github.com/russianinvestments/invest-api-go-sdk/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - investapi "github.com/tinkoff/invest-api-go-sdk" "github.com/undefinedlabs/go-mpatch" "go.uber.org/zap" - "github.com/evsamsonov/tinkoff-broker/internal/tnkposition" + "github.com/evsamsonov/tinkoff-broker/v2/internal/tnkposition" ) const ( @@ -23,11 +24,11 @@ const ( func TestTinkoff_OpenPosition(t *testing.T) { type testWant struct { positionType trengin.PositionType - orderDirection investapi.OrderDirection - stopOrderDirection investapi.StopOrderDirection - openPrice *investapi.MoneyValue - stopLoss *investapi.Quotation - takeProfit *investapi.Quotation + orderDirection pb.OrderDirection + stopOrderDirection pb.StopOrderDirection + openPrice *pb.MoneyValue + stopLoss *pb.Quotation + takeProfit *pb.Quotation stopLossID string takeProfitID string } @@ -47,12 +48,12 @@ func TestTinkoff_OpenPosition(t *testing.T) { TakeProfitOffset: 20.1, }, want: testWant{ - orderDirection: investapi.OrderDirection_ORDER_DIRECTION_BUY, - stopOrderDirection: investapi.StopOrderDirection_STOP_ORDER_DIRECTION_SELL, + orderDirection: pb.OrderDirection_ORDER_DIRECTION_BUY, + stopOrderDirection: pb.StopOrderDirection_STOP_ORDER_DIRECTION_SELL, positionType: trengin.Long, - openPrice: &investapi.MoneyValue{Units: 148, Nano: 0.2 * 10e8}, - stopLoss: &investapi.Quotation{Units: 136, Nano: 0.7 * 10e8}, - takeProfit: &investapi.Quotation{Units: 168, Nano: 0.3 * 10e8}, + openPrice: &pb.MoneyValue{Units: 148, Nano: 0.2 * 10e8}, + stopLoss: &pb.Quotation{Units: 136, Nano: 0.7 * 10e8}, + takeProfit: &pb.Quotation{Units: 168, Nano: 0.3 * 10e8}, stopLossID: "123", takeProfitID: "321", }, @@ -67,12 +68,12 @@ func TestTinkoff_OpenPosition(t *testing.T) { TakeProfitOffset: 20.1, }, want: testWant{ - orderDirection: investapi.OrderDirection_ORDER_DIRECTION_SELL, - stopOrderDirection: investapi.StopOrderDirection_STOP_ORDER_DIRECTION_BUY, + orderDirection: pb.OrderDirection_ORDER_DIRECTION_SELL, + stopOrderDirection: pb.StopOrderDirection_STOP_ORDER_DIRECTION_BUY, positionType: trengin.Short, - openPrice: &investapi.MoneyValue{Units: 148, Nano: 0.2 * 10e8}, - stopLoss: &investapi.Quotation{Units: 159, Nano: 0.7 * 10e8}, - takeProfit: &investapi.Quotation{Units: 128, Nano: 0.1 * 10e8}, + openPrice: &pb.MoneyValue{Units: 148, Nano: 0.2 * 10e8}, + stopLoss: &pb.Quotation{Units: 159, Nano: 0.7 * 10e8}, + takeProfit: &pb.Quotation{Units: 128, Nano: 0.1 * 10e8}, stopLossID: "123", takeProfitID: "321", }, @@ -87,12 +88,12 @@ func TestTinkoff_OpenPosition(t *testing.T) { TakeProfitOffset: 0.0, }, want: testWant{ - orderDirection: investapi.OrderDirection_ORDER_DIRECTION_BUY, - stopOrderDirection: investapi.StopOrderDirection_STOP_ORDER_DIRECTION_SELL, + orderDirection: pb.OrderDirection_ORDER_DIRECTION_BUY, + stopOrderDirection: pb.StopOrderDirection_STOP_ORDER_DIRECTION_SELL, positionType: trengin.Long, - openPrice: &investapi.MoneyValue{Units: 148, Nano: 0.2 * 10e8}, - stopLoss: &investapi.Quotation{}, - takeProfit: &investapi.Quotation{}, + openPrice: &pb.MoneyValue{Units: 148, Nano: 0.2 * 10e8}, + stopLoss: &pb.Quotation{}, + takeProfit: &pb.Quotation{}, stopLossID: "", takeProfitID: "", }, @@ -104,7 +105,7 @@ func TestTinkoff_OpenPosition(t *testing.T) { ordersServiceClient := &mockOrdersServiceClient{} stopOrdersServiceClient := &mockStopOrdersServiceClient{} marketDataServiceClient := &mockMarketDataServiceClient{} - instrumentServiceClient := &mockInstrumentServiceClient{} + instrumentServiceClient := &mockInstrumentsServiceClient{} patch, err := mpatch.PatchMethod(uuid.New, func() uuid.UUID { return uuid.MustParse("8942e9ae-e4e1-11ec-8fea-0242ac120002") @@ -122,76 +123,89 @@ func TestTinkoff_OpenPosition(t *testing.T) { logger: zap.NewNop(), } - instrumentServiceClient.On("GetInstrumentBy", mock.Anything, &investapi.InstrumentRequest{ - IdType: investapi.InstrumentIdType_INSTRUMENT_ID_TYPE_FIGI, - Id: "FUTSBRF06220", - }).Return(&investapi.InstrumentResponse{ - Instrument: &investapi.Instrument{ - Figi: "FUTSBRF06220", - MinPriceIncrement: &investapi.Quotation{Units: 0, Nano: 0.1 * 10e8}, - }, - }, nil) - - marketDataServiceClient.On("GetLastPrices", mock.Anything, &investapi.GetLastPricesRequest{ - Figi: []string{"FUTSBRF06220"}, - }).Return(&investapi.GetLastPricesResponse{ - LastPrices: []*investapi.LastPrice{{ - Figi: "FUTSBRF06220", - Price: &investapi.Quotation{Units: 100}, - }}, - }, nil) - - ordersServiceClient.On("PostOrder", mock.Anything, &investapi.PostOrderRequest{ - Figi: "FUTSBRF06220", - Quantity: 2, - Price: &investapi.Quotation{Units: 100}, - Direction: tt.want.orderDirection, - AccountId: "123", - OrderType: investapi.OrderType_ORDER_TYPE_LIMIT, - OrderId: "8942e9ae-e4e1-11ec-8fea-0242ac120002", - }).Return(&investapi.PostOrderResponse{ - ExecutionReportStatus: investapi.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, - ExecutedOrderPrice: tt.want.openPrice, - OrderId: "1953", - }, nil) - - ordersServiceClient.On("GetOrderState", mock.Anything, &investapi.GetOrderStateRequest{ - AccountId: "123", - OrderId: "1953", - }).Return(&investapi.OrderState{ - InitialCommission: &investapi.MoneyValue{Units: 12, Nano: 0.1 * 10e8}, - ExecutionReportStatus: investapi.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, - AveragePositionPrice: tt.want.openPrice, - }, nil) + instrumentServiceClient. + On("InstrumentByFigi", "FUTSBRF06220"). + Return( + &investgo.InstrumentResponse{ + InstrumentResponse: &pb.InstrumentResponse{ + Instrument: &pb.Instrument{ + Figi: "FUTSBRF06220", + MinPriceIncrement: &pb.Quotation{Units: 0, Nano: 0.1 * 10e8}, + }, + }, + }, nil) + + marketDataServiceClient. + On("GetLastPrices", []string{"FUTSBRF06220"}). + Return(&investgo.GetLastPricesResponse{ + GetLastPricesResponse: &pb.GetLastPricesResponse{ + LastPrices: []*pb.LastPrice{{ + Figi: "FUTSBRF06220", + Price: &pb.Quotation{Units: 100}, + }}, + }}, nil) + + ordersServiceClient. + On("PostOrder", &investgo.PostOrderRequest{ + InstrumentId: "FUTSBRF06220", + Quantity: 2, + Price: &pb.Quotation{Units: 100}, + Direction: tt.want.orderDirection, + AccountId: "123", + OrderType: pb.OrderType_ORDER_TYPE_LIMIT, + OrderId: "8942e9ae-e4e1-11ec-8fea-0242ac120002", + }).Return( + &investgo.PostOrderResponse{ + PostOrderResponse: &pb.PostOrderResponse{ + ExecutionReportStatus: pb.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, + ExecutedOrderPrice: tt.want.openPrice, + OrderId: "1953", + }}, nil) + + ordersServiceClient. + On("GetOrderState", "123", "1953", pb.PriceType_PRICE_TYPE_CURRENCY). + Return(&investgo.GetOrderStateResponse{ + OrderState: &pb.OrderState{ + InitialCommission: &pb.MoneyValue{Units: 12, Nano: 0.1 * 10e8}, + ExecutionReportStatus: pb.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, + AveragePositionPrice: tt.want.openPrice, + }}, nil) if tt.openPositionAction.StopLossOffset != 0 { - stopOrdersServiceClient.On("PostStopOrder", mock.Anything, &investapi.PostStopOrderRequest{ - Figi: "FUTSBRF06220", - Quantity: 2, - Price: tt.want.stopLoss, - StopPrice: tt.want.stopLoss, - Direction: tt.want.stopOrderDirection, - AccountId: "123", - ExpirationType: investapi.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, - StopOrderType: investapi.StopOrderType_STOP_ORDER_TYPE_STOP_LIMIT, - }).Return(&investapi.PostStopOrderResponse{ - StopOrderId: "123", - }, nil).Once() + stopOrdersServiceClient. + On("PostStopOrder", &investgo.PostStopOrderRequest{ + InstrumentId: "FUTSBRF06220", + Quantity: 2, + Price: tt.want.stopLoss, + StopPrice: tt.want.stopLoss, + Direction: tt.want.stopOrderDirection, + AccountId: "123", + ExpirationType: pb.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, + StopOrderType: pb.StopOrderType_STOP_ORDER_TYPE_STOP_LIMIT, + }). + Return(&investgo.PostStopOrderResponse{ + PostStopOrderResponse: &pb.PostStopOrderResponse{ + StopOrderId: "123", + }, + }, nil).Once() } if tt.openPositionAction.TakeProfitOffset != 0 { - stopOrdersServiceClient.On("PostStopOrder", mock.Anything, &investapi.PostStopOrderRequest{ - Figi: "FUTSBRF06220", - Quantity: 2, - Price: tt.want.takeProfit, - StopPrice: tt.want.takeProfit, - Direction: tt.want.stopOrderDirection, - AccountId: "123", - ExpirationType: investapi.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, - StopOrderType: investapi.StopOrderType_STOP_ORDER_TYPE_TAKE_PROFIT, - }).Return(&investapi.PostStopOrderResponse{ - StopOrderId: "321", - }, nil).Once() + stopOrdersServiceClient. + On("PostStopOrder", &investgo.PostStopOrderRequest{ + InstrumentId: "FUTSBRF06220", + Quantity: 2, + Price: tt.want.takeProfit, + StopPrice: tt.want.takeProfit, + Direction: tt.want.stopOrderDirection, + AccountId: "123", + ExpirationType: pb.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, + StopOrderType: pb.StopOrderType_STOP_ORDER_TYPE_TAKE_PROFIT, + }). + Return(&investgo.PostStopOrderResponse{ + PostStopOrderResponse: &pb.PostStopOrderResponse{ + StopOrderId: "321", + }}, nil).Once() } position, _, err := tinkoff.OpenPosition(context.Background(), tt.openPositionAction) @@ -232,9 +246,9 @@ func TestTinkoff_ChangeConditionalOrder_noOpenPosition(t *testing.T) { func TestTinkoff_ChangeConditionalOrder(t *testing.T) { type testWant struct { - stopLoss *investapi.Quotation - takeProfit *investapi.Quotation - stopOrderDirection investapi.StopOrderDirection + stopLoss *pb.Quotation + takeProfit *pb.Quotation + stopOrderDirection pb.StopOrderDirection stopLossID string takeProfitID string } @@ -255,8 +269,8 @@ func TestTinkoff_ChangeConditionalOrder(t *testing.T) { want: testWant{ stopLossID: "1", takeProfitID: "3", - stopLoss: &investapi.Quotation{}, - takeProfit: &investapi.Quotation{}, + stopLoss: &pb.Quotation{}, + takeProfit: &pb.Quotation{}, }, }, { @@ -268,9 +282,9 @@ func TestTinkoff_ChangeConditionalOrder(t *testing.T) { }, positionType: trengin.Long, want: testWant{ - stopLoss: &investapi.Quotation{Units: 123, Nano: 0.43 * 10e8}, - takeProfit: &investapi.Quotation{Units: 156, Nano: 0.31 * 10e8}, - stopOrderDirection: investapi.StopOrderDirection_STOP_ORDER_DIRECTION_SELL, + stopLoss: &pb.Quotation{Units: 123, Nano: 0.43 * 10e8}, + takeProfit: &pb.Quotation{Units: 156, Nano: 0.31 * 10e8}, + stopOrderDirection: pb.StopOrderDirection_STOP_ORDER_DIRECTION_SELL, stopLossID: "2", takeProfitID: "4", }, @@ -281,7 +295,7 @@ func TestTinkoff_ChangeConditionalOrder(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ordersServiceClient := &mockOrdersServiceClient{} stopOrdersServiceClient := &mockStopOrdersServiceClient{} - instrumentServiceClient := &mockInstrumentServiceClient{} + instrumentServiceClient := &mockInstrumentsServiceClient{} positionStorage := tnkposition.NewStorage() tinkoff := &Tinkoff{ @@ -294,63 +308,74 @@ func TestTinkoff_ChangeConditionalOrder(t *testing.T) { } positionStorage.Store(tnkposition.NewPosition( &trengin.Position{Type: tt.positionType, Quantity: 2, FIGI: "FUTSBRF06220"}, - &investapi.Instrument{ + &pb.Instrument{ Figi: "FUTSBRF06220", - MinPriceIncrement: &investapi.Quotation{Units: 0, Nano: 0.01 * 10e8}, + MinPriceIncrement: &pb.Quotation{Units: 0, Nano: 0.01 * 10e8}, }, "1", "3", make(chan trengin.Position, 1), )) - instrumentServiceClient.On("GetInstrumentBy", mock.Anything, &investapi.InstrumentRequest{ - IdType: investapi.InstrumentIdType_INSTRUMENT_ID_TYPE_FIGI, + instrumentServiceClient.On("GetInstrumentBy", mock.Anything, &pb.InstrumentRequest{ + IdType: pb.InstrumentIdType_INSTRUMENT_ID_TYPE_FIGI, Id: "FUTSBRF06220", - }).Return(&investapi.InstrumentResponse{ - Instrument: &investapi.Instrument{ + }).Return(&pb.InstrumentResponse{ + Instrument: &pb.Instrument{ Figi: "FUTSBRF06220", - MinPriceIncrement: &investapi.Quotation{Units: 0, Nano: 0.1 * 10e8}, + MinPriceIncrement: &pb.Quotation{Units: 0, Nano: 0.1 * 10e8}, }, }, nil) if tt.changeConditionOrderAction.StopLoss != 0 { - stopOrdersServiceClient.On("CancelStopOrder", mock.Anything, &investapi.CancelStopOrderRequest{ - AccountId: "123", - StopOrderId: "1", - }).Return(&investapi.CancelStopOrderResponse{}, nil).Once() - - stopOrdersServiceClient.On("PostStopOrder", mock.Anything, &investapi.PostStopOrderRequest{ - Figi: "FUTSBRF06220", + stopOrdersServiceClient. + On("CancelStopOrder", "123", "1"). + Return( + &investgo.CancelStopOrderResponse{ + CancelStopOrderResponse: &pb.CancelStopOrderResponse{}, + }, + nil, + ). + Once() + + stopOrdersServiceClient.On("PostStopOrder", &investgo.PostStopOrderRequest{ + InstrumentId: "FUTSBRF06220", Quantity: 2, Price: tt.want.stopLoss, StopPrice: tt.want.stopLoss, Direction: tt.want.stopOrderDirection, AccountId: "123", - ExpirationType: investapi.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, - StopOrderType: investapi.StopOrderType_STOP_ORDER_TYPE_STOP_LIMIT, - }).Return(&investapi.PostStopOrderResponse{ - StopOrderId: "2", + ExpirationType: pb.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, + StopOrderType: pb.StopOrderType_STOP_ORDER_TYPE_STOP_LIMIT, + }).Return(&investgo.PostStopOrderResponse{ + PostStopOrderResponse: &pb.PostStopOrderResponse{ + StopOrderId: "2", + }, }, nil).Once() } if tt.changeConditionOrderAction.TakeProfit != 0 { - stopOrdersServiceClient.On("CancelStopOrder", mock.Anything, &investapi.CancelStopOrderRequest{ - AccountId: "123", - StopOrderId: "3", - }).Return(&investapi.CancelStopOrderResponse{}, nil).Once() - - stopOrdersServiceClient.On("PostStopOrder", mock.Anything, &investapi.PostStopOrderRequest{ - Figi: "FUTSBRF06220", - Quantity: 2, - Price: tt.want.takeProfit, - StopPrice: tt.want.takeProfit, - Direction: tt.want.stopOrderDirection, - AccountId: "123", - ExpirationType: investapi.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, - StopOrderType: investapi.StopOrderType_STOP_ORDER_TYPE_TAKE_PROFIT, - }).Return(&investapi.PostStopOrderResponse{ - StopOrderId: "4", - }, nil).Once() + stopOrdersServiceClient. + On("CancelStopOrder", "123", "3"). + Return(&investgo.CancelStopOrderResponse{}, nil). + Once() + + stopOrdersServiceClient. + On("PostStopOrder", &investgo.PostStopOrderRequest{ + InstrumentId: "FUTSBRF06220", + Quantity: 2, + Price: tt.want.takeProfit, + StopPrice: tt.want.takeProfit, + Direction: tt.want.stopOrderDirection, + AccountId: "123", + ExpirationType: pb.StopOrderExpirationType_STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL, + StopOrderType: pb.StopOrderType_STOP_ORDER_TYPE_TAKE_PROFIT, + }). + Return(&investgo.PostStopOrderResponse{ + PostStopOrderResponse: &pb.PostStopOrderResponse{ + StopOrderId: "4", + }}, nil). + Once() } position, err := tinkoff.ChangeConditionalOrder(context.Background(), trengin.ChangeConditionalOrderAction{ @@ -394,20 +419,20 @@ func TestTinkoff_ClosePosition(t *testing.T) { tests := []struct { name string positionType trengin.PositionType - wantOrderDirection investapi.OrderDirection - wantClosePrice *investapi.MoneyValue + wantOrderDirection pb.OrderDirection + wantClosePrice *pb.MoneyValue }{ { name: "long", positionType: trengin.Long, - wantOrderDirection: investapi.OrderDirection_ORDER_DIRECTION_SELL, - wantClosePrice: &investapi.MoneyValue{Units: 148, Nano: 0.2 * 10e8}, + wantOrderDirection: pb.OrderDirection_ORDER_DIRECTION_SELL, + wantClosePrice: &pb.MoneyValue{Units: 148, Nano: 0.2 * 10e8}, }, { name: "short", positionType: trengin.Short, - wantOrderDirection: investapi.OrderDirection_ORDER_DIRECTION_BUY, - wantClosePrice: &investapi.MoneyValue{Units: 256, Nano: 0.3 * 10e8}, + wantOrderDirection: pb.OrderDirection_ORDER_DIRECTION_BUY, + wantClosePrice: &pb.MoneyValue{Units: 256, Nano: 0.3 * 10e8}, }, } @@ -416,7 +441,7 @@ func TestTinkoff_ClosePosition(t *testing.T) { ordersServiceClient := &mockOrdersServiceClient{} stopOrdersServiceClient := &mockStopOrdersServiceClient{} marketDataServiceClient := &mockMarketDataServiceClient{} - instrumentServiceClient := &mockInstrumentServiceClient{} + instrumentServiceClient := &mockInstrumentsServiceClient{} patch, err := mpatch.PatchMethod(uuid.New, func() uuid.UUID { return uuid.MustParse("8942e9ae-e4e1-11ec-8fea-0242ac120002") @@ -443,56 +468,60 @@ func TestTinkoff_ClosePosition(t *testing.T) { positionStorage.Store(tnkposition.NewPosition( pos, - &investapi.Instrument{ + &pb.Instrument{ Figi: "FUTSBRF06220", - MinPriceIncrement: &investapi.Quotation{Units: 0, Nano: 0.01 * 10e8}, + MinPriceIncrement: &pb.Quotation{Units: 0, Nano: 0.01 * 10e8}, }, "1", "3", make(chan trengin.Position, 1), )) - stopOrdersServiceClient.On("CancelStopOrder", mock.Anything, &investapi.CancelStopOrderRequest{ - AccountId: "123", - StopOrderId: "1", - }).Return(&investapi.CancelStopOrderResponse{}, nil).Once() - - stopOrdersServiceClient.On("CancelStopOrder", mock.Anything, &investapi.CancelStopOrderRequest{ - AccountId: "123", - StopOrderId: "3", - }).Return(&investapi.CancelStopOrderResponse{}, nil).Once() - - ordersServiceClient.On("PostOrder", mock.Anything, &investapi.PostOrderRequest{ - Figi: "FUTSBRF06220", - Quantity: 2, - Direction: tt.wantOrderDirection, - AccountId: "123", - OrderType: investapi.OrderType_ORDER_TYPE_LIMIT, - OrderId: "8942e9ae-e4e1-11ec-8fea-0242ac120002", - Price: &investapi.Quotation{Units: 100}, - }).Return(&investapi.PostOrderResponse{ - ExecutionReportStatus: investapi.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, - ExecutedOrderPrice: tt.wantClosePrice, - OrderId: "1953", - }, nil) - - marketDataServiceClient.On("GetLastPrices", mock.Anything, &investapi.GetLastPricesRequest{ - Figi: []string{"FUTSBRF06220"}, - }).Return(&investapi.GetLastPricesResponse{ - LastPrices: []*investapi.LastPrice{{ - Figi: "FUTSBRF06220", - Price: &investapi.Quotation{Units: 100}, - }}, + stopOrdersServiceClient. + On("CancelStopOrder", "123", "1"). + Return(&investgo.CancelStopOrderResponse{}, nil). + Once() + + stopOrdersServiceClient. + On("CancelStopOrder", "123", "3"). + Return(&investgo.CancelStopOrderResponse{}, nil). + Once() + + ordersServiceClient.On("PostOrder", &investgo.PostOrderRequest{ + InstrumentId: "FUTSBRF06220", + Quantity: 2, + Direction: tt.wantOrderDirection, + AccountId: "123", + OrderType: pb.OrderType_ORDER_TYPE_LIMIT, + OrderId: "8942e9ae-e4e1-11ec-8fea-0242ac120002", + Price: &pb.Quotation{Units: 100}, + }).Return(&investgo.PostOrderResponse{ + PostOrderResponse: &pb.PostOrderResponse{ + ExecutionReportStatus: pb.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, + ExecutedOrderPrice: tt.wantClosePrice, + OrderId: "1953", + }, }, nil) - ordersServiceClient.On("GetOrderState", mock.Anything, &investapi.GetOrderStateRequest{ - AccountId: "123", - OrderId: "1953", - }).Return(&investapi.OrderState{ - InitialCommission: &investapi.MoneyValue{Units: 12, Nano: 0.1 * 10e8}, - ExecutionReportStatus: investapi.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, - AveragePositionPrice: tt.wantClosePrice, - }, nil) + marketDataServiceClient. + On("GetLastPrices", []string{"FUTSBRF06220"}). + Return(&investgo.GetLastPricesResponse{ + GetLastPricesResponse: &pb.GetLastPricesResponse{ + LastPrices: []*pb.LastPrice{{ + Figi: "FUTSBRF06220", + Price: &pb.Quotation{Units: 100}, + }}, + }}, nil) + + ordersServiceClient. + On("GetOrderState", "123", "1953", pb.PriceType_PRICE_TYPE_CURRENCY). + Return(&investgo.GetOrderStateResponse{ + OrderState: &pb.OrderState{ + InitialCommission: &pb.MoneyValue{Units: 12, Nano: 0.1 * 10e8}, + ExecutionReportStatus: pb.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, + AveragePositionPrice: tt.wantClosePrice, + }, + }, nil) position, err := tinkoff.ClosePosition(context.Background(), trengin.ClosePositionAction{ PositionID: pos.ID, @@ -506,13 +535,13 @@ func TestTinkoff_ClosePosition(t *testing.T) { func TestTinkoff_stopLossPriceByOpen(t *testing.T) { tests := []struct { name string - openPrice *investapi.MoneyValue + openPrice *pb.MoneyValue action trengin.OpenPositionAction - want *investapi.Quotation + want *pb.Quotation }{ { name: "long nano is zero", - openPrice: &investapi.MoneyValue{ + openPrice: &pb.MoneyValue{ Units: 123, Nano: 0, }, @@ -520,14 +549,14 @@ func TestTinkoff_stopLossPriceByOpen(t *testing.T) { Type: trengin.Long, StopLossOffset: 5, }, - want: &investapi.Quotation{ + want: &pb.Quotation{ Units: 118, Nano: 0, }, }, { name: "long nano without overflow", - openPrice: &investapi.MoneyValue{ + openPrice: &pb.MoneyValue{ Units: 123, Nano: 430000000, }, @@ -535,14 +564,14 @@ func TestTinkoff_stopLossPriceByOpen(t *testing.T) { Type: trengin.Long, StopLossOffset: 50.5, }, - want: &investapi.Quotation{ + want: &pb.Quotation{ Units: 72, Nano: 930000000, }, }, { name: "long nano with overflow", - openPrice: &investapi.MoneyValue{ + openPrice: &pb.MoneyValue{ Units: 123, Nano: 530000000, }, @@ -550,14 +579,14 @@ func TestTinkoff_stopLossPriceByOpen(t *testing.T) { Type: trengin.Long, StopLossOffset: 50.556, }, - want: &investapi.Quotation{ + want: &pb.Quotation{ Units: 72, Nano: 974000000, }, }, { name: "short nano is zero", - openPrice: &investapi.MoneyValue{ + openPrice: &pb.MoneyValue{ Units: 123, Nano: 0, }, @@ -565,14 +594,14 @@ func TestTinkoff_stopLossPriceByOpen(t *testing.T) { Type: trengin.Short, StopLossOffset: 5, }, - want: &investapi.Quotation{ + want: &pb.Quotation{ Units: 128, Nano: 0, }, }, { name: "short nano without overflow", - openPrice: &investapi.MoneyValue{ + openPrice: &pb.MoneyValue{ Units: 123, Nano: 430000000, }, @@ -580,14 +609,14 @@ func TestTinkoff_stopLossPriceByOpen(t *testing.T) { Type: trengin.Short, StopLossOffset: 50.4, }, - want: &investapi.Quotation{ + want: &pb.Quotation{ Units: 173, Nano: 830000000, }, }, { name: "short nano with overflow", - openPrice: &investapi.MoneyValue{ + openPrice: &pb.MoneyValue{ Units: 123, Nano: 530000000, }, @@ -595,7 +624,7 @@ func TestTinkoff_stopLossPriceByOpen(t *testing.T) { Type: trengin.Short, StopLossOffset: 50.556, }, - want: &investapi.Quotation{ + want: &pb.Quotation{ Units: 174, Nano: 86000000, }, @@ -607,7 +636,7 @@ func TestTinkoff_stopLossPriceByOpen(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { openPrice := NewMoneyValue(tt.openPrice) - quotation := tinkoff.stopLossPriceByOpen(openPrice, tt.action, &investapi.Quotation{ + quotation := tinkoff.stopLossPriceByOpen(openPrice, tt.action, &pb.Quotation{ Nano: 1000000, }) assert.Equal(t, tt.want, quotation) @@ -621,31 +650,34 @@ func TestTinkoff_processOrderTrades(t *testing.T) { closed := make(chan trengin.Position, 1) - stopOrdersServiceClient.On("GetStopOrders", mock.Anything, &investapi.GetStopOrdersRequest{ - AccountId: "123", - }).Return(&investapi.GetStopOrdersResponse{ - StopOrders: []*investapi.StopOrder{ - {StopOrderId: "10"}, - {StopOrderId: "30"}, - }, - }, nil) - stopOrdersServiceClient.On("CancelStopOrder", mock.Anything, &investapi.CancelStopOrderRequest{ - AccountId: "123", - StopOrderId: "1", - }).Return(&investapi.CancelStopOrderResponse{}, nil) - stopOrdersServiceClient.On("CancelStopOrder", mock.Anything, &investapi.CancelStopOrderRequest{ - AccountId: "123", - StopOrderId: "3", - }).Return(&investapi.CancelStopOrderResponse{}, nil) - - ordersServiceClient.On("GetOrderState", mock.Anything, &investapi.GetOrderStateRequest{ - AccountId: "123", - OrderId: "1953465028754600565", - }).Return(&investapi.OrderState{ - InitialCommission: &investapi.MoneyValue{Units: 125, Nano: 0.6 * 10e8}, - ExecutionReportStatus: investapi.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, - AveragePositionPrice: &investapi.MoneyValue{Units: 174, Nano: 0.7 * 10e8}, - }, nil) + stopOrdersServiceClient. + On("GetStopOrders", "123"). + Return(&investgo.GetStopOrdersResponse{ + GetStopOrdersResponse: &pb.GetStopOrdersResponse{ + StopOrders: []*pb.StopOrder{ + {StopOrderId: "10"}, + {StopOrderId: "30"}, + }, + }, + }, nil) + + stopOrdersServiceClient. + On("CancelStopOrder", "123", "1"). + Return(&investgo.CancelStopOrderResponse{}, nil) + + stopOrdersServiceClient. + On("CancelStopOrder", "123", "3"). + Return(&investgo.CancelStopOrderResponse{}, nil) + + ordersServiceClient. + On("GetOrderState", "123", "1953465028754600565", pb.PriceType_PRICE_TYPE_CURRENCY). + Return(&investgo.GetOrderStateResponse{ + OrderState: &pb.OrderState{ + InitialCommission: &pb.MoneyValue{Units: 125, Nano: 0.6 * 10e8}, + ExecutionReportStatus: pb.OrderExecutionReportStatus_EXECUTION_REPORT_STATUS_FILL, + AveragePositionPrice: &pb.MoneyValue{Units: 174, Nano: 0.7 * 10e8}, + }, + }, nil) positionStorage := tnkposition.NewStorage() tinkoff := &Tinkoff{ @@ -663,9 +695,9 @@ func TestTinkoff_processOrderTrades(t *testing.T) { assert.NoError(t, err) positionStorage.Store(tnkposition.NewPosition( pos, - &investapi.Instrument{ + &pb.Instrument{ Figi: "FUTSBRF06220", - MinPriceIncrement: &investapi.Quotation{Units: 0, Nano: 0.01 * 10e8}, + MinPriceIncrement: &pb.Quotation{Units: 0, Nano: 0.01 * 10e8}, Lot: 1, }, "1", @@ -673,13 +705,13 @@ func TestTinkoff_processOrderTrades(t *testing.T) { closed, )) - ot := &investapi.OrderTrades{ + ot := &pb.OrderTrades{ OrderId: "1953465028754600565", - Direction: investapi.OrderDirection_ORDER_DIRECTION_SELL, + Direction: pb.OrderDirection_ORDER_DIRECTION_SELL, Figi: "FUTSBRF06220", - Trades: []*investapi.OrderTrade{ - {Price: &investapi.Quotation{Units: 112, Nano: 0.3 * 10e8}, Quantity: 2}, - {Price: &investapi.Quotation{Units: 237, Nano: 0.1 * 10e8}, Quantity: 1}, + Trades: []*pb.OrderTrade{ + {Price: &pb.Quotation{Units: 112, Nano: 0.3 * 10e8}, Quantity: 2}, + {Price: &pb.Quotation{Units: 237, Nano: 0.1 * 10e8}, Quantity: 1}, }, AccountId: "123", } @@ -687,12 +719,12 @@ func TestTinkoff_processOrderTrades(t *testing.T) { err = tinkoff.processOrderTrades(context.Background(), ot) assert.NoError(t, err) - ot2 := &investapi.OrderTrades{ + ot2 := &pb.OrderTrades{ OrderId: "1953465028754600565", - Direction: investapi.OrderDirection_ORDER_DIRECTION_SELL, + Direction: pb.OrderDirection_ORDER_DIRECTION_SELL, Figi: "FUTSBRF06220", - Trades: []*investapi.OrderTrade{ - {Price: &investapi.Quotation{Units: 237, Nano: 0.1 * 10e8}, Quantity: 1}, + Trades: []*pb.OrderTrade{ + {Price: &pb.Quotation{Units: 237, Nano: 0.1 * 10e8}, Quantity: 1}, }, AccountId: "123", } @@ -713,17 +745,17 @@ func TestTinkoff_addProtectedSpread(t *testing.T) { var tests = []struct { name string pType trengin.PositionType - price *investapi.Quotation - want *investapi.Quotation + price *pb.Quotation + want *pb.Quotation }{ { name: "long", pType: trengin.Long, - price: &investapi.Quotation{ + price: &pb.Quotation{ Units: 237, Nano: 0.1 * 10e8, }, - want: &investapi.Quotation{ + want: &pb.Quotation{ Units: 225, Nano: 0.2 * 10e8, }, @@ -731,11 +763,11 @@ func TestTinkoff_addProtectedSpread(t *testing.T) { { name: "short", pType: trengin.Short, - price: &investapi.Quotation{ + price: &pb.Quotation{ Units: 237, Nano: 0.1 * 10e8, }, - want: &investapi.Quotation{ + want: &pb.Quotation{ Units: 248, Nano: 1 * 10e8, }, @@ -747,7 +779,7 @@ func TestTinkoff_addProtectedSpread(t *testing.T) { tinkoff := Tinkoff{ protectiveSpread: 5, } - result := tinkoff.addProtectedSpread(tt.pType, tt.price, &investapi.Quotation{ + result := tinkoff.addProtectedSpread(tt.pType, tt.price, &pb.Quotation{ Units: 0, Nano: 0.1 * 10e8, })