polygon-io / client-go

The official Go client library for the Polygon REST and WebSocket API.
MIT License
127 stars 36 forks source link

models.Agg uses `Millis` but `MarshalJSON` is defined on a pointer receiver #360

Closed jabalsad closed 9 months ago

jabalsad commented 10 months ago

Hello,

I am trying to marshal models.Agg items, which have Timestamp defined as Millis (non-pointer type). However, the Millis type defines MarshalJSON on pointer receivers, so when attempting to marshal the Agg type, timestamps don't get marshaled correctly:

Ref: https://github.com/polygon-io/client-go/blob/9588ed03df836a53c1c55f8d83ab538c552d336e/rest/models/aggs.go#L220 Ref: https://github.com/polygon-io/client-go/blob/9588ed03df836a53c1c55f8d83ab538c552d336e/rest/models/types.go#L253

justinpolygon commented 9 months ago

Hey @jabalsad, timestamps are unmashaled but they go into a custom Millis which is time.Time. The intention here is more of connivence in that they are already converted to human readable for you and you don't need to do it yourself.

Here's some example code:

// Stocks - Aggregates (Bars)
// https://polygon.io/docs/stocks/get_v2_aggs_ticker__stocksticker__range__multiplier___timespan___from___to
// https://github.com/polygon-io/client-go/blob/master/rest/aggs.go
package main

import (
    "context"
    "log"
    "os"
    "time"

    polygon "github.com/polygon-io/client-go/rest"
    "github.com/polygon-io/client-go/rest/models"
    "github.com/davecgh/go-spew/spew"
)

func main() {

    // init client
    c := polygon.New(os.Getenv("POLYGON_API_KEY"))

    // set params
    params := models.ListAggsParams{
        Ticker:     "AAPL",
        Multiplier: 1,
        Timespan:   "day",
        From:       models.Millis(time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC)),
        To:         models.Millis(time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC)),
    }.WithOrder(models.Desc).WithLimit(50000).WithAdjusted(true)

    // make request
    iter := c.ListAggs(context.Background(), params)

    // do something with the result
    for iter.Next() {
        //log.Print(iter.Item())

        // dump the struct
        spew.Dump(iter.Item())

        // Convert Nanos to time.Time
        timestamp := time.Time(iter.Item().Timestamp) //res.Results.ParticipantTimestamp)

        // Print the Unix millisecond timestamp (int64)
        log.Printf("Unix millisecond timestamp: %d", timestamp.UnixMilli())

        // Print the datetime
        log.Printf("Datetime: %v", timestamp)

    }
    if iter.Err() != nil {
        log.Fatal(iter.Err())
    }

}

You'll see something like:

$ go run main.go
(models.Agg) {
 Ticker: (string) "",
 Close: (float64) 189.95,
 High: (float64) 190.32,
 Low: (float64) 188.19,
 Transactions: (int64) 486786,
 Open: (float64) 189.84,
 Timestamp: (models.Millis) {
  wall: (uint64) 0,
  ext: (int64) 63836917200,
  loc: (*time.Location)(0x102a007e0)(Local)
 },
 Volume: (float64) 4.8744366e+07,
 VWAP: (float64) 189.337,
 OTC: (bool) false
}
2023/12/04 11:55:15 Unix millisecond timestamp: 1701320400000
2023/12/04 11:55:15 Datetime: 2023-11-29 21:00:00 -0800 PST

Hope this explains how it works.

jabalsad commented 9 months ago

Hi. I was actually talking about marshaling the data into a JSON string:

    for iter.Next() {
        bytes, err := json.Marshal(iter.Item())
                 fmt.Println(string(bytes))
    }

Notice how the timestamp field does not get marshaled.

justinpolygon commented 9 months ago

Sorry, I understand now. Yeah, I'll explore this and likely submit a PR to fix the issue. Thanks for reporting.

justinpolygon commented 9 months ago

FYI - have a PR that'll fix this via https://github.com/polygon-io/client-go/pull/368. I'll keep you posted on when this gets released.

justinpolygon commented 9 months ago

Fixed and the latest release has the update. Thanks again for reporting this.