Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Indicator is a Golang library for technical analysis, providing a wide range of

## Development Standards

- **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.
- **No External Dependencies:** This project aims to have no external dependencies. Do not add any new dependencies.
- **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.
- **Copyright Header:** Every file must start with the copyright notice:
Expand Down
4 changes: 4 additions & 0 deletions asset/GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ type Repository interface {
- `SqlRepository`: Database-backed persistence for large datasets.
- `TiingoRepository`: Remote API connector for fetching real-time data.

## Model Consistency

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.

## Testing Pattern

Test files use `asset_test` package and verify repository implementations against mock and real-world data sources.
Expand Down
6 changes: 3 additions & 3 deletions asset/tiingo_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type TiingoEndOfDay struct {
Close float64 `json:"close"`

// Volume is the total volume.
Volume int64 `json:"volume"`
Volume float64 `json:"volume"`

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

// AdjVolume is the adjusted total volume.
AdjVolume int64 `json:"adjVolume"`
AdjVolume float64 `json:"adjVolume"`

// Dividend is the dividend paid out.
Dividend float64 `json:"divCash"`
Expand All @@ -87,7 +87,7 @@ func (e *TiingoEndOfDay) ToSnapshot() *Snapshot {
High: e.AdjHigh,
Low: e.AdjLow,
Close: e.AdjClose,
Volume: float64(e.AdjVolume),
Volume: e.AdjVolume,
}
}

Expand Down
44 changes: 44 additions & 0 deletions asset/tiingo_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,47 @@ func TestTiingoRepositoryAppend(t *testing.T) {
t.Fatal(err)
}
}

func TestTiingoRepositoryGetFractionalVolume(t *testing.T) {
// JSON with fractional volume, which should fail to unmarshal into int64
jsonResponse := `[
{
"date": "2024-03-01T00:00:00.000Z",
"open": 61000.5,
"high": 62000.5,
"low": 60000.5,
"close": 61500.5,
"volume": 20297.44489644,
"adjOpen": 61000.5,
"adjHigh": 62000.5,
"adjLow": 60000.5,
"adjClose": 61500.5,
"adjVolume": 20297.44489644,
"divCash": 0.0,
"splitFactor": 1.0
}
]`

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = fmt.Fprint(w, jsonResponse)
}))
defer server.Close()

repository := asset.NewTiingoRepository("1234")
repository.BaseURL = server.URL

snapshots, err := repository.Get("btcusd")
if err != nil {
t.Fatal(err)
}

snapshot, ok := <-snapshots
if !ok {
t.Fatal("expected snapshot, but channel closed (probably due to unmarshal error)")
}

expectedVolume := 20297.44489644
if snapshot.Volume != expectedVolume {
t.Fatalf("actual volume %v expected %v", snapshot.Volume, expectedVolume)
}
}
Loading