Skip to content

Commit

Permalink
:sparles: getChartRangeRequest (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
MateoGreil committed May 14, 2024
1 parent a222a7f commit daaa7f0
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 27 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ xapiRealClient, err := xapi.NewClient(os.Getenv("XAPI_USER_ID"), os.Getenv("XAPI

### GetCandles

#### With subscription

```go
xapiClient.SubscribeCandles("EURUSD")
for {
Expand All @@ -33,6 +35,26 @@ for {
}
```

#### With query

```go
end := int(time.Now().Add(-24 * 1 * time.Hour).UnixMilli())
period := 1
ticks := 50
start := int(time.Now().Add(-24 * time.Hour).UnixMilli())
candles, err := xapiClient.GetCandles(end, period, start, "EURUSD", ticks)
```

| Value | Type | Description |
| ----- | ---- | ----------- |
| end | int | End of chart block (rounded down to the nearest interval and excluding) |
| period | int | Period code |
| start | int | Start of chart block (rounded down to the nearest interval and excluding) |
| symbol | string | Symbol |
| ticks | int | Number of ticks needed |

More details here : http://developers.xstore.pro/documentation/current#getChartRangeRequest

## Contributions

Contributions and feedback are welcome! If you encounter any issues, have suggestions for improvement, or would like to contribute new features, please open an issue or submit a pull request on the GitHub repository.
Expand Down
18 changes: 14 additions & 4 deletions internal/protocols/socket/request.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
package socket

type Request struct {
Command string `json:"command"`
Arguments RequestArguments `json:"arguments"`
Command string `json:"command"`
Arguments interface{} `json:"arguments"`
}

type RequestArguments interface{}

type LoginRequestArguments struct {
UserId string `json:"userId"`
Password string `json:"password"`
}

type InfoArguments struct {
Info interface{} `json:"info"`
}

type GetCandlesInfo struct {
End int `json:"end"`
Period int `json:"period"`
Start int `json:"start"`
Symbol string `json:"symbol"`
Ticks int `json:"ticks"`
}
25 changes: 22 additions & 3 deletions internal/protocols/socket/response.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
package socket

type Response struct {
Status bool `json:"status"`
type LoginResponse struct {
Status bool `json:"status"`
StreamSessionId string `json:"streamSessionId"`
ErrorCode string `json:"errorCode"`
ErrorDescr string `json:"errorDescr"`
}

type LoginResponse struct {
type Response struct {
Status bool `json:"status"`
StreamSessionId string `json:"streamSessionId"`
ErrorCode string `json:"errorCode"`
ErrorDescr string `json:"errorDescr"`
ReturnData struct {
Digits int `json:"digits"`
RateInfos []Candle `json:"rateInfos"`
} `json:"returnData"`
}

// TODO: Move it to a common package (stream and socket use it)
type Candle struct {
Close float64 `json:"close"`
Ctm int64 `json:"ctm"`
CtmString string `json:"ctmString"`
High float64 `json:"high"`
Low float64 `json:"low"`
Open float64 `json:"open"`
QuoteId int `json:"quoteId"`
Vol float64 `json:"vol"`
}
64 changes: 44 additions & 20 deletions xapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package xapi
import (
"encoding/json"
"fmt"
"sync"
"time"

"github.com/MateoGreil/xapi-go/internal/protocols/socket"
Expand All @@ -14,9 +15,9 @@ type client struct {
conn *websocket.Conn
streamConn *websocket.Conn
streamSessionId string
socketMessageChannel chan interface{}
streamMessageChannel chan interface{}
CandlesChannel chan stream.Candle
mutexSendMessage sync.Mutex
}

const (
Expand Down Expand Up @@ -53,18 +54,18 @@ func NewClient(userId string, password string, connectionType string) (*client,
}
getKeepAlive(streamConn, streamSessionId)

var m sync.Mutex
c := &client{
conn: conn,
streamConn: streamConn,
streamSessionId: streamSessionId,
socketMessageChannel: make(chan interface{}),
streamMessageChannel: make(chan interface{}),
CandlesChannel: make(chan stream.Candle),
mutexSendMessage: m,
}
go c.pingSocket()
go c.pingStream()
go c.listenStream()
go c.socketWriteJSON()
go c.streamWriteJSON()

return c, nil
Expand All @@ -79,6 +80,33 @@ func (c *client) SubscribeCandles(symbol string) {
c.streamMessageChannel <- request
}

func (c *client) GetCandles(end int, period int, start int, symbol string, ticks int) ([]socket.Candle, error) {
request := socket.Request{
Command: "getChartRangeRequest",
Arguments: socket.InfoArguments{
Info: socket.GetCandlesInfo{
End: end,
Period: period,
Start: start,
Symbol: symbol,
Ticks: ticks,
},
},
}
response := socket.Response{}
c.mutexSendMessage.Lock()
c.conn.WriteJSON(request)
err := c.conn.ReadJSON(&response)
c.mutexSendMessage.Unlock()
if err != nil {
return nil, err
}
if response.Status != true {
return nil, fmt.Errorf("Error on sending getChartRangeRequest: %+v, response:, %+v", request, response)
}
return response.ReturnData.RateInfos, nil
}

func (c *client) listenStream() {
for {
_, message, err := c.streamConn.ReadMessage()
Expand Down Expand Up @@ -109,16 +137,20 @@ func (c *client) listenStream() {

func (c *client) pingSocket() {
for {
request := socket.Request{
Command: "ping",
Arguments: nil,
request := struct {
Command string `json:"command"`
}{Command: "ping"}
response := socket.Response{}
c.mutexSendMessage.Lock()
c.conn.WriteJSON(request)
err := c.conn.ReadJSON(&response)
c.mutexSendMessage.Unlock()
if err != nil {
//TODO: Handle error
fmt.Printf("Ping socket failed: %s", err.Error())
} else if response.Status != true {
fmt.Errorf("Error on sending request: %+v, response:, %+v", request, response)
}
c.socketMessageChannel <- request
// response := socket.Response{}
// err := c.conn.ReadJSON(&response)
// if err != nil {
// fmt.Println(err.Error())
// }
time.Sleep(pingInterval)
}
}
Expand All @@ -142,14 +174,6 @@ func (c *client) streamWriteJSON() {
}
}

func (c *client) socketWriteJSON() {
for {
message := <-c.socketMessageChannel
c.conn.WriteJSON(message)
fmt.Printf("messageSocket: %+v\n", message)
}
}

func login(conn *websocket.Conn, userId string, password string) (string, error) {
request := socket.Request{
Command: "login",
Expand Down
33 changes: 33 additions & 0 deletions xapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,36 @@ func TestSuscribeCandles(t *testing.T) {
t.Error("Did not receive candles")
}
}

func TestGetCandles(t *testing.T) {
xapiClient, err := NewClient(os.Getenv("XAPI_USER_ID"), os.Getenv("XAPI_PASSWORD"), "demo")
if err != nil {
t.Error(err)
}

start := int(time.Now().Add(-24 * 1 * time.Hour).UnixMilli())
period := 1
ticks := 1
end := int(time.Now().Add(-24 * 1 * time.Hour).UnixMilli())
candles, err := xapiClient.GetCandles(start, period, end, "EURUSD", ticks)
if err != nil {
t.Error(err)
} else {
length := len(candles)
if length != 1 {
t.Errorf("Should contain 1 candle, but contains %d", length)
}
}

ticks = 50
candles, err = xapiClient.GetCandles(start, period, end, "EURUSD", ticks)
if err != nil {
t.Error(err)
} else {
length := len(candles)
if length != 50 {
t.Errorf("Should contain 50 candle, but contains %d", length)
}
}

}

0 comments on commit daaa7f0

Please sign in to comment.