77 "encoding/binary"
88 "fmt"
99 "math"
10+ "reflect"
1011 "syscall/js"
1112)
1213
@@ -16,6 +17,16 @@ var candlestickShader string
1617//go:embed chart/shaders/line.wgsl
1718var lineShader string
1819
20+ // ohlcvData represents extracted OHLCV data
21+ type ohlcvData struct {
22+ Timestamp int64
23+ Open float64
24+ High float64
25+ Low float64
26+ Close float64
27+ Volume float64
28+ }
29+
1930// ChartRenderer manages rendering of charts
2031type ChartRenderer struct {
2132 Canvas * GPUCanvas
@@ -280,16 +291,8 @@ func (cr *ChartRenderer) renderCandlestickSeries(pass js.Value, series *GPUNode)
280291 return
281292 }
282293
283- // Try to extract OHLCV data
284- var candles []interface {}
285- switch d := data .(type ) {
286- case []interface {}:
287- candles = d
288- default :
289- logError ("[ChartRenderer] Invalid candlestick data type" )
290- return
291- }
292-
294+ // Extract OHLCV data using reflection
295+ candles := cr .extractOHLCVData (data )
293296 if len (candles ) == 0 {
294297 return
295298 }
@@ -410,6 +413,72 @@ func (cr *ChartRenderer) renderLineSeries(pass js.Value, series *GPUNode) {
410413
411414// Helper functions
412415
416+ // extractOHLCVData uses reflection to extract OHLCV data from any slice type
417+ func (cr * ChartRenderer ) extractOHLCVData (data interface {}) []ohlcvData {
418+ v := reflect .ValueOf (data )
419+ if v .Kind () != reflect .Slice {
420+ logError (fmt .Sprintf ("[ChartRenderer] Data is not a slice: %T" , data ))
421+ return nil
422+ }
423+
424+ result := make ([]ohlcvData , v .Len ())
425+ for i := 0 ; i < v .Len (); i ++ {
426+ item := v .Index (i )
427+ if item .Kind () == reflect .Struct {
428+ // Extract fields by name
429+ timestamp := getInt64Field (item , "Timestamp" )
430+ open := getFloat64Field (item , "Open" )
431+ high := getFloat64Field (item , "High" )
432+ low := getFloat64Field (item , "Low" )
433+ close := getFloat64Field (item , "Close" )
434+ volume := getFloat64Field (item , "Volume" )
435+
436+ result [i ] = ohlcvData {
437+ Timestamp : timestamp ,
438+ Open : open ,
439+ High : high ,
440+ Low : low ,
441+ Close : close ,
442+ Volume : volume ,
443+ }
444+ }
445+ }
446+
447+ return result
448+ }
449+
450+ // getInt64Field extracts an int64 field from a struct
451+ func getInt64Field (v reflect.Value , fieldName string ) int64 {
452+ field := v .FieldByName (fieldName )
453+ if ! field .IsValid () {
454+ return 0
455+ }
456+ switch field .Kind () {
457+ case reflect .Int64 :
458+ return field .Int ()
459+ case reflect .Int , reflect .Int32 :
460+ return field .Int ()
461+ default :
462+ return 0
463+ }
464+ }
465+
466+ // getFloat64Field extracts a float64 field from a struct
467+ func getFloat64Field (v reflect.Value , fieldName string ) float64 {
468+ field := v .FieldByName (fieldName )
469+ if ! field .IsValid () {
470+ return 0
471+ }
472+ switch field .Kind () {
473+ case reflect .Float64 , reflect .Float32 :
474+ return field .Float ()
475+ case reflect .Int , reflect .Int32 , reflect .Int64 :
476+ return float64 (field .Int ())
477+ default :
478+ return 0
479+ }
480+ }
481+
413482func (cr * ChartRenderer ) getPadding () map [string ]float32 {
414483 padding := map [string ]float32 {"top" : 60 , "right" : 20 , "bottom" : 40 , "left" : 80 }
415484
@@ -424,7 +493,7 @@ func (cr *ChartRenderer) getPadding() map[string]float32 {
424493 return padding
425494}
426495
427- func (cr * ChartRenderer ) calculateDataRanges (candles []interface {} ) {
496+ func (cr * ChartRenderer ) calculateDataRanges (candles []ohlcvData ) {
428497 if len (candles ) == 0 {
429498 return
430499 }
@@ -433,14 +502,9 @@ func (cr *ChartRenderer) calculateDataRanges(candles []interface{}) {
433502 minY , maxY := math .MaxFloat64 , - math .MaxFloat64
434503
435504 for _ , c := range candles {
436- candleMap , ok := c .(map [string ]interface {})
437- if ! ok {
438- continue
439- }
440-
441- timestamp , _ := candleMap ["Timestamp" ].(float64 )
442- high , _ := candleMap ["High" ].(float64 )
443- low , _ := candleMap ["Low" ].(float64 )
505+ timestamp := float64 (c .Timestamp )
506+ high := c .High
507+ low := c .Low
444508
445509 if timestamp < minX {
446510 minX = timestamp
@@ -497,24 +561,19 @@ func (cr *ChartRenderer) calculateLineDataRanges(points []interface{}) {
497561 cr .DataYRange = [2 ]float64 {minY , maxY }
498562}
499563
500- func (cr * ChartRenderer ) createCandleDataBuffer (candles []interface {} ) * GPUBuffer {
564+ func (cr * ChartRenderer ) createCandleDataBuffer (candles []ohlcvData ) * GPUBuffer {
501565 // Each candle: timestamp(f32), open(f32), high(f32), low(f32), close(f32), volume(f32) = 24 bytes
502566 bufferSize := len (candles ) * 24
503567 data := make ([]byte , bufferSize )
504568
505569 for i , c := range candles {
506- candleMap , ok := c .(map [string ]interface {})
507- if ! ok {
508- continue
509- }
510-
511570 offset := i * 24
512- timestamp , _ := candleMap [ "Timestamp" ].( float64 )
513- open , _ := candleMap [ "Open" ].( float64 )
514- high , _ := candleMap [ "High" ].( float64 )
515- low , _ := candleMap [ "Low" ].( float64 )
516- close , _ := candleMap [ "Close" ].( float64 )
517- volume , _ := candleMap [ "Volume" ].( float64 )
571+ timestamp := float64 ( c . Timestamp )
572+ open := c . Open
573+ high := c . High
574+ low := c . Low
575+ close := c . Close
576+ volume := c . Volume
518577
519578 binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (float32 (timestamp )))
520579 binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (float32 (open )))
0 commit comments