Skip to content

Commit

Permalink
added bulkquotes endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
MarketDataApp committed Feb 8, 2024
1 parent 3930489 commit f0ad9ce
Show file tree
Hide file tree
Showing 36 changed files with 782 additions and 187 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ jobs:
run: go build -v ./...

- name: Run Test Coverage Script
run: ./tests/test_coverage.sh
run: ./.tests/test_coverage.sh

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4
with:
file: ./tests/coverage.out
file: ./.tests/coverage.out
fail_ci_if_error: false
verbose: true
env:
Expand Down
18 changes: 18 additions & 0 deletions .tests/test_coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

# Define paths relative to the script location
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
PROJECT_ROOT="${SCRIPT_DIR}/.."
COVERAGE_PROFILE="${SCRIPT_DIR}/coverage.out"
COVERAGE_HTML="${SCRIPT_DIR}/coverage.html"

# Ensure we're in the project root directory
cd "${PROJECT_ROOT}"

# Run tests for all packages including the root, and collect coverage across all packages
go test -timeout 100s -covermode=atomic -coverpkg=./... -coverprofile="${COVERAGE_PROFILE}" ./... || exit 1

# Generate a human-readable HTML coverage report
go tool cover -html="${COVERAGE_PROFILE}" -o "${COVERAGE_HTML}"

echo "Coverage report generated at ${COVERAGE_HTML}"
57 changes: 49 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Market Data's Go SDK covers almost all v1 endponts. See our complete list of end
|-------------------|--------------|-----------|-----------|
| Markets | Status || |
| Stocks | Candles |||
| Stocks | Bulk Candles | | |
| Stocks | Bulk Candles | | |
| Stocks | Quotes || |
| Stocks | Bulk Quotes || |
| Stocks | Earnings || |
Expand All @@ -53,7 +53,7 @@ Market Data's Go SDK covers almost all v1 endponts. See our complete list of end
| Indices | Candles || |
| Indices | Quotes || |

Note on v2: Even though some v2 endpoints are available for use in this SDK, Market Data has not yet released v2 of its API for clients and v2 usage is restricted to admins only. Clients should onlly use v1 endpoints at this time. Even after v2 is released, we do not plan on deprecating v1 endpoints, so please build your applications with confidence using v1 endpoints.
> Note on v2: Even though some v2 endpoints are available for use in this SDK, Market Data has not yet released v2 of its API for clients and v2 usage is restricted to admins only. Clients should onlly use v1 endpoints at this time. Even after v2 is released, we do not plan on deprecating v1 endpoints, so please build your applications with confidence using v1 endpoints.
# Getting Started

Expand All @@ -74,6 +74,44 @@ export MARKETDATA_TOKEN="<your_api_token>" # mac/linux
setx MARKETDATA_TOKEN "<your_api_token>" # windows
```

### Making Your First Request

Check the examples folder for working examples for each request.

Get a stock quote:
```go
import (
"fmt"
"log"

api "github.com/MarketDataApp/sdk-go"
)

