// Copyright (c) 2013-2019 by Michael Dvorkin and contributors. 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 import ( "strings" "strconv" ) // Filter gets called to sort stock quotes by one of the columns. The // setup is rather lengthy; there should probably be more concise way // that uses reflection and avoids hardcoding the column names. type Filter struct { profile *Profile // Pointer to where we store sort column and order. } // Returns new Filter struct. func NewFilter(profile *Profile) *Filter { return &Filter{ profile: profile, } } // Changes money and % notation to a plain float for math, comparisons. func stringToNumber (numberString string) float64 { // If the string "$3.6B" is passed in, the returned float will be 3.6E+09. // If 0.03% is passed in, the returned float will be 0.03 (NOT 0.0003!). newString := strings.TrimSpace(numberString) // Take off whitespace. newString = strings.Replace(newString,"$","",1) // Remove the $ symbol. newString = strings.Replace(newString,"%","",1) // Remove the $ symbol. newString = strings.Replace(newString,"K","E+3",1) // Thousand (kilo) newString = strings.Replace(newString,"M","E+6",1) // Million newString = strings.Replace(newString,"B","E+9",1) // Billion newString = strings.Replace(newString,"T","E+12",1) // Trillion finalValue, _ := strconv.ParseFloat(newString, 64) return finalValue } // Apply builds a list of sort interface based on current sort // order, then calls sort.Sort to do the actual job. func (filter *Filter) Apply(stocks []Stock) []Stock { var filteredStocks []Stock for _, stock := range stocks { var values = make(map[string]interface{}) // Make conversions from the strings to floats where necessary. values["ticker"] = strings.TrimSpace(stock.Ticker) // Remains string values["last"] = stringToNumber(stock.LastTrade) values["change"] = stringToNumber(stock.Change) values["changePercent"] = stringToNumber(stock.ChangePct) values["open"] = stringToNumber(stock.Open) values["low"] = stringToNumber(stock.Low) values["high"] = stringToNumber(stock.High) values["low52"] = stringToNumber(stock.Low52) values["high52"] = stringToNumber(stock.High52) values["dividend"] = stringToNumber(stock.Dividend) values["yield"] = stringToNumber(stock.Yield) values["mktCap"] = stringToNumber(stock.MarketCap) values["mktCapX"] = stringToNumber(stock.MarketCapX) values["volume"] = stringToNumber(stock.Volume) values["avgVolume"] = stringToNumber(stock.AvgVolume) values["pe"] = stringToNumber(stock.PeRatio) values["peX"] = stringToNumber(stock.PeRatioX) values["direction"] = stock.Direction // Remains int. result, err := filter.profile.filterExpression.Evaluate(values) if err != nil { // The filter isn't working, so reset to no filter. filter.profile.Filter = "" // Return an empty list. The next main loop cycle will // show unfiltered. return filteredStocks } truthy, ok := result.(bool) if !ok { // The filter isn't working, so reset to no filter. filter.profile.Filter = "" // Return an empty list. The next main loop cycle will // show unfiltered. return filteredStocks } if truthy { filteredStocks = append(filteredStocks, stock) } } return filteredStocks }