Skip to content

Commit 57d7860

Browse files
authored
Fix Tiingo crypto volume unmarshal error (#354)
This PR fixes the unmarshal error for crypto assets by changing volume types to float64. Fixes #352
1 parent 8c47c8e commit 57d7860

4 files changed

Lines changed: 52 additions & 3 deletions

File tree

GEMINI.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Indicator is a Golang library for technical analysis, providing a wide range of
1515

1616
## Development Standards
1717

18+
- **Finding Tasks:** Use the `gh issue list` command to identify open issues. Issues labeled `good first issue` are typically self-contained indicator or strategy implementations.
1819
- **No External Dependencies:** This project aims to have no external dependencies. Do not add any new dependencies.
1920
- **Composition & Reusability:** Build and utilize reusable blocks, particularly those in the `helper/` package. Avoid re-implementing existing logic within a single indicator. For example, if an indicator uses a moving average, it should employ the existing implementation rather than duplicating the logic internally.
2021
- **Copyright Header:** Every file must start with the copyright notice:

asset/GEMINI.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ type Repository interface {
2727
- `SqlRepository`: Database-backed persistence for large datasets.
2828
- `TiingoRepository`: Remote API connector for fetching real-time data.
2929

30+
## Model Consistency
31+
32+
All price and volume fields in repository models (e.g., `TiingoEndOfDay`) must use `float64`. This ensures compatibility with crypto assets that provide fractional volumes, which would otherwise cause JSON unmarshaling errors if `int64` is used.
33+
3034
## Testing Pattern
3135

3236
Test files use `asset_test` package and verify repository implementations against mock and real-world data sources.

asset/tiingo_repository.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type TiingoEndOfDay struct {
5555
Close float64 `json:"close"`
5656

5757
// Volume is the total volume.
58-
Volume int64 `json:"volume"`
58+
Volume float64 `json:"volume"`
5959

6060
// AdjOpen is the adjusted opening price.
6161
AdjOpen float64 `json:"adjOpen"`
@@ -70,7 +70,7 @@ type TiingoEndOfDay struct {
7070
AdjClose float64 `json:"adjClose"`
7171

7272
// AdjVolume is the adjusted total volume.
73-
AdjVolume int64 `json:"adjVolume"`
73+
AdjVolume float64 `json:"adjVolume"`
7474

7575
// Dividend is the dividend paid out.
7676
Dividend float64 `json:"divCash"`
@@ -87,7 +87,7 @@ func (e *TiingoEndOfDay) ToSnapshot() *Snapshot {
8787
High: e.AdjHigh,
8888
Low: e.AdjLow,
8989
Close: e.AdjClose,
90-
Volume: float64(e.AdjVolume),
90+
Volume: e.AdjVolume,
9191
}
9292
}
9393

asset/tiingo_repository_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,47 @@ func TestTiingoRepositoryAppend(t *testing.T) {
182182
t.Fatal(err)
183183
}
184184
}
185+
186+
func TestTiingoRepositoryGetFractionalVolume(t *testing.T) {
187+
// JSON with fractional volume, which should fail to unmarshal into int64
188+
jsonResponse := `[
189+
{
190+
"date": "2024-03-01T00:00:00.000Z",
191+
"open": 61000.5,
192+
"high": 62000.5,
193+
"low": 60000.5,
194+
"close": 61500.5,
195+
"volume": 20297.44489644,
196+
"adjOpen": 61000.5,
197+
"adjHigh": 62000.5,
198+
"adjLow": 60000.5,
199+
"adjClose": 61500.5,
200+
"adjVolume": 20297.44489644,
201+
"divCash": 0.0,
202+
"splitFactor": 1.0
203+
}
204+
]`
205+
206+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
207+
_, _ = fmt.Fprint(w, jsonResponse)
208+
}))
209+
defer server.Close()
210+
211+
repository := asset.NewTiingoRepository("1234")
212+
repository.BaseURL = server.URL
213+
214+
snapshots, err := repository.Get("btcusd")
215+
if err != nil {
216+
t.Fatal(err)
217+
}
218+
219+
snapshot, ok := <-snapshots
220+
if !ok {
221+
t.Fatal("expected snapshot, but channel closed (probably due to unmarshal error)")
222+
}
223+
224+
expectedVolume := 20297.44489644
225+
if snapshot.Volume != expectedVolume {
226+
t.Fatalf("actual volume %v expected %v", snapshot.Volume, expectedVolume)
227+
}
228+
}

0 commit comments

Comments
 (0)