You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Go语言调用外部API后提取并保存Time Series数据的方法

提取AlphaVantage API返回的Time Series字段数据

嘿,作为Go新手能写出调用API的客户端已经超棒啦!要提取返回JSON里的Time Series (1min)字段其实很简单,咱们分两种方式来实现,优先推荐类型安全的结构体方案,也会给你灵活的map方案作为补充:


方案一:用结构体解析(推荐,类型安全)

Go的JSON解析非常依赖结构体,我们可以根据返回的JSON结构定义对应的结构体,通过json:"键名"的tag来映射JSON里带特殊字符的键。

步骤1:定义对应结构体

根据你给出的JSON结构,我们可以定义三个结构体:

  • MetaData:对应Meta Data下的元信息
  • TimePoint:对应每个时间点的价格成交量数据
  • AlphaVantageResponse:外层的响应结构体,包含上面两个类型的字段

步骤2:修改你的代码,实现解析和提取

把你的代码修改成下面这样,我已经加了详细的注释:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "strconv"
)

// MetaData 对应JSON里的"Meta Data"字段
type MetaData struct {
    Information   string `json:"1. Information"`
    Symbol        string `json:"2. Symbol"`
    LastRefreshed string `json:"3. Last Refreshed"`
    Interval      string `json:"4. Interval"`
    OutputSize    string `json:"5. Output Size"`
    TimeZone      string `json:"6. Time Zone"`
}

// TimePoint 对应每个时间点的价格数据
type TimePoint struct {
    Open   string `json:"1. open"`
    High   string `json:"2. high"`
    Low    string `json:"3. low"`
    Close  string `json:"4. close"`
    Volume string `json:"5. volume"`
}

// AlphaVantageResponse 外层响应结构体
type AlphaVantageResponse struct {
    MetaData         MetaData             `json:"Meta Data"`
    TimeSeriesOneMin map[string]TimePoint `json:"Time Series (1min)"`
}

func main() {
    response, err := http.Get("https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=1min&apikey=demo")
    if err != nil {
        fmt.Println("发起请求出错:", err)
        return
    }
    defer response.Body.Close()

    // 读取响应内容
    contents, err := ioutil.ReadAll(response.Body)
    if err != nil {
        fmt.Println("读取响应出错:", err)
        return
    }

    // 将JSON字节解析到结构体
    var apiResponse AlphaVantageResponse
    err = json.Unmarshal(contents, &apiResponse)
    if err != nil {
        fmt.Println("解析JSON出错:", err)
        return
    }

    // 提取你需要的Time Series数据!
    // apiResponse.TimeSeriesOneMin就是一个map,key是时间戳,value是对应的数据点
    fmt.Println("=== 提取的时间序列数据 ===")
    for timeStamp, data := range apiResponse.TimeSeriesOneMin {
        fmt.Printf("时间: %s\n", timeStamp)
        fmt.Printf("开盘价: %s | 最高价: %s | 最低价: %s | 收盘价: %s | 成交量: %s\n",
            data.Open, data.High, data.Low, data.Close, data.Volume)
    }

    // 示例:将字符串价格转为数值类型用于后续计算
    fmt.Println("\n=== 转换为数值型收盘价用于计算 ===")
    for timeStamp, data := range apiResponse.TimeSeriesOneMin {
        closePrice, err := strconv.ParseFloat(data.Close, 64)
        if err != nil {
            fmt.Printf("转换%s的收盘价出错: %v\n", timeStamp, err)
            continue
        }
        fmt.Printf("%s 收盘价(数值): %.4f\n", timeStamp, closePrice)
        // 这里可以加入你的自定义计算逻辑,比如计算均线、涨幅等
    }
}

方案二:用map解析(灵活,兼容不同时间间隔)

如果你需要调用不同间隔的API(比如5min、15min),返回的JSON里的Time Series键会变成Time Series (5min),这时候用结构体需要修改tag,而用map可以更灵活地适配:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
)

func main() {
    response, err := http.Get("https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=1min&apikey=demo")
    if err != nil {
        fmt.Println("发起请求出错:", err)
        return
    }
    defer response.Body.Close()

    contents, err := ioutil.ReadAll(response.Body)
    if err != nil {
        fmt.Println("读取响应出错:", err)
        return
    }

    // 用map[string]interface{}来接收整个JSON
    var result map[string]interface{}
    err = json.Unmarshal(contents, &result)
    if err != nil {
        fmt.Println("解析JSON出错:", err)
        return
    }

    // 遍历找到以"Time Series"开头的键(兼容不同间隔)
    var timeSeries map[string]interface{}
    for key, value := range result {
        if strings.HasPrefix(key, "Time Series") {
            timeSeries = value.(map[string]interface{})
            break
        }
    }

    // 处理提取到的时间序列数据
    if timeSeries != nil {
        fmt.Println("=== 提取的时间序列数据 ===")
        for timeStamp, data := range timeSeries {
            point := data.(map[string]interface{})
            open := point["1. open"].(string)
            high := point["2. high"].(string)
            low := point["3. low"].(string)
            close := point["4. close"].(string)
            volume := point["5. volume"].(string)

            fmt.Printf("时间: %s\n开盘价: %s | 最高价: %s | 最低价: %s | 收盘价: %s | 成交量: %s\n",
                timeStamp, open, high, low, close, volume)
        }
    } else {
        fmt.Println("未找到Time Series字段")
    }
}

小提示

  • 结构体方案更适合固定API返回结构的场景,类型安全,不容易出错,新手优先用这个
  • map方案适合需要兼容多种返回结构的场景,但需要手动做类型断言,容易出现类型转换错误
  • 如果需要把提取的数据保存下来,可以写入文件(用ioutil.WriteFile)或者存入数据库,直接用apiResponse.TimeSeriesOneMin或者timeSeries变量即可

内容的提问来源于stack exchange,提问作者Philip Mutua

火山引擎 最新活动