go-examples/go-financial/main.go

276 lines
5.5 KiB
Go
Raw Normal View History

2017-07-08 07:43:10 +00:00
package main
import "encoding/csv"
import "fmt"
import "io"
import "log"
import "os"
import "strconv"
2017-07-08 08:16:32 +00:00
import "strings"
2017-07-08 07:43:10 +00:00
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, `<!DOCTYPE html>
<html>
<head><title>Financial</title></head>
<body>
<table border="1">
<tr>
`)
for _, col := range COLUMNS {
io.WriteString(out, " <th>")
io.WriteString(out, col)
io.WriteString(out, "</th>\n")
}
io.WriteString(out, " </tr>\n")
// data
for _, row := range data {
row.printHTMLRow(out)
}
io.WriteString(out, ` </table>
<div id="container"></div>
<style>
#container {
min-width: 310px;
max-width: 800px;
height: 400px;
margin: 0 auto
}
</style>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
title: {
text: 'Financial data'
},
subtitle: {
text: 'Source: yahoo.com'
},
2017-07-08 08:16:32 +00:00
xAxis: {
type: 'datetime'
},
2017-07-08 07:43:10 +00:00
yAxis: {
title: {
text: 'Value'
}
},
2017-07-08 08:16:32 +00:00
plotOptions: {
area: {
fillColor: {
linearGradient: {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops: [
[0, Highcharts.getOptions().colors[0]],
[1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
]
},
marker: {
radius: 2
},
lineWidth: 1,
states: {
hover: {
lineWidth: 1
}
},
threshold: null
}
},
2017-07-08 07:43:10 +00:00
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
2017-07-08 08:16:32 +00:00
series: [{
type: 'area',
name: 'Open',
data: `+getChartDataOpen(data)+`
},{
type: 'area',
name: 'Close',
data: `+getChartDataClose(data)+`
}]
2017-07-08 07:43:10 +00:00
});
</script>
</body>
</html>`)
}
2017-07-08 08:16:32 +00:00
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 := "["
2017-07-08 07:43:10 +00:00
for i, v := range data {
2017-07-08 08:16:32 +00:00
str += "["
str += toJSDate(v.Date)
str += fmt.Sprintf(", %.4f", v.Open)
str += "]"
2017-07-08 07:43:10 +00:00
if i < len(data)-1 {
str += ","
}
}
2017-07-08 08:16:32 +00:00
str += "]"
2017-07-08 07:43:10 +00:00
return str
}
2017-07-08 08:16:32 +00:00
func getChartDataClose(data []FinancialData) string {
str := "["
2017-07-08 07:43:10 +00:00
for i, v := range data {
2017-07-08 08:16:32 +00:00
str += "["
str += toJSDate(v.Date)
str += fmt.Sprintf(", %.4f", v.Close)
str += "]"
2017-07-08 07:43:10 +00:00
if i < len(data)-1 {
str += ","
}
}
2017-07-08 08:16:32 +00:00
str += "]"
2017-07-08 07:43:10 +00:00
return str
}
func (data FinancialData) printHTMLRow(out io.Writer) {
io.WriteString(out, " <tr>")
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, "</tr>\n")
}
func printCellString(val string, out io.Writer) {
io.WriteString(out, "<td>"+val+"</td>")
}
func printCellFloatFmt(val float64, format string, out io.Writer) {
io.WriteString(out, fmt.Sprintf("<td>"+format+"</td>", val))
}
func printCellFloat(val float64, out io.Writer) {
printCellFloatFmt(val, "%.2f", out)
}
func printFooter() {
fmt.Println(` </table>
</body>
</html> `)
}
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 <file>")
}
// 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)
}