From e0b845368021d8cac39a9b855a9770f4000f914d Mon Sep 17 00:00:00 2001 From: Michael Dvorkin Date: Thu, 25 Jul 2013 22:08:38 -0700 Subject: [PATCH] Column sorting --- lib/layout.go | 122 ++++++++++++++++++++++++++++++-------------- lib/sorter.go | 91 +++++++++++++++++++++++++++++++++ lib/yahoo_market.go | 7 +-- lib/yahoo_quotes.go | 70 ++++++++++++------------- 4 files changed, 215 insertions(+), 75 deletions(-) create mode 100644 lib/sorter.go diff --git a/lib/layout.go b/lib/layout.go index 8b996bd..4bdf66e 100644 --- a/lib/layout.go +++ b/lib/layout.go @@ -8,6 +8,7 @@ import ( `regexp` `strings` `text/template` + `sort` `time` ) @@ -24,23 +25,23 @@ type Layout struct { //----------------------------------------------------------------------------- func (self *Layout) Initialize() *Layout { - self.columns = make([]Column, TotalColumns) - - self.columns[0] = Column{ -7, `Ticker`} - self.columns[1] = Column{ 10, `Last`} - self.columns[2] = Column{ 10, `Change`} - self.columns[3] = Column{ 10, `%Change`} - self.columns[4] = Column{ 10, `Open`} - self.columns[5] = Column{ 10, `Low`} - self.columns[6] = Column{ 10, `High`} - self.columns[7] = Column{ 10, `52w Low`} - self.columns[8] = Column{ 10, `52w High`} - self.columns[9] = Column{ 11, `Volume`} - self.columns[10] = Column{ 11, `AvgVolume`} - self.columns[11] = Column{ 10, `P/E`} - self.columns[12] = Column{ 10, `Dividend`} - self.columns[13] = Column{ 10, `Yield`} - self.columns[14] = Column{ 11, `MktCap`} + self.columns = []Column{ + { -7, `Ticker`}, + { 10, `Last`}, + { 10, `Change`}, + { 10, `%Change`}, + { 10, `Open`}, + { 10, `Low`}, + { 10, `High`}, + { 10, `52w Low`}, + { 10, `52w High`}, + { 11, `Volume`}, + { 11, `AvgVolume`}, + { 10, `P/E`}, + { 10, `Dividend`}, + { 10, `Yield`}, + { 11, `MktCap`}, + } return self } @@ -68,7 +69,7 @@ func (self *Layout) Market(m *Market) string { markup += "\n" markup += `{{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. ` markup += `{{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq` - if !m.Open { + if m.IsClosed { markup += `U.S. markets closed` } markup += "\n" @@ -105,7 +106,7 @@ func (self *Layout) Quotes(quotes *Quotes) string { {{.Header}} -{{range.Stocks}}{{.Color}}{{.Ticker}}{{.LastTrade}}{{.Change}}{{.ChangePercent}}{{.Open}}{{.Low}}{{.High}}{{.Low52}}{{.High52}}{{.Volume}}{{.AvgVolume}}{{.PeRatio}}{{.Dividend}}{{.Yield}}{{.MarketCap}} +{{range.Stocks}}{{.Color}}{{.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) @@ -140,24 +141,71 @@ func (self *Layout) Header(profile *Profile) string { //----------------------------------------------------------------------------- func (self *Layout) prettify(quotes *Quotes) []Stock { + var sorts []sort.Interface pretty := make([]Stock, len(quotes.stocks)) - for i, q := range group(quotes) { - pretty[i].Ticker = pad(q.Ticker, self.columns[0].width) - pretty[i].LastTrade = pad(with_currency(q.LastTrade), self.columns[1].width) - pretty[i].Change = pad(with_currency(q.Change), self.columns[2].width) - pretty[i].ChangePercent = pad(last_of_pair(q.ChangePercent), self.columns[3].width) - pretty[i].Open = pad(with_currency(q.Open), self.columns[4].width) - pretty[i].Low = pad(with_currency(q.Low), self.columns[5].width) - pretty[i].High = pad(with_currency(q.High), self.columns[6].width) - pretty[i].Low52 = pad(with_currency(q.Low52), self.columns[7].width) - pretty[i].High52 = pad(with_currency(q.High52), self.columns[8].width) - pretty[i].Volume = pad(q.Volume, self.columns[9].width) - pretty[i].AvgVolume = pad(q.AvgVolume, self.columns[10].width) - pretty[i].PeRatio = pad(nullify(q.PeRatioX), self.columns[11].width) - pretty[i].Dividend = pad(with_currency(q.Dividend), self.columns[12].width) - pretty[i].Yield = pad(with_percent(q.Yield), self.columns[13].width) - pretty[i].MarketCap = pad(with_currency(q.MarketCapX), self.columns[14].width) + + //for i, q := range group(quotes) { + for i, q := range quotes.stocks { + pretty[i].Ticker = pad(q.Ticker, self.columns[0].width) + pretty[i].LastTrade = pad(with_currency(q.LastTrade), self.columns[1].width) + pretty[i].Change = pad(with_currency(q.Change), self.columns[2].width) + pretty[i].ChangePct = pad(last_of_pair(q.ChangePct), self.columns[3].width) + pretty[i].Open = pad(with_currency(q.Open), self.columns[4].width) + pretty[i].Low = pad(with_currency(q.Low), self.columns[5].width) + pretty[i].High = pad(with_currency(q.High), self.columns[6].width) + pretty[i].Low52 = pad(with_currency(q.Low52), self.columns[7].width) + pretty[i].High52 = pad(with_currency(q.High52), self.columns[8].width) + pretty[i].Volume = pad(q.Volume, self.columns[9].width) + pretty[i].AvgVolume = pad(q.AvgVolume, self.columns[10].width) + pretty[i].PeRatio = pad(nullify(q.PeRatioX), self.columns[11].width) + pretty[i].Dividend = pad(with_currency(q.Dividend), self.columns[12].width) + pretty[i].Yield = pad(with_percent(q.Yield), self.columns[13].width) + pretty[i].MarketCap = pad(with_currency(q.MarketCapX), self.columns[14].width) + } + + if quotes.profile.Ascending { + sorts = []sort.Interface{ + ByTickerAsc { pretty }, + ByLastTradeAsc { pretty }, + ByChangeAsc { pretty }, + ByChangePctAsc { pretty }, + ByOpenAsc { pretty }, + ByLowAsc { pretty }, + ByHighAsc { pretty }, + ByLow52Asc { pretty }, + ByHigh52Asc { pretty }, + ByVolumeAsc { pretty }, + ByAvgVolumeAsc { pretty }, + ByPeRatioAsc { pretty }, + //ByPeRatioXAsc { pretty }, + ByDividendAsc { pretty }, + ByYieldAsc { pretty }, + ByMarketCapAsc { pretty }, + //ByMarketCapXAsc { pretty }, + } + } else { + sorts = []sort.Interface{ + ByTickerDesc { pretty }, + ByLastTradeDesc { pretty }, + ByChangeDesc { pretty }, + ByChangePctDesc { pretty }, + ByOpenDesc { pretty }, + ByLowDesc { pretty }, + ByHighDesc { pretty }, + ByLow52Desc { pretty }, + ByHigh52Desc { pretty }, + ByVolumeDesc { pretty }, + ByAvgVolumeDesc { pretty }, + ByPeRatioDesc { pretty }, + //ByPeRatioXDesc { pretty }, + ByDividendDesc { pretty }, + ByYieldDesc { pretty }, + ByMarketCapDesc { pretty }, + //ByMarketCapXDesc { pretty }, + } } + + sort.Sort(sorts[quotes.profile.SortColumn]) return pretty } @@ -188,9 +236,9 @@ func group(quotes *Quotes) []Stock { func arrow_for(column int, profile *Profile) string { if column == profile.SortColumn { if profile.Ascending { - return string('\U00002193') - } else { return string('\U00002191') + } else { + return string('\U00002193') } } return `` diff --git a/lib/sorter.go b/lib/sorter.go new file mode 100644 index 0000000..27d15ae --- /dev/null +++ b/lib/sorter.go @@ -0,0 +1,91 @@ +// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved. +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +package mop + +import ( + `strings` + `strconv` +) + +type Sortable []Stock +func (list Sortable) Len() int { return len(list) } +func (list Sortable) Swap(i, j int) { list[i], list[j] = list[j], list[i] } + +type ByTickerAsc struct { Sortable } +type ByLastTradeAsc struct { Sortable } +type ByChangeAsc struct { Sortable } +type ByChangePctAsc struct { Sortable } +type ByOpenAsc struct { Sortable } +type ByLowAsc struct { Sortable } +type ByHighAsc struct { Sortable } +type ByLow52Asc struct { Sortable } +type ByHigh52Asc struct { Sortable } +type ByVolumeAsc struct { Sortable } +type ByAvgVolumeAsc struct { Sortable } +type ByPeRatioAsc struct { Sortable } +type ByPeRatioXAsc struct { Sortable } +type ByDividendAsc struct { Sortable } +type ByYieldAsc struct { Sortable } +type ByMarketCapAsc struct { Sortable } +type ByMarketCapXAsc struct { Sortable } + +type ByTickerDesc struct { Sortable } +type ByLastTradeDesc struct { Sortable } +type ByChangeDesc struct { Sortable } +type ByChangePctDesc struct { Sortable } +type ByOpenDesc struct { Sortable } +type ByLowDesc struct { Sortable } +type ByHighDesc struct { Sortable } +type ByLow52Desc struct { Sortable } +type ByHigh52Desc struct { Sortable } +type ByVolumeDesc struct { Sortable } +type ByAvgVolumeDesc struct { Sortable } +type ByPeRatioDesc struct { Sortable } +type ByPeRatioXDesc struct { Sortable } +type ByDividendDesc struct { Sortable } +type ByYieldDesc struct { Sortable } +type ByMarketCapDesc struct { Sortable } +type ByMarketCapXDesc struct { Sortable } + + +func (list ByTickerAsc) Less(i, j int) bool { return list.Sortable[i].Ticker < list.Sortable[j].Ticker } +func (list ByLastTradeAsc) Less(i, j int) bool { return list.Sortable[i].LastTrade < list.Sortable[j].LastTrade } +func (list ByChangeAsc) Less(i, j int) bool { return z(list.Sortable[i].Change) < z(list.Sortable[j].Change) } +func (list ByChangePctAsc) Less(i, j int) bool { return z(list.Sortable[i].ChangePct) < z(list.Sortable[j].ChangePct) } +func (list ByOpenAsc) Less(i, j int) bool { return list.Sortable[i].Open < list.Sortable[j].Open } +func (list ByLowAsc) Less(i, j int) bool { return list.Sortable[i].Low < list.Sortable[j].Low } +func (list ByHighAsc) Less(i, j int) bool { return list.Sortable[i].High < list.Sortable[j].High } +func (list ByLow52Asc) Less(i, j int) bool { return list.Sortable[i].Low52 < list.Sortable[j].Low52 } +func (list ByHigh52Asc) Less(i, j int) bool { return list.Sortable[i].High52 < list.Sortable[j].High52 } +func (list ByVolumeAsc) Less(i, j int) bool { return list.Sortable[i].Volume < list.Sortable[j].Volume } +func (list ByAvgVolumeAsc) Less(i, j int) bool { return list.Sortable[i].AvgVolume < list.Sortable[j].AvgVolume } +func (list ByPeRatioAsc) Less(i, j int) bool { return list.Sortable[i].PeRatio < list.Sortable[j].PeRatio } +func (list ByPeRatioXAsc) Less(i, j int) bool { return list.Sortable[i].PeRatioX < list.Sortable[j].PeRatioX } +func (list ByDividendAsc) Less(i, j int) bool { return list.Sortable[i].Dividend < list.Sortable[j].Dividend } +func (list ByYieldAsc) Less(i, j int) bool { return list.Sortable[i].Yield < list.Sortable[j].Yield } +func (list ByMarketCapAsc) Less(i, j int) bool { return list.Sortable[i].MarketCap < list.Sortable[j].MarketCap } +func (list ByMarketCapXAsc) Less(i, j int) bool { return list.Sortable[i].MarketCapX < list.Sortable[j].MarketCapX } + +func (list ByTickerDesc) Less(i, j int) bool { return list.Sortable[j].Ticker < list.Sortable[i].Ticker } +func (list ByLastTradeDesc) Less(i, j int) bool { return list.Sortable[j].LastTrade < list.Sortable[i].LastTrade } +func (list ByChangeDesc) Less(i, j int) bool { return z(list.Sortable[j].Change) < z(list.Sortable[i].Change) } +func (list ByChangePctDesc) Less(i, j int) bool { return z(list.Sortable[j].ChangePct) < z(list.Sortable[i].ChangePct) } +func (list ByOpenDesc) Less(i, j int) bool { return list.Sortable[j].Open < list.Sortable[i].Open } +func (list ByLowDesc) Less(i, j int) bool { return list.Sortable[j].Low < list.Sortable[i].Low } +func (list ByHighDesc) Less(i, j int) bool { return list.Sortable[j].High < list.Sortable[i].High } +func (list ByLow52Desc) Less(i, j int) bool { return list.Sortable[j].Low52 < list.Sortable[i].Low52 } +func (list ByHigh52Desc) Less(i, j int) bool { return list.Sortable[j].High52 < list.Sortable[i].High52 } +func (list ByVolumeDesc) Less(i, j int) bool { return list.Sortable[j].Volume < list.Sortable[i].Volume } +func (list ByAvgVolumeDesc) Less(i, j int) bool { return list.Sortable[j].AvgVolume < list.Sortable[i].AvgVolume } +func (list ByPeRatioDesc) Less(i, j int) bool { return list.Sortable[j].PeRatio < list.Sortable[i].PeRatio } +func (list ByPeRatioXDesc) Less(i, j int) bool { return list.Sortable[j].PeRatioX < list.Sortable[i].PeRatioX } +func (list ByDividendDesc) Less(i, j int) bool { return list.Sortable[j].Dividend < list.Sortable[i].Dividend } +func (list ByYieldDesc) Less(i, j int) bool { return list.Sortable[j].Yield < list.Sortable[i].Yield } +func (list ByMarketCapDesc) Less(i, j int) bool { return list.Sortable[j].MarketCap < list.Sortable[i].MarketCap } +func (list ByMarketCapXDesc) Less(i, j int) bool { return list.Sortable[j].MarketCapX < list.Sortable[i].MarketCapX } + +func z(str string) float32 { + float := strings.Replace(strings.Trim(str, ` %`), `$`, ``, 1) + value,_ := strconv.ParseFloat(float, 32) + return float32(value) +} \ No newline at end of file diff --git a/lib/yahoo_market.go b/lib/yahoo_market.go index 83469d2..82d4900 100644 --- a/lib/yahoo_market.go +++ b/lib/yahoo_market.go @@ -11,7 +11,7 @@ import ( ) type Market struct { - Open bool + IsClosed bool Dow map[string]string Nasdaq map[string]string Sp500 map[string]string @@ -24,7 +24,7 @@ type Market struct { //----------------------------------------------------------------------------- func (self *Market) Initialize() *Market { - self.Open = true + self.IsClosed = false self.Dow = make(map[string]string) self.Nasdaq = make(map[string]string) self.Sp500 = make(map[string]string) @@ -65,7 +65,8 @@ func (self *Market) Format() string { func (self *Market) check_if_market_open(body []byte) []byte { start := bytes.Index(body, []byte(`id="yfs_market_time"`)) finish := start + bytes.Index(body[start:], []byte(``)) - self.Open = !bytes.Contains(body[start:finish], []byte(`closed`)) + snippet := body[start:finish] + self.IsClosed = bytes.Contains(snippet, []byte(`closed`)) || bytes.Contains(snippet, []byte(`open in`)) return body[finish:] } diff --git a/lib/yahoo_quotes.go b/lib/yahoo_quotes.go index ef01e44..db45094 100644 --- a/lib/yahoo_quotes.go +++ b/lib/yahoo_quotes.go @@ -37,23 +37,23 @@ import ( const yahoo_quotes_url = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=,l1c6k2oghjkva2r2rdyj3j1` type Stock struct { - Ticker string - LastTrade string - Change string - ChangePercent string - Open string - Low string - High string - Low52 string - High52 string - Volume string - AvgVolume string - PeRatio string - PeRatioX string - Dividend string - Yield string - MarketCap string - MarketCapX string + Ticker string + LastTrade string + Change string + ChangePct string + Open string + Low string + High string + Low52 string + High52 string + Volume string + AvgVolume string + PeRatio string + PeRatioX string + Dividend string + Yield string + MarketCap string + MarketCapX string } type Quotes struct { @@ -72,7 +72,7 @@ func (self *Quotes) Initialize(market *Market, profile *Profile) *Quotes { //----------------------------------------------------------------------------- func (self *Quotes) Fetch() *Quotes { - if self.market.Open || self.stocks == nil { + if !self.market.IsClosed || self.stocks == nil { // Format the URL and send the request. url := fmt.Sprintf(yahoo_quotes_url, self.profile.ListOfTickers()) response, err := http.Get(url) @@ -121,23 +121,23 @@ func (self *Quotes) parse(body []byte) *Quotes { for i, line := range lines { columns := bytes.Split(bytes.TrimSpace(line), []byte{','}) - self.stocks[i].Ticker = string(columns[0]) - self.stocks[i].LastTrade = string(columns[1]) - self.stocks[i].Change = string(columns[2]) - self.stocks[i].ChangePercent = string(columns[3]) - self.stocks[i].Open = string(columns[4]) - self.stocks[i].Low = string(columns[5]) - self.stocks[i].High = string(columns[6]) - self.stocks[i].Low52 = string(columns[7]) - self.stocks[i].High52 = string(columns[8]) - self.stocks[i].Volume = string(columns[9]) - self.stocks[i].AvgVolume = string(columns[10]) - self.stocks[i].PeRatio = string(columns[11]) - self.stocks[i].PeRatioX = string(columns[12]) - self.stocks[i].Dividend = string(columns[13]) - self.stocks[i].Yield = string(columns[14]) - self.stocks[i].MarketCap = string(columns[15]) - self.stocks[i].MarketCapX = string(columns[16]) + self.stocks[i].Ticker = string(columns[0]) + self.stocks[i].LastTrade = string(columns[1]) + self.stocks[i].Change = string(columns[2]) + self.stocks[i].ChangePct = string(columns[3]) + self.stocks[i].Open = string(columns[4]) + self.stocks[i].Low = string(columns[5]) + self.stocks[i].High = string(columns[6]) + self.stocks[i].Low52 = string(columns[7]) + self.stocks[i].High52 = string(columns[8]) + self.stocks[i].Volume = string(columns[9]) + self.stocks[i].AvgVolume = string(columns[10]) + self.stocks[i].PeRatio = string(columns[11]) + self.stocks[i].PeRatioX = string(columns[12]) + self.stocks[i].Dividend = string(columns[13]) + self.stocks[i].Yield = string(columns[14]) + self.stocks[i].MarketCap = string(columns[15]) + self.stocks[i].MarketCapX = string(columns[16]) } return self