func main() {
// Initalize a new request, set the sybmol parameter, make the request.
quotes, err := api.StockQuote().Symbol("AAPL").Get()
if err != nil {
log.Fatalf("Failed to get stock quotes: %v", err)
}

// Loop over the quotes and print them out.
for _, quote := range quotes {
fmt.Println(quote)
}
```
### SDK Usage Instructions
- All requests are initialized using the name of the endpoint and parameters are set using a builder pattern.
- All requests have 3 methods to get results:
- Use the `.Get()` method on any request to get a slice of objects. This is what you will use in most cases.
- Use the `.Packed()` method on any request to get a struct that models the Market Data JSON response. If you want to model the objects differently, this method could be useful for you.
- Use the `.Raw()` method on any request to get the raw *resty.Response object. This allows you to access the raw JSON or the raw *http.Response object and you can use any of the resty methods on the response.
> Note: Since all our structs are pre-defined based on the Market Data API's standard response format, our API's universal parameters such as `human`, `columns`, `dateformat`, `format` are not supported in this SDK. If you wish to make use of these parameters, you will need to model your own structs that can unmarshal the modified API response.
- We have already implemeneted `.String()` methods for all `.Get()` and `.Packed()` responses and sorting methods for all candle objects.
### Logging
The SDK systematically logs API responses to facilitate troubleshooting and analysis, adhering to the following rules:
Expand All @@ -84,7 +122,6 @@ The SDK systematically logs API responses to facilitate troubleshooting and anal
All log files are formatted in JSON and stored within the `/logs` subdirectory. Each entry captures comprehensive details including the request URL, request headers, CF Ray ID (a unique identifier for the request), response status code, response headers, and the response body, providing a full context for each logged event.
Example of a log entry:

```json
{
"level":"info",
Expand Down Expand Up @@ -149,8 +186,10 @@ To enable debug mode, you need to call the `Debug` method on the `MarketDataClie
package main

import (
"log"
"fmt"

api "github.com/MarketDataApp/sdk-go"
"log"
)

func main() {
Expand All @@ -159,17 +198,19 @@ func main() {
log.Fatalf("Failed to get client: %v", err)
}

client.Debug(true)
client.Debug(true) // Here is where debug mode is turned on.

quote, err := client.StockQuotes().Symbol("AAPL").Get()
quotes, err := client.StockQuotes().Symbol("AAPL").Get()
if err != nil {
log.Fatalf("Failed to get stock quotes: %v", err)
}

log.Printf("Quote: %+v\n", quote)
for _, quote := range quotes {
fmt.Println(quote)
}
}
```
Please note that the information printed in debug mode can be quite verbose. It is recommended to use this mode only when you are facing issues and need to understand what's happening under the hood. When debug mode is activated all requests are logged, not just requests that fail.
Debug mode can be particularly useful when you are first getting started with the SDK. It can help you understand how the SDK constructs requests, how it handles responses, and how it manages errors. By examining the debug output, you can gain a deeper understanding of the SDK's inner workings and be better prepared to handle any issues that might arise.
Debug mode can be particularly useful when you are first getting started with the SDK. It can help you understand how the SDK constructs requests, how it handles responses, and how it manages errors. By examining the debug output, you can gain a deeper understanding of the SDK's inner workings and be better prepared to handle any issues that might arise.
123 changes: 123 additions & 0 deletions SDK_New_Endpoint_Integration_Guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Adding New Endpoints

To add a new endpoint to the Market Data Go SDK, follow these steps:

1. **Update Endpoints File**: Add the new endpoint to the endpoints map in endpoints.go. Specify the version and category it belongs to.

```go
// Example for adding a new "futures" quote in version 1
1: {
"futures": {
"quotes": "/v1/futures/quotes/{symbol}/",
},
```
2. **Update baseRequest.go**: Add a type assertion for the new request type.
```go
// Example for adding a new FuturesQuoteRequest in version 1
if fqr, ok := br.child.(*FuturesQuoteRequest); ok {
params, err := fqr.getParams()
if err != nil {
return nil, err
}
return params, nil
}
```
### 3. **Create Request Struct/Methods**:
- Define a struct in the `client` package to represent the new request. Use the existing requests as a guide.
- Attach the existing parameters, or design new parameters in the `helpers/parameters` package. Most common parameters are already defined.
- Create setter methods for the request struct that passes throught the parameters to the `helpers/parameters` that you've imported to use. These methods return a pointer to the struct and store errors, allowing the user to use the builder pattern to define the parameters.
- Create a function to initialize a new empty request that attaches the default client or a user-provided client.
```go
package client

import (
"github.com/MarketDataApp/sdk-go/helpers/parameters"
)

// FuturesQuoteRequest is the struct that sets/stores the request parameters.
type FuturesQuoteRequest struct {
*baseRequest
symbolParams *parameters.SymbolParams
}

// Symbol sets the FuturesQuoteRequest ticket symbol.
func (fqr *FuturesQuoteRequest) Symbol(q string) *FuturesQuoteRequest {
if fqr == nil {
return nil
}
err := fqr.symbolParams.SetSymbol(q)
if err != nil {
fqr.Error = err
}
return fqr
}

// FuturesQuote initializes a new FuturesQuoteRequest.
func FuturesQuote(client ...*MarketDataClient) *FuturesQuoteRequest {
baseReq := newBaseRequest(client...)
baseReq.path = endpoints[1]["futures"]["quotes"]

fqr := &StockQuoteRequest{
baseRequest: baseReq,
symbolParams: &parameters.SymbolParams{},
}

baseReq.child = fqr

return fqr
}
```
4. **Create Response Struct**: Define a struct in the `models` package to unmarshal the JSON response from the Market Data API. This struct should mirror the JSON structure returned by the new endpoint. Market Data structs are typically JSON arrays.
```go
package models

// FuturesQuoteResponse represents the JSON response structure for futures quotes.
type FuturesQuoteResponse struct {
Symbol []string `json:"symbol"`
Bid []float64 `json:"bid"`
Ask []float64 `json:"ask"`
Time []int64 `json:"time"`
}
```
5. **Create Struct for Unpacked Object**: Define a struct that represents the unpacked object. This struct is what the Unpack method will return.
```go
// FuturesQuote represents a single futures quote.
type FuturesQuote struct {
Symbol string
Bid float64
Ask float64
Time time.Time
}
```
6. **Create Unpack Method**: Implement an Unpack method for your response struct to convert the packed JSON array format into individual struct objects. This method should be part of the response struct in the `models` package.
```go
// Unpack converts packed FuturesQuoteResponse into a slice of individual structs.
func (fpr *FuturesQuoteResponse) Unpack() ([]FuturesQuote, error) {
var quotes []FuturesQuote
for i := range fpr.Symbol {
quote := FuturesQuote{
Symbol: fpr.Symbol[i],
Bid: fpr.Bid[i],
Ask: fpr.Ask[i],
Time: time.Unix(fpr.Time[i], 0),
}
quotes = append(quotes, quote)
}
return quotes, nil
```
7. **Implement Packed and Get Methods**: In the client package, create methods for your endpoint that return the packed response (Packed) and the unpacked response (Get). These methods should utilize the base request functionality and your response struct.
By following these steps, you can extend the Market Data Go SDK to support new endpoints, ensuring that the SDK remains a comprehensive tool for accessing financial data.
8. **Create Documentation, Tests and Testable Examples**: Document with DocStrings, create tests, and create testable examples for both the `Get` and `Packed` methods.
10 changes: 9 additions & 1 deletion baseRequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (br *baseRequest) getParams() ([]parameters.MarketDataParam, error) {
return params, nil
}

if tr, ok := br.child.(*TickersRequest); ok {
if tr, ok := br.child.(*StockTickersRequestV2); ok {
params, err := tr.getParams()
if err != nil {
return nil, err
Expand Down Expand Up @@ -82,6 +82,14 @@ func (br *baseRequest) getParams() ([]parameters.MarketDataParam, error) {
return params, nil
}

if bsqr, ok := br.child.(*BulkStockQuotesRequest); ok {
params, err := bsqr.getParams()
if err != nil {
return nil, err
}
return params, nil
}

if snr, ok := br.child.(*StockNewsRequest); ok {
params, err := snr.getParams()
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ import (
"sync"
"time"

"github.com/MarketDataApp/sdk-go/helpers/logging"
"github.com/go-resty/resty/v2"
_ "github.com/joho/godotenv/autoload"
)

// marketDataClient is the default *MarketDataClient
var marketDataClient *MarketDataClient

const (
Expand Down Expand Up @@ -81,7 +83,6 @@ const (
// Public Methods:
// - Debug(enable bool) *MarketDataClient: Enables or disables debug mode for logging detailed request and response information.
// - RateLimitExceeded() bool: Checks if the rate limit for API requests has been exceeded.

type MarketDataClient struct {
*resty.Client // Embedding resty.Client to utilize its HTTP client functionalities.
RateLimitLimit int // RateLimitLimit represents the maximum number of requests that can be made in a rate limit window.
Expand Down Expand Up @@ -148,7 +149,7 @@ func (c *MarketDataClient) addLogFromRequestResponse(req *resty.Request, resp *r
body := string(resp.Body())

// Create a new log entry with the gathered information.
logEntry := AddToLog(GetLogs(), time.Now(), rayID, req.URL, rateLimitConsumed, delay, status, body, redactedHeaders, resHeaders)
logEntry := logging.AddToLog(GetLogs(), time.Now(), rayID, req.URL, rateLimitConsumed, delay, status, body, redactedHeaders, resHeaders)
// If debug mode is enabled and the log entry is not nil, pretty print the log entry.
if c.debug && logEntry != nil {
logEntry.PrettyPrint()
Expand Down Expand Up @@ -527,6 +528,6 @@ func (c *MarketDataClient) Token(bearerToken string) *MarketDataClient {

// GetLogs returns a pointer to the HttpRequestLogs instance.
// This function is useful for accessing the logs that have been collected during HTTP requests.
func GetLogs() *HttpRequestLogs {
return Logs
func GetLogs() *logging.HttpRequestLogs {
return logging.Logs
}
1 change: 1 addition & 0 deletions endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var endpoints = map[int]map[string]map[string]string{
"candles": "/v1/stocks/candles/{resolution}/{symbol}/",
"bulkcandles": "/v1/stocks/bulkcandles/{resolution}/",
"quotes": "/v1/stocks/quotes/{symbol}/",
"bulkquotes": "/v1/stocks/bulkquotes/",
"earnings": "/v1/stocks/earnings/{symbol}/",
"news": "/v1/stocks/news/{symbol}/",
},
Expand Down
5 changes: 4 additions & 1 deletion examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ func Start() {
log.Fatalf("Failed to get client: %v", err)
}

client.Debug(false)
client.Debug(false) // Turn off debug mode to see the log example clearly.

fmt.Println("Staring log example...")
LogExample()

client.Debug(true)

/*
//fmt.Println("Starting market status request...")
//marketstatusExample()
Expand Down
8 changes: 8 additions & 0 deletions helpers/dates/dates.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,3 +481,11 @@ func Latest(dates ...interface{}) (time.Time, error) {
}
return latest, nil
}

// IsStartOfDay checks if the given time is at the start of the day in the specified timezone.
func IsStartOfDay(t time.Time, loc *time.Location) bool {
// Convert the time to the specified timezone
tInLoc := t.In(loc)
// Check if the time is at the start of the day (midnight)
return tInLoc.Hour() == 0 && tInLoc.Minute() == 0 && tInLoc.Second() == 0 && tInLoc.Nanosecond() == 0
}

Check failure on line 491 in helpers/dates/dates.go

View workflow job for this annotation

GitHub Actions / build

syntax error: unexpected var after top level declaration
2 changes: 1 addition & 1 deletion logging.go → helpers/logging/logging.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package client
package logging

import (
"encoding/json"
Expand Down
Loading

0 comments on commit f0ad9ce

Please sign in to comment.