package main import "encoding/csv" import "fmt" import "io" import "log" import "os" import "strconv" import "strings" var COLUMNS []string type FinancialData struct { Date string Open, High, Low, Close, Adj_Close, Volume float64 } func printHTML(data []FinancialData, out io.Writer) { io.WriteString(out, ` Financial `) for _, col := range COLUMNS { io.WriteString(out, " \n") } io.WriteString(out, " \n") // data for _, row := range data { row.printHTMLRow(out) } io.WriteString(out, `
") io.WriteString(out, col) io.WriteString(out, "
`) } func toJSDate(date string) string { str := "Date.UTC(" parts := strings.Split(date, "-") for i, part := range parts { str += strings.TrimLeft(part, "0") if i < len(parts)-1 { str += "," } } str += ")" return str } func getChartDataOpen(data []FinancialData) string { str := "[" for i, v := range data { str += "[" str += toJSDate(v.Date) str += fmt.Sprintf(", %.4f", v.Open) str += "]" if i < len(data)-1 { str += "," } } str += "]" return str } func getChartDataClose(data []FinancialData) string { str := "[" for i, v := range data { str += "[" str += toJSDate(v.Date) str += fmt.Sprintf(", %.4f", v.Close) str += "]" if i < len(data)-1 { str += "," } } str += "]" return str } func (data FinancialData) printHTMLRow(out io.Writer) { io.WriteString(out, " ") printCellString(data.Date, out) printCellFloat(data.Open, out) printCellFloat(data.High, out) printCellFloat(data.Low, out) printCellFloat(data.Close, out) printCellFloat(data.Adj_Close, out) printCellFloatFmt(data.Volume, "%f", out) io.WriteString(out, "\n") } func printCellString(val string, out io.Writer) { io.WriteString(out, ""+val+"") } func printCellFloatFmt(val float64, format string, out io.Writer) { io.WriteString(out, fmt.Sprintf(""+format+"", val)) } func printCellFloat(val float64, out io.Writer) { printCellFloatFmt(val, "%.2f", out) } func printFooter() { fmt.Println(` `) } func readCsvLine(line []string) *FinancialData { data := new(FinancialData) for i, col := range line { switch COLUMNS[i] { case "Date": data.Date = col case "Open": data.Open = toFloat(col) case "High": data.High = toFloat(col) case "Low": data.Low = toFloat(col) case "Close": data.Close = toFloat(col) case "Adj Close": data.Adj_Close = toFloat(col) case "Volume": data.Volume = toFloat(col) } } return data } func toFloat(v string) float64 { f, err := strconv.ParseFloat(v, 64) if err != nil { log.Fatalln(err) } return f } func readCsvHeader(line []string) { for _, col := range line { COLUMNS = append(COLUMNS, col) } } func readCsv(in io.Reader) []FinancialData { data := []FinancialData{} csvReader := csv.NewReader(in) for { line, err := csvReader.Read() if err == io.EOF { break } else if err != nil { log.Fatal(err) } if len(COLUMNS) == 0 { readCsvHeader(line) } else { data = append(data, *readCsvLine(line)) } } return data } func main() { if len(os.Args) < 2 { log.Fatalln("Usage go-financial ") } // read input csv, err := os.Open(os.Args[1]) if err != nil { log.Fatalln("Error reading file", err) } defer csv.Close() // output var out io.Writer if len(os.Args) > 2 { fout, err := os.Create(os.Args[2]) if err != nil { log.Fatalln("Error creating output file", err) } defer fout.Close() out = fout } else { out = os.Stdout } // parse data data := readCsv(csv) printHTML(data, out) }