一、需求分析
股价提醒系统具备以下基本功能:
- 实时获取股价:能够从金融数据接口获取指定股票的实时价格。
- 设置提醒条件:用户可以设置目标股价,当股价达到设定值时触发提醒。
- 提醒通知:通过邮件(或其他方式)向用户发送提醒信息。
二、技术选型
- 数据获取:使用第三方金融数据 API,如Alpha Vantage(https://www.alphavantage.co/) ,它提供丰富的金融数据,且有免费的使用额度。
- 邮件发送:Go 语言的net/smtp包可以实现邮件发送功能,用于发送提醒邮件。
- HTTP 服务器:利用net/http包搭建一个简单的 HTTP 服务器,方便用户设置提醒条件。
三、项目搭建
创建Go 项目(stock_alert_system),并初始化 Go 模块:
mkdir stock_alert_system
cd stock_alert_system
go mod init stock_alert_system
四、代码实现
1. 获取股价
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type StockData struct {
TimeSeriesDaily struct {
"TimeSeriesDaily": map[string]map[string]string
}
}
func getStockPrice(symbol string, apiKey string) (float64, error) {
url := fmt.Sprintf("https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=%s&apikey=%s", symbol, apiKey)
resp, err := http.Get(url)
if err!= nil {
return 0, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err!= nil {
return 0, err
}
var stockData StockData
err = json.Unmarshal(body, &stockData)
if err!= nil {
return 0, err
}
latestDate := ""
for date := range stockData.TimeSeriesDaily {
if latestDate == "" || date > latestDate {
latestDate = date
}
}
priceStr := stockData.TimeSeriesDaily[latestDate]["4. close"]
var price float64
_, err = fmt.Sscanf(priceStr, "%f", &price)
if err!= nil {
return 0, err
}
return price, nil
}
2. 发送邮件提醒
package main
import (
"fmt"
"log"
"net/smtp"
)
func sendEmail(to, subject, body string) {
from := "your_email@example.com"
password := "your_email_password"
msg := []byte("To: " + to + "\r\n" +
"Subject: " + subject + "\r\n" +
"\r\n" +
body + "\r\n")
auth := smtp.PlainAuth("", from, password, "smtp.example.com")
err := smtp.SendMail("smtp.example.com:587", auth, from, []string{to}, msg)
if err!= nil {
log.Println("Error sending email:", err)
return
}
fmt.Println("Email sent successfully!")
}
3. HTTP 服务器
package main
import (
"fmt"
"html/template"
"log"
"net/http"
)
type Alert struct {
Symbol string
Price float64
To string
}
var alerts []Alert
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/add_alert", addAlertHandler)
log.Println("Server is running on http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles("templates/home.html")
if err!= nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(w, nil)
}
func addAlertHandler(w http.ResponseWriter, r *http.Request) {
if r.Method!= http.MethodPost {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}
symbol := r.FormValue("symbol")
priceStr := r.FormValue("price")
to := r.FormValue("to")
var price float64
_, err := fmt.Sscanf(priceStr, "%f", &price)
if err!= nil {
http.Error(w, "Invalid price format", http.StatusBadRequest)
return
}
alert := Alert{
Symbol: symbol,
Price: price,
To: to,
}
alerts = append(alerts, alert)
go checkAlerts()
http.Redirect(w, r, "/", http.StatusFound)
}
func checkAlerts() {
apiKey := "your_api_key"
for {
for _, alert := range alerts {
price, err := getStockPrice(alert.Symbol, apiKey)
if err!= nil {
log.Println("Error getting stock price:", err)
continue
}
if price >= alert.Price {
subject := fmt.Sprintf("股价提醒:%s 已达到目标价格", alert.Symbol)
body := fmt.Sprintf("当前 %s 的股价为 %.2f,已达到您设定的目标价格 %.2f。", alert.Symbol, price, alert.Price)
sendEmail(alert.To, subject, body)
}
}
}
}