1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
package ibd
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
"github.com/ansg191/ibd-trader-backend/internal/database"
)
const (
searchUrl = "https://ibdservices.investors.com/im/api/search"
)
var ErrSymbolNotFound = fmt.Errorf("symbol not found")
func (c *Client) Search(ctx context.Context, symbol string) (database.Stock, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchUrl, nil)
if err != nil {
return database.Stock{}, err
}
_, cookie, err := c.getCookie(ctx, nil)
if err != nil {
return database.Stock{}, err
}
req.AddCookie(cookie)
params := url.Values{}
params.Set("key", symbol)
req.URL.RawQuery = params.Encode()
resp, err := c.Do(req)
if err != nil {
return database.Stock{}, err
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
if resp.StatusCode != http.StatusOK {
content, err := io.ReadAll(resp.Body)
if err != nil {
return database.Stock{}, fmt.Errorf("failed to read response body: %w", err)
}
return database.Stock{}, fmt.Errorf(
"unexpected status code %d: %s",
resp.StatusCode,
string(content),
)
}
var sr searchResponse
if err = json.NewDecoder(resp.Body).Decode(&sr); err != nil {
return database.Stock{}, err
}
for _, stock := range sr.StockData {
if stock.Symbol == symbol {
return database.Stock{
Symbol: stock.Symbol,
Name: stock.Company,
IBDUrl: stock.QuoteUrl,
}, nil
}
}
return database.Stock{}, ErrSymbolNotFound
}
type searchResponse struct {
Status int `json:"_status"`
Timestamp string `json:"_timestamp"`
StockData []struct {
Id int `json:"id"`
Symbol string `json:"symbol"`
Company string `json:"company"`
PriceDate string `json:"priceDate"`
Price float64 `json:"price"`
PreviousPrice float64 `json:"previousPrice"`
PriceChange float64 `json:"priceChange"`
PricePctChange float64 `json:"pricePctChange"`
Volume int `json:"volume"`
VolumeChange int `json:"volumeChange"`
VolumePctChange int `json:"volumePctChange"`
QuoteUrl string `json:"quoteUrl"`
} `json:"stockData"`
News []struct {
Title string `json:"title"`
Category string `json:"category"`
Body string `json:"body"`
ImageAlt string `json:"imageAlt"`
ImageUrl string `json:"imageUrl"`
NewsUrl string `json:"newsUrl"`
CategoryUrl string `json:"categoryUrl"`
PublishDate time.Time `json:"publishDate"`
PublishDateUnixts int `json:"publishDateUnixts"`
Stocks []struct {
Id int `json:"id"`
Index int `json:"index"`
Symbol string `json:"symbol"`
PricePctChange string `json:"pricePctChange"`
} `json:"stocks"`
VideoFormat bool `json:"videoFormat"`
} `json:"news"`
FullUrl string `json:"fullUrl"`
}
|