diff --git a/api.go b/api.go index a343ba3..4173435 100644 --- a/api.go +++ b/api.go @@ -24,7 +24,7 @@ const ( // DerivAPI is the main struct for the DerivAPI client. type DerivAPI struct { - reqChan chan ApiReqest + reqChan chan APIReqest Endpoint *url.URL keepAliveOnDisconnect chan bool Origin *url.URL @@ -40,8 +40,8 @@ type DerivAPI struct { debugEnabled bool } -// ApiReqest is an interface for all API requests. -type ApiReqest struct { +// APIReqest is an interface for all API requests. +type APIReqest struct { respChan chan []byte msg []byte id int @@ -173,7 +173,7 @@ func (api *DerivAPI) Connect() error { api.ws = ws - api.reqChan = make(chan ApiReqest) + api.reqChan = make(chan APIReqest) respChan := make(chan []byte) outputChan := make(chan []byte) @@ -195,7 +195,6 @@ func (api *DerivAPI) Connect() error { case <-onDisconnect: return } - } }(api.keepAliveInterval, api.keepAliveOnDisconnect) } @@ -235,11 +234,10 @@ func (api *DerivAPI) requestSender(wsConn *websocket.Conn, reqChan chan []byte) for req := range reqChan { api.logDebugf("Sending request: %s", req) - err := wsConn.Write(context.TODO(), websocket.MessageText, req) - - if err != nil { + if err := wsConn.Write(context.TODO(), websocket.MessageText, req); err != nil { api.logDebugf("Failed to send request: %s", err.Error()) api.Disconnect() + return } } @@ -283,7 +281,7 @@ func (api *DerivAPI) handleResponses(wsConn *websocket.Conn, respChan chan []byt // requestMapper forward requests to the Deriv API server and // responses from the WebSocket server to the appropriate channels. -func (api *DerivAPI) requestMapper(respChan chan []byte, outputChan chan []byte, reqChan chan ApiReqest, closingChan chan int) { +func (api *DerivAPI) requestMapper(respChan chan []byte, outputChan chan []byte, reqChan chan APIReqest, closingChan chan int) { responseMap := make(map[int]chan []byte) defer func() { @@ -297,29 +295,26 @@ func (api *DerivAPI) requestMapper(respChan chan []byte, outputChan chan []byte, select { case rawResp := <-respChan: var response APIResponseReqID - err := json.Unmarshal(rawResp, &response) - if err != nil { + + if err := json.Unmarshal(rawResp, &response); err != nil { continue } - channel, ok := responseMap[response.ReqID] - if ok { + if channel, ok := responseMap[response.ReqID]; ok { channel <- rawResp } case req, ok := <-reqChan: if !ok { return } + responseMap[req.id] = req.respChan outputChan <- req.msg case reqID := <-closingChan: - channel, okGet := responseMap[reqID] - - if okGet { + if channel, ok := responseMap[reqID]; ok { close(channel) delete(responseMap, reqID) } - } } } @@ -339,13 +334,13 @@ func (api *DerivAPI) Send(reqID int, request any) (chan []byte, error) { respChan := make(chan []byte) - ApiReqest := ApiReqest{ + req := APIReqest{ id: reqID, msg: msg, respChan: respChan, } - api.reqChan <- ApiReqest + api.reqChan <- req return respChan, nil } @@ -381,7 +376,6 @@ func (api *DerivAPI) SendRequest(reqID int, request any, response any) (err erro } return nil - } } diff --git a/api_test.go b/api_test.go index 6885ba0..cdfe94a 100644 --- a/api_test.go +++ b/api_test.go @@ -19,19 +19,24 @@ func TestNewDerivAPI(t *testing.T) { origin := "https://example.com" appID := 123 lang := "en" + api, err := NewDerivAPI(endpoint, appID, lang, origin) if err != nil { t.Errorf("Unexpected error: %v", err) } + if api.Endpoint.String() != endpoint+"?app_id=123&l=en" { t.Errorf("Unexpected endpoint: got %v, want %v", api.Endpoint.String(), endpoint) } + if api.Origin.String() != origin { t.Errorf("Unexpected origin: got %v, want %v", api.Origin.String(), origin) } + if api.AppID != appID { t.Errorf("Unexpected app ID: got %v, want %v", api.AppID, appID) } + if api.Lang != lang { t.Errorf("Unexpected language: got %v, want %v", api.Lang, lang) } @@ -41,8 +46,8 @@ func TestNewDerivAPI(t *testing.T) { origin = "https://example.com" appID = 123 lang = "en" - _, err = NewDerivAPI(endpoint, appID, lang, origin) - if err == nil { + + if _, err = NewDerivAPI(endpoint, appID, lang, origin); err == nil { t.Errorf("Expected error, got nil") } @@ -51,8 +56,8 @@ func TestNewDerivAPI(t *testing.T) { origin = "https://example.com" appID = -1 lang = "en" - _, err = NewDerivAPI(endpoint, appID, lang, origin) - if err == nil { + + if _, err = NewDerivAPI(endpoint, appID, lang, origin); err == nil { t.Errorf("Expected error, got nil") } @@ -61,8 +66,8 @@ func TestNewDerivAPI(t *testing.T) { origin = "https://example.com" appID = 123 lang = "eng" - _, err = NewDerivAPI(endpoint, appID, lang, origin) - if err == nil { + + if _, err = NewDerivAPI(endpoint, appID, lang, origin); err == nil { t.Errorf("Expected error, got nil") } @@ -71,8 +76,8 @@ func TestNewDerivAPI(t *testing.T) { origin = "https://example.com" appID = 123 lang = "en" - _, err = NewDerivAPI(endpoint, appID, lang, origin) - if err == nil { + + if _, err = NewDerivAPI(endpoint, appID, lang, origin); err == nil { t.Errorf("Expected error, got nil") } @@ -81,8 +86,8 @@ func TestNewDerivAPI(t *testing.T) { origin = ":invalid:" appID = 123 lang = "en" - _, err = NewDerivAPI(endpoint, appID, lang, origin) - if err == nil { + + if _, err = NewDerivAPI(endpoint, appID, lang, origin); err == nil { t.Errorf("Expected error, got nil") } } @@ -95,10 +100,13 @@ func TestGetNextRequestID(t *testing.T) { for i := 0; i < numRequests; i++ { requestID := api.getNextRequestID() + if _, ok := requestIDs[requestID]; ok { t.Errorf("Request ID %d already used", requestID) } + requestIDs[requestID] = true + orderedRequestIDs = append(orderedRequestIDs, requestID) } @@ -111,6 +119,7 @@ func TestGetNextRequestID(t *testing.T) { if id <= lastID { t.Errorf("Request IDs not increasing, lastID=%d currentID=%d", lastID, id) } + lastID = id } } @@ -215,6 +224,7 @@ func TestSend(t *testing.T) { } msg := <-respChan + testMsg := "{\"req_id\":1}" if string(msg) != testMsg { t.Errorf("Expected message to be %s, but got %s", testMsg, msg) @@ -275,7 +285,9 @@ func TestSendRequestTimeout(t *testing.T) { reqID := 1 req := schema.Ping{Ping: 1, ReqId: &reqID} + var resp schema.PingResp + err := api.SendRequest(reqID, req, &resp) if err != nil && err.Error() != "timeout" { @@ -293,9 +305,10 @@ func TestSendRequestAndGotInvalidJSON(t *testing.T) { } time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() + defer server.Close() + url := "ws://" + server.Listener.Addr().String() api, _ := NewDerivAPI(url, 123, "en", "http://example.com") _ = api.Connect() @@ -334,6 +347,7 @@ func TestSendRequest(t *testing.T) { } time.Sleep(time.Second) // to keep the connection open })) + defer server.Close() url := "ws://" + server.Listener.Addr().String() @@ -348,7 +362,9 @@ func TestSendRequest(t *testing.T) { reqID := 1 req := schema.Ping{Ping: 1, ReqId: &reqID} + var resp schema.PingResp + err = api.SendRequest(reqID, req, &resp) if err != nil { @@ -376,7 +392,9 @@ func TestSendRequestFailed(t *testing.T) { reqID := 1 req := schema.Ping{Ping: 1, ReqId: &reqID} + var resp schema.PingResp + err := api.SendRequest(reqID, req, &resp) if err == nil { @@ -440,13 +458,14 @@ func TestDebugLogs(t *testing.T) { if err != nil { t.Errorf("Failed to create pipe: %v", err) } + defer reader.Close() + log.SetOutput(writer) - scanner := bufio.NewScanner(reader) + scanner := bufio.NewScanner(reader) server := newMockWSServer(echoHandler) url := "ws://" + server.Listener.Addr().String() - api, _ := NewDerivAPI(url, 123, "en", "http://example.com", Debug) _ = api.Connect() diff --git a/custom_subscription_calls.go b/custom_subscription_calls.go index 40c6e63..fd25b39 100644 --- a/custom_subscription_calls.go +++ b/custom_subscription_calls.go @@ -4,35 +4,41 @@ import "github.com/ksysoev/deriv-api/schema" // SubscribeTicksHistory Get historic tick data for a given symbol. func (api *DerivAPI) SubscribeTicksHistory(r schema.TicksHistory) (rsp schema.TicksHistoryResp, s *Subsciption[schema.TicksHistoryResp, schema.TicksResp], err error) { - id := api.getNextRequestID() var f schema.TicksHistorySubscribe = 1 + + id := api.getNextRequestID() r.ReqId = &id r.Subscribe = &f r.Style = schema.TicksHistoryStyleTicks s = NewSubcription[schema.TicksHistoryResp, schema.TicksResp](api) rsp, err = s.Start(id, r) + return } // SubscribeTicksHistory Get historic candles data for a given symbol. func (api *DerivAPI) SubscribeCandlesHistory(r schema.TicksHistory) (rsp schema.TicksHistoryResp, s *Subsciption[schema.TicksHistoryResp, schema.TicksHistoryResp], err error) { - id := api.getNextRequestID() var f schema.TicksHistorySubscribe = 1 + + id := api.getNextRequestID() r.ReqId = &id r.Subscribe = &f r.Style = schema.TicksHistoryStyleCandles s = NewSubcription[schema.TicksHistoryResp, schema.TicksHistoryResp](api) rsp, err = s.Start(id, r) + return } // SubscribeTransaction Subscribe to transaction notifications func (api *DerivAPI) SubscribeTransaction(r schema.Transaction) (rsp schema.TransactionResp, s *Subsciption[schema.TransactionResp, schema.TransactionResp], err error) { - id := api.getNextRequestID() var f schema.TransactionSubscribe = 1 + + id := api.getNextRequestID() r.ReqId = &id r.Subscribe = f s = NewSubcription[schema.TransactionResp, schema.TransactionResp](api) rsp, err = s.Start(id, r) + return } diff --git a/errors.go b/errors.go index f3b16d7..d8d6703 100644 --- a/errors.go +++ b/errors.go @@ -29,7 +29,7 @@ type APIErrorResponse struct { func parseError(rawResponse []byte) error { var errorResponse APIErrorResponse - err := json.Unmarshal([]byte(rawResponse), &errorResponse) + err := json.Unmarshal(rawResponse, &errorResponse) if err != nil { return err } diff --git a/helpers_test.go b/helpers_test.go index 51f0535..30e9d37 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -42,13 +42,12 @@ func echoHandler(ws *websocket.Conn) { } buffer := bytes.NewBuffer(make([]byte, 0)) - _, err = buffer.ReadFrom(reader) - if err != nil { + + if _, err = buffer.ReadFrom(reader); err != nil { return } - err = ws.Write(context.Background(), msgType, buffer.Bytes()) - if err != nil { + if err = ws.Write(context.Background(), msgType, buffer.Bytes()); err != nil { return } } @@ -66,18 +65,20 @@ func onMessageHanler(cb func(ws *websocket.Conn, msgType websocket.MessageType, } buffer := bytes.NewBuffer(make([]byte, 0)) - _, err = buffer.ReadFrom(reader) - if err != nil { + + if _, err = buffer.ReadFrom(reader); err != nil { return } msg := buffer.Bytes() + wg.Add(1) + go func() { + defer wg.Done() + cb(ws, msgType, msg) - wg.Done() }() } - } } diff --git a/subscriptions.go b/subscriptions.go index 5da9e9c..ff7cac5 100644 --- a/subscriptions.go +++ b/subscriptions.go @@ -116,23 +116,29 @@ func (s *Subsciption[initResp, Resp]) Start(reqID int, request any) (initResp, e select { case <-time.After(s.API.TimeOut): s.API.logDebugf("Timeout waiting for response for request %d", reqID) + return resp, fmt.Errorf("timeout") case initResponse, ok := <-inChan: if !ok { s.API.logDebugf("Connection closed while waiting for response for request %d", reqID) + return resp, fmt.Errorf("connection closed") } + subResp, err := parseSubsciption(initResponse) if err != nil { s.API.closeRequestChannel(reqID) + return resp, err } + s.SubsciptionID = subResp.Subscription.ID err = json.Unmarshal(initResponse, &resp) if err != nil { s.API.logDebugf("Failed to parse response for request %d: %s", reqID, err.Error()) s.API.closeRequestChannel(reqID) + return resp, err } @@ -155,20 +161,20 @@ func (s *Subsciption[initResp, Resp]) messageHandler(inChan chan []byte) { } s.statusLock.Unlock() }() + for rawResponse := range inChan { - err := parseError(rawResponse) - if err != nil { + if err := parseError(rawResponse); err != nil { s.API.logDebugf("Error in subsciption message: %v", err) continue } var response Resp - err = json.Unmarshal(rawResponse, &response) - if err != nil { + if err := json.Unmarshal(rawResponse, &response); err != nil { s.API.logDebugf("Failed to parse response in subscription: %s", err.Error()) continue } + s.statusLock.Lock() s.Stream <- response s.statusLock.Unlock() @@ -184,5 +190,6 @@ func (s *Subsciption[initResp, Resp]) GetStream() chan Resp { func (s *Subsciption[initResp, Resp]) IsActive() bool { s.statusLock.Lock() defer s.statusLock.Unlock() + return s.isActive } diff --git a/subscriptions_test.go b/subscriptions_test.go index f54b884..04e39ee 100644 --- a/subscriptions_test.go +++ b/subscriptions_test.go @@ -16,9 +16,11 @@ func TestParseSubscription_ValidInput(t *testing.T) { input := []byte(`{"subscription": {"id": "123"}}`) expected := SubscriptionResponse{Subscription: SubscriptionIDResponse{ID: "123"}} result, err := parseSubsciption(input) + if err != nil { t.Errorf("Unexpected error: %v", err) } + if !reflect.DeepEqual(result, expected) { t.Errorf("Expected %+v, but got %+v", expected, result) } @@ -26,8 +28,8 @@ func TestParseSubscription_ValidInput(t *testing.T) { func TestParseSubscription_InvalidJSONInput(t *testing.T) { input := []byte(`{"subscription": {"id": "123", "status": "active"`) - _, err := parseSubsciption(input) - if err == nil { + + if _, err := parseSubsciption(input); err == nil { t.Errorf("Expected an error, but got nil") } } @@ -36,9 +38,11 @@ func TestParseSubscription_InvalidSubscriptionData(t *testing.T) { input := []byte(`{"subscription": {"id": "123", "status": "active"}, "error": {"code": "invalid_subscription"}}`) expectedErr := &APIError{Code: "invalid_subscription"} _, err := parseSubsciption(input) + if err == nil { t.Errorf("Expected an error, but got nil") } + if !reflect.DeepEqual(err, expectedErr) { t.Errorf("Expected %+v, but got %+v", expectedErr, err) } @@ -54,10 +58,12 @@ func TestParseSubscription_EmptyInput(t *testing.T) { func TestParseSubscription_EmptySubscriptionData(t *testing.T) { input := []byte(`{}`) expectedErr := fmt.Errorf("subscription ID is empty") + _, err := parseSubsciption(input) if err == nil { t.Errorf("Expected an error, but got nil") } + if errors.Is(err, expectedErr) { t.Errorf("Expected %+v, but got %+v", expectedErr, err) } @@ -66,6 +72,7 @@ func TestParseSubscription_EmptySubscriptionData(t *testing.T) { func TestNewNewSubcription(t *testing.T) { api, _ := NewDerivAPI("ws://example.com", 123, "en", "http://example.com") sub := NewSubcription[schema.TicksResp, schema.TicksResp](api) + if sub == nil { t.Errorf("Expected a subscription, but got nil") } @@ -98,12 +105,13 @@ func TestStart(t *testing.T) { ws.Write(context.Background(), websocket.MessageText, []byte(testResp)) time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() defer server.Close() + + url := "ws://" + server.Listener.Addr().String() api, _ := NewDerivAPI(url, 123, "en", "http://example.com") - err := api.Connect() - if err != nil { + + if err := api.Connect(); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -114,7 +122,9 @@ func TestStart(t *testing.T) { } reqID := 1 + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} initResp, err := sub.Start(reqID, req) @@ -141,6 +151,7 @@ func TestStart(t *testing.T) { } sub.Start(reqID, req) + if sub.IsActive() == false { t.Errorf("Expected subscription to be active, but got inactive") } @@ -152,14 +163,18 @@ func TestStartFailed(t *testing.T) { ws.Write(context.Background(), websocket.MessageText, []byte("")) time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() + server.Close() + + url := "ws://" + server.Listener.Addr().String() api, _ := NewDerivAPI(url, 123, "en", "http://example.com") sub := NewSubcription[schema.TicksResp, schema.TicksResp](api) reqID := 1 + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} _, err := sub.Start(reqID, req) @@ -194,19 +209,20 @@ func TestForget(t *testing.T) { server := newMockWSServer( onMessageHanler(func(ws *websocket.Conn, _ websocket.MessageType, _ []byte) { - for resp := range responses { ws.Write(context.Background(), websocket.MessageText, []byte(resp)) } time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() + defer server.Close() + url := "ws://" + server.Listener.Addr().String() + api, _ := NewDerivAPI(url, 123, "en", "http://example.com") - err := api.Connect() - if err != nil { + + if err := api.Connect(); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -217,11 +233,12 @@ func TestForget(t *testing.T) { } reqID := api.getNextRequestID() + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} - _, err = sub.Start(reqID, req) - if err != nil { + if _, err := sub.Start(reqID, req); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -283,19 +300,19 @@ func TestForgetFailed(t *testing.T) { server := newMockWSServer( onMessageHanler(func(ws *websocket.Conn, _ websocket.MessageType, _ []byte) { - for resp := range responses { ws.Write(context.Background(), websocket.MessageText, []byte(resp)) } time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() + defer server.Close() + url := "ws://" + server.Listener.Addr().String() api, _ := NewDerivAPI(url, 123, "en", "http://example.com") - err := api.Connect() - if err != nil { + + if err := api.Connect(); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -306,9 +323,11 @@ func TestForgetFailed(t *testing.T) { } reqID := api.getNextRequestID() + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} - _, err = sub.Start(reqID, req) + _, err := sub.Start(reqID, req) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -336,9 +355,8 @@ func TestForgetFailed(t *testing.T) { } }` }() - err = sub.Forget() - if err == nil { + if err = sub.Forget(); err == nil { t.Errorf("Expected error, but got nil") } @@ -366,11 +384,13 @@ func TestStartAPIError(t *testing.T) { ws.Write(context.Background(), websocket.MessageText, []byte(testResp)) time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() + defer server.Close() + + url := "ws://" + server.Listener.Addr().String() api, _ := NewDerivAPI(url, 123, "en", "http://example.com") - err := api.Connect() - if err != nil { + + if err := api.Connect(); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -381,11 +401,12 @@ func TestStartAPIError(t *testing.T) { } reqID := 1 + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} - _, err = sub.Start(reqID, req) - if err == nil { + if _, err := sub.Start(reqID, req); err == nil { t.Errorf("Expected an error, but got nil") } } @@ -405,11 +426,12 @@ func TestStartInvalidResponse(t *testing.T) { time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() defer server.Close() + + url := "ws://" + server.Listener.Addr().String() api, _ := NewDerivAPI(url, 123, "en", "http://example.com") - err := api.Connect() - if err != nil { + + if err := api.Connect(); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -420,11 +442,12 @@ func TestStartInvalidResponse(t *testing.T) { } reqID := 1 + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} - _, err = sub.Start(reqID, req) - if err == nil { + if _, err := sub.Start(reqID, req); err == nil { t.Errorf("Expected an error, but got nil") } } @@ -454,18 +477,19 @@ func TestStartInvalidResponseInSubscription(t *testing.T) { server := newMockWSServer( onMessageHanler(func(ws *websocket.Conn, _ websocket.MessageType, _ []byte) { - for _, resp := range responses { ws.Write(context.Background(), websocket.MessageText, []byte(resp)) } time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() + defer server.Close() + + url := "ws://" + server.Listener.Addr().String() api, _ := NewDerivAPI(url, 123, "en", "http://example.com") - err := api.Connect() - if err != nil { + + if err := api.Connect(); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -476,7 +500,9 @@ func TestStartInvalidResponseInSubscription(t *testing.T) { } reqID := 1 + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} initResp, err := sub.Start(reqID, req) @@ -497,7 +523,6 @@ func TestStartInvalidResponseInSubscription(t *testing.T) { } case <-time.After(time.Millisecond): } - } func TestStartAPIErrorInSubscription(t *testing.T) { @@ -536,18 +561,19 @@ func TestStartAPIErrorInSubscription(t *testing.T) { server := newMockWSServer( onMessageHanler(func(ws *websocket.Conn, _ websocket.MessageType, _ []byte) { - for _, resp := range responses { ws.Write(context.Background(), websocket.MessageText, []byte(resp)) } time.Sleep(time.Second) // to keep the connection open })) - url := "ws://" + server.Listener.Addr().String() + defer server.Close() + + url := "ws://" + server.Listener.Addr().String() api, _ := NewDerivAPI(url, 123, "en", "http://example.com") - err := api.Connect() - if err != nil { + + if err := api.Connect(); err != nil { t.Errorf("Unexpected error: %v", err) } @@ -558,7 +584,9 @@ func TestStartAPIErrorInSubscription(t *testing.T) { } reqID := 1 + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} initResp, err := sub.Start(reqID, req) @@ -579,7 +607,6 @@ func TestStartAPIErrorInSubscription(t *testing.T) { } case <-time.After(time.Millisecond): } - } func TestStartTimeout(t *testing.T) { @@ -596,7 +623,9 @@ func TestStartTimeout(t *testing.T) { sub := NewSubcription[schema.TicksResp, schema.TicksResp](api) reqID := 1 + var f schema.TicksSubscribe = 1 + req := schema.Ticks{Ticks: "R50", Subscribe: &f, ReqId: &reqID} _, err := sub.Start(reqID, req)