diff --git a/layout.go b/layout.go
index 43f1519..d167e01 100644
--- a/layout.go
+++ b/layout.go
@@ -53,26 +53,24 @@ func (self *Layout) Market(market *Market) string {
return err
}
+ buffer := new(bytes.Buffer)
markup := `{{.Dow.name}}: {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}}, {{.Sp500.name}}: {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}}, {{.Nasdaq.name}}: {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}}
{{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. {{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq {{if .IsClosed}}U.S. markets closed{{end}}
New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. New lows: {{.Lows.nyse}} on NYSE and {{.Lows.nasdaq}} on Nasdaq.`
- template, err := template.New(`market`).Parse(markup)
- if err != nil {
- panic(err)
- }
-
- buffer := new(bytes.Buffer)
highlight(market.Dow, market.Sp500, market.Nasdaq)
- if err := template.Execute(buffer, market); err != nil {
- panic(err)
- }
+ template,_ := template.New(`market`).Parse(markup)
+ template.Execute(buffer, market)
return buffer.String()
}
//-----------------------------------------------------------------------------
func (self *Layout) Quotes(quotes *Quotes) string {
+ if ok, err := quotes.Ok(); !ok {
+ return err
+ }
+
vars := struct {
Now string
Header string
@@ -83,6 +81,7 @@ func (self *Layout) Quotes(quotes *Quotes) string {
self.prettify(quotes),
}
+ buffer := new(bytes.Buffer)
markup := `{{.Now}}>
@@ -90,17 +89,9 @@ func (self *Layout) Quotes(quotes *Quotes) string {
{{.Header}}
{{range.Stocks}}{{if .Advancing}}{{end}}{{.Ticker}}{{.LastTrade}}{{.Change}}{{.ChangePct}}{{.Open}}{{.Low}}{{.High}}{{.Low52}}{{.High52}}{{.Volume}}{{.AvgVolume}}{{.PeRatio}}{{.Dividend}}{{.Yield}}{{.MarketCap}}>
{{end}}`
- //markup += fmt.Sprintf("[%v]", quotes.profile.Grouped)
- template, err := template.New(`quotes`).Parse(markup)
- if err != nil {
- panic(err)
- }
- buffer := new(bytes.Buffer)
- err = template.Execute(buffer, vars)
- if err != nil {
- panic(err)
- }
+ template,_ := template.New(`quotes`).Parse(markup)
+ template.Execute(buffer, vars)
return buffer.String()
}
diff --git a/yahoo_market.go b/yahoo_market.go
index c3c96e2..8cb9e5f 100644
--- a/yahoo_market.go
+++ b/yahoo_market.go
@@ -1,6 +1,7 @@
// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//-----------------------------------------------------------------------------
package mop
@@ -13,7 +14,7 @@ import (
`strings`
)
-const url = `http://finance.yahoo.com/marketupdate/overview`
+const market_url = `http://finance.yahoo.com/marketupdate/overview`
type Market struct {
IsClosed bool
@@ -67,14 +68,14 @@ func (self *Market) Initialize() *Market {
//-----------------------------------------------------------------------------
func (self *Market) Fetch() (this *Market) {
- this = self // <-- This ensures we return correct self in case of panic attack.
+ this = self // <-- This ensures we return correct self after recover() from panic() attack.
defer func() {
if err := recover(); err != nil {
self.errors = fmt.Sprintf("Error fetching market data...\n%s", err)
}
}()
- response, err := http.Get(url)
+ response, err := http.Get(market_url)
if err != nil {
panic(err)
}
@@ -124,7 +125,7 @@ func (self *Market) trim(body []byte) []byte {
func (self *Market) extract(snippet []byte) *Market {
matches := self.regex.FindAllStringSubmatch(string(snippet), -1)
if len(matches) < 1 || len(matches[0]) < 37 {
- panic(`Unable to parse ` + url)
+ panic(`Unable to parse ` + market_url)
}
self.Dow[`name`] = matches[0][1]
diff --git a/yahoo_quotes.go b/yahoo_quotes.go
index d60610f..60268c6 100644
--- a/yahoo_quotes.go
+++ b/yahoo_quotes.go
@@ -1,6 +1,7 @@
// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//-----------------------------------------------------------------------------
package mop
@@ -35,7 +36,7 @@ import (
// j3: market cap rt
// j1: market cap
-const yahoo_quotes_url = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=,l1c6k2oghjkva2r2rdyj3j1`
+const quotes_url = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=,l1c6k2oghjkva2r2rdyj3j1`
type Stock struct {
Ticker string
@@ -59,51 +60,56 @@ type Stock struct {
}
type Quotes struct {
- market *Market
- profile *Profile
- stocks []Stock
+ market *Market
+ profile *Profile
+ stocks []Stock
+ errors string
}
//-----------------------------------------------------------------------------
func (self *Quotes) Initialize(market *Market, profile *Profile) *Quotes {
self.market = market
self.profile = profile
+ self.errors = ``
return self
}
// Fetch the latest stock quotes and parse raw fetched data into array of
// []Stock structs.
-func (self *Quotes) Fetch() *Quotes {
- if self.Ready() {
- // Format the URL and send the request.
- url := fmt.Sprintf(yahoo_quotes_url, self.profile.ListOfTickers())
+//-----------------------------------------------------------------------------
+func (self *Quotes) Fetch() (this *Quotes) {
+ this = self // <-- This ensures we return correct self after recover() from panic() attack.
+ if self.is_ready() {
+ defer func() {
+ if err := recover(); err != nil {
+ self.errors = fmt.Sprintf("\n\n\n\nError fetching stock quotes...\n%s", err)
+ }
+ }()
+
+ url := fmt.Sprintf(quotes_url, self.profile.ListOfTickers())
response, err := http.Get(url)
if err != nil {
panic(err)
}
- // Fetch response and get its body.
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
panic(err)
}
- self.parse(self.sanitize(body))
+ self.parse(sanitize(body))
}
return self
}
-// Return true if we haven't fetched the quotes yet *or* the stock market is
-// still open and we might want to grab the latest quotes. In both cases we
-// make sure the list of requested tickers is not empty.
-func (self *Quotes) Ready() bool {
- return (self.stocks == nil || !self.market.IsClosed) && len(self.profile.Tickers) > 0
+//-----------------------------------------------------------------------------
+func (self *Quotes) Ok() (bool, string) {
+ return self.errors == ``, self.errors
}
-
//-----------------------------------------------------------------------------
func (self *Quotes) Format() string {
return new(Layout).Initialize().Quotes(self)
@@ -125,6 +131,17 @@ func (self *Quotes) RemoveTickers(tickers []string) (removed int, err error) {
return
}
+// "Private" methods.
+
+// Return true if we haven't fetched the quotes yet *or* the stock market is
+// still open and we might want to grab the latest quotes. In both cases we
+// make sure the list of requested tickers is not empty.
+//-----------------------------------------------------------------------------
+func (self *Quotes) is_ready() bool {
+ return (self.stocks == nil || !self.market.IsClosed) && len(self.profile.Tickers) > 0
+}
+
+
//-----------------------------------------------------------------------------
func (self *Quotes) parse(body []byte) *Quotes {
lines := bytes.Split(body, []byte{'\n'})
@@ -155,7 +172,9 @@ func (self *Quotes) parse(body []byte) *Quotes {
return self
}
+// Utility methods.
+
//-----------------------------------------------------------------------------
-func (self *Quotes) sanitize(body []byte) []byte {
+func sanitize(body []byte) []byte {
return bytes.Replace(bytes.TrimSpace(body), []byte{'"'}, []byte{}, -1)
}