diff --git a/.gitignore b/.gitignore index 5b90e79..bcc43f8 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ go.work.sum # env file .env +.apikey \ No newline at end of file diff --git a/api/alphavantage/ipo_calandar.go b/api/alphavantage/ipo_calandar.go new file mode 100644 index 0000000..8e04cbe --- /dev/null +++ b/api/alphavantage/ipo_calandar.go @@ -0,0 +1,106 @@ +package alphavantage + +import ( + "encoding/csv" + "fmt" + "io" + "net/http" + "strconv" + "time" +) + +// IPOEvent represents a single row from the Alpha Vantage IPO Calendar CSV +type IPOEvent struct { + Symbol string `json:"symbol"` + Name string `json:"name"` + IPODate time.Time `json:"ipo_date"` + PriceRangeLow float64 `json:"price_range_low"` + PriceRangeHigh float64 `json:"price_range_high"` + Currency string `json:"currency"` + Exchange string `json:"exchange"` +} + +// FetchIPOCalendar fetches the CSV data from Alpha Vantage and parses it into a slice of IPOEvent +func FetchIPOCalendar(apiKey string) ([]IPOEvent, error) { + url := fmt.Sprintf("https://www.alphavantage.co/query?function=IPO_CALENDAR&apikey=%s", apiKey) + + // 1. Make the HTTP Request + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("failed to fetch data: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("bad status code: %d", resp.StatusCode) + } + + // 2. Initialize CSV reader + reader := csv.NewReader(resp.Body) + + // Read the header row first to skip it + _, err = reader.Read() + if err != nil { + return nil, fmt.Errorf("failed to read CSV header: %w", err) + } + + var events []IPOEvent + + // 3. Iterate through CSV records + for { + record, err := reader.Read() + if err == io.EOF { + break // End of file + } + if err != nil { + return nil, fmt.Errorf("error reading csv record: %w", err) + } + + // Map CSV columns to Struct + // Index mapping: 0:symbol, 1:name, 2:ipoDate, 3:priceRangeLow, 4:priceRangeHigh, 5:currency, 6:exchange + event, err := mapRecordToIPOEvent(record) + if err != nil { + // We log the error but continue processing other rows + fmt.Printf("Skipping row due to error: %v\n", err) + continue + } + events = append(events, event) + } + + return events, nil +} + +// mapRecordToIPOEvent handles the type conversion for a single CSV row +func mapRecordToIPOEvent(record []string) (IPOEvent, error) { + if len(record) < 7 { + return IPOEvent{}, fmt.Errorf("insufficient columns in record") + } + + // Parse Date (Format: 2026-05-05) + ipoDate, err := time.Parse("2006-01-02", record[2]) + if err != nil { + return IPOEvent{}, fmt.Errorf("invalid date format: %v", err) + } + + // Parse Price Low + priceLow, err := strconv.ParseFloat(record[3], 64) + if err != nil { + return IPOEvent{}, fmt.Errorf("invalid price low: %v", err) + } + + // Parse Price High + priceHigh, err := strconv.ParseFloat(record[4], 64) + if err != nil { + return IPOEvent{}, fmt.Errorf("invalid price high: %v", err) + } + + return IPOEvent{ + Symbol: record[0], + Name: record[1], + IPODate: ipoDate, + PriceRangeLow: priceLow, + PriceRangeHigh: priceHigh, + Currency: record[5], + Exchange: record[6], + }, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9d7cda0 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gitea.epicknex.ddns.net/epicknex/gamba-gods + +go 1.26.2 diff --git a/main.go b/main.go new file mode 100644 index 0000000..dcc60b2 --- /dev/null +++ b/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "os" + + "gitea.epicknex.ddns.net/epicknex/gamba-gods/api/alphavantage" +) + +func main() { + data, err := os.ReadFile(".apikey") + if err != nil { + panic(err) + } + events, err := alphavantage.FetchIPOCalendar(string(data)) + if err != nil { + fmt.Println("Error:", err) + } + for _, event := range events { + fmt.Printf("%+v\n", event) // Print the IPOEvent struct + } +}