You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mop/filter.go

95 lines
3.7 KiB

// 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
}