Skip to content

Commit

Permalink
feat: Added option to view fundamentals of a symbol
Browse files Browse the repository at this point in the history
  • Loading branch information
achannarasappa committed Jan 31, 2021
1 parent 8bf1836 commit bf0e934
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 51 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ ticker -w NET,AAPL,TSLA
|-i|--interval|`5`|Refresh interval in seconds|
|-w|--watchlist||comma separated list of symbols to watch|
|-e|--extra-info-exchange||display currency, exchange name, and quote delay for each quote |
|-q|--extra-info-fundamentals||display open price, high, low, and volume for each quote |
| |--compact||compact layout without separators between each quote|

## Configuration
Expand Down
30 changes: 15 additions & 15 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,26 @@ import (
)

var (
configPath string
config cli.Config
watchlist string
refreshInterval int
compact bool
extraInfoExchange bool
extraInfoQuote bool
rootCmd = &cobra.Command{
configPath string
config cli.Config
watchlist string
refreshInterval int
compact bool
extraInfoExchange bool
extraInfoFundamentals bool
rootCmd = &cobra.Command{
Use: "ticker",
Short: "Terminal stock ticker and stock gain/loss tracker",
Args: cli.Validate(
&config,
afero.NewOsFs(),
cli.Options{
ConfigPath: &configPath,
RefreshInterval: &refreshInterval,
Watchlist: &watchlist,
Compact: &compact,
ExtraInfoExchange: &extraInfoExchange,
ExtraInfoQuote: &extraInfoQuote,
ConfigPath: &configPath,
RefreshInterval: &refreshInterval,
Watchlist: &watchlist,
Compact: &compact,
ExtraInfoExchange: &extraInfoExchange,
ExtraInfoFundamentals: &extraInfoFundamentals,
},
),
Run: cli.Run(ui.Start(&config)),
Expand All @@ -55,7 +55,7 @@ func init() {
rootCmd.Flags().IntVarP(&refreshInterval, "interval", "i", 0, "refresh interval in seconds")
rootCmd.Flags().BoolVar(&compact, "compact", false, "compact layout without separators between each quote")
rootCmd.Flags().BoolVar(&extraInfoExchange, "extra-info-exchange", false, "display currency, exchange name, and quote delay for each quote")
rootCmd.Flags().BoolVar(&extraInfoQuote, "extra-info-quote", false, "display open price, high, low, and volume for each quote")
rootCmd.Flags().BoolVar(&extraInfoFundamentals, "extra-info-fundamentals", false, "display open price, high, low, and volume for each quote")
}

func initConfig() {
Expand Down
26 changes: 13 additions & 13 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@ import (
)

type Config struct {
RefreshInterval int `yaml:"interval"`
Watchlist []string `yaml:"watchlist"`
Lots []position.Lot `yaml:"lots"`
Compact bool `yaml:"compact"`
ExtraInfoExchange bool `yaml:"extra-info-exchange"`
ExtraInfoQuote bool `yaml:"extra-info-quote"`
RefreshInterval int `yaml:"interval"`
Watchlist []string `yaml:"watchlist"`
Lots []position.Lot `yaml:"lots"`
Compact bool `yaml:"compact"`
ExtraInfoExchange bool `yaml:"extra-info-exchange"`
ExtraInfoFundamentals bool `yaml:"extra-info-fundamentals"`
}

type Options struct {
ConfigPath *string
RefreshInterval *int
Watchlist *string
Compact *bool
ExtraInfoExchange *bool
ExtraInfoQuote *bool
ConfigPath *string
RefreshInterval *int
Watchlist *string
Compact *bool
ExtraInfoExchange *bool
ExtraInfoFundamentals *bool
}

func Run(uiStartFn func() error) func(*cobra.Command, []string) {
Expand Down Expand Up @@ -83,7 +83,7 @@ func read(fs afero.Fs, options Options, configFile *Config) (Config, error) {
config.RefreshInterval = getRefreshInterval(*options.RefreshInterval, configFile.RefreshInterval)
config.Compact = getBoolOption(*options.Compact, configFile.Compact)
config.ExtraInfoExchange = getBoolOption(*options.ExtraInfoExchange, configFile.ExtraInfoExchange)
config.ExtraInfoQuote = getBoolOption(*options.ExtraInfoQuote, configFile.ExtraInfoQuote)
config.ExtraInfoFundamentals = getBoolOption(*options.ExtraInfoFundamentals, configFile.ExtraInfoFundamentals)

return config, err

Expand Down
2 changes: 2 additions & 0 deletions internal/quote/quote.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type ResponseQuote struct {
RegularMarketChangePercent float64 `json:"regularMarketChangePercent"`
RegularMarketPrice float64 `json:"regularMarketPrice"`
RegularMarketPreviousClose float64 `json:"regularMarketPreviousClose"`
RegularMarketOpen float64 `json:"regularMarketOpen"`
RegularMarketDayRange string `json:"regularMarketDayRange"`
PostMarketChange float64 `json:"postMarketChange"`
PostMarketChangePercent float64 `json:"postMarketChangePercent"`
PostMarketPrice float64 `json:"postMarketPrice"`
Expand Down
61 changes: 39 additions & 22 deletions internal/ui/component/watchlist/watchlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ const (
)

type Model struct {
Width int
Quotes []quote.Quote
Positions map[string]position.Position
Compact bool
ExtraInfoExchange bool
ExtraInfoQuote bool
Width int
Quotes []quote.Quote
Positions map[string]position.Position
Compact bool
ExtraInfoExchange bool
ExtraInfoFundamentals bool
}

// NewModel returns a model with default values.
func NewModel(compact bool, extraInfoExchange bool, extraInfoQuote bool) Model {
func NewModel(compact bool, extraInfoExchange bool, extraInfoFundamentals bool) Model {
return Model{
Width: 80,
Compact: compact,
ExtraInfoExchange: extraInfoExchange,
ExtraInfoQuote: extraInfoQuote,
Width: 80,
Compact: compact,
ExtraInfoExchange: extraInfoExchange,
ExtraInfoFundamentals: extraInfoFundamentals,
}
}

Expand All @@ -63,7 +63,7 @@ func (m Model) View() string {
strings.Join(
[]string{
item(quote, m.Positions[quote.Symbol], m.Width),
// extraInfoQuote(m.ExtraInfoQuote, quote, m.Width),
extraInfoFundamentals(m.ExtraInfoFundamentals, quote, m.Width),
extraInfoExchange(m.ExtraInfoExchange, quote, m.Width),
},
"",
Expand Down Expand Up @@ -136,10 +136,6 @@ func extraInfoExchange(show bool, q quote.Quote, width int) string {
}
return "\n" + Line(
width,
Cell{
Text: "",
Align: RightAlign,
},
Cell{
Text: tagText(q.ExchangeName) + " " + tagText(exchangeDelayText(q.ExchangeDelay)) + " " + tagText(q.Currency),
Align: RightAlign,
Expand All @@ -152,12 +148,33 @@ func extraInfoExchange(show bool, q quote.Quote, width int) string {
)
}

// func extraInfoQuote(show bool, q quote.Quote, width int) string {
// if !show {
// return ""
// }
// return ""
// }
func extraInfoFundamentals(show bool, q quote.Quote, width int) string {
if !show {
return ""
}

return "\n" + Line(
width,
Cell{
Width: 25,
Text: styleNeutralFaded("Prev Close: ") + styleNeutral(ConvertFloatToString(q.RegularMarketPreviousClose)),
},
Cell{
Width: 20,
Text: styleNeutralFaded("Open: ") + styleNeutral(ConvertFloatToString(q.RegularMarketOpen)),
},
Cell{
Text: dayRangeText(q.RegularMarketDayRange),
},
)
}

func dayRangeText(dayRange string) string {
if len(dayRange) <= 0 {
return ""
}
return styleNeutralFaded("Day Range: ") + styleNeutral(dayRange)
}

func exchangeDelayText(delay float64) string {
if delay <= 0 {
Expand Down
55 changes: 55 additions & 0 deletions internal/ui/component/watchlist/watchlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,61 @@ var _ = Describe("Watchlist", func() {
})
})

When("the option for extra fundamental information is set", func() {
It("should render extra fundamental information", func() {
m := NewModel(true, false, true)
m.Quotes = []Quote{
{
ResponseQuote: ResponseQuote{
Symbol: "BTC-USD",
ShortName: "Bitcoin",
RegularMarketPreviousClose: 10000.0,
RegularMarketOpen: 10000.0,
RegularMarketDayRange: "10000 - 10000",
},
Price: 50000.0,
Change: 10000.0,
ChangePercent: 20.0,
IsActive: true,
IsRegularTradingSession: true,
},
}
expected := strings.Join([]string{
"BTC-USD ⦿ 50000.00",
"Bitcoin ↑ 10000.00 (20.00%)",
"Prev Close: 10000.00 Open: 10000.00 Day Range: 10000 - 10000 ",
}, "\n")
Expect(removeFormatting(m.View())).To(Equal(expected))
})

When("there is no day range", func() {
It("should not render the day range field", func() {
m := NewModel(true, false, true)
m.Quotes = []Quote{
{
ResponseQuote: ResponseQuote{
Symbol: "BTC-USD",
ShortName: "Bitcoin",
RegularMarketPreviousClose: 10000.0,
RegularMarketOpen: 10000.0,
},
Price: 50000.0,
Change: 10000.0,
ChangePercent: 20.0,
IsActive: true,
IsRegularTradingSession: true,
},
}
expected := strings.Join([]string{
"BTC-USD ⦿ 50000.00",
"Bitcoin ↑ 10000.00 (20.00%)",
"Prev Close: 10000.00 Open: 10000.00 ",
}, "\n")
Expect(removeFormatting(m.View())).To(Equal(expected))
})
})
})

When("no quotes are set", func() {
It("should render an empty watchlist", func() {
m := NewModel(false, false, false)
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func NewModel(config cli.Config, client *resty.Client) Model {
requestInterval: 3,
getQuotes: quote.GetQuotes(*client, symbols),
getPositions: position.GetPositions(aggregatedLots),
watchlist: watchlist.NewModel(config.Compact, config.ExtraInfoExchange, config.ExtraInfoQuote),
watchlist: watchlist.NewModel(config.Compact, config.ExtraInfoExchange, config.ExtraInfoFundamentals),
}
}

Expand Down

0 comments on commit bf0e934

Please sign in to comment.