Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Terminal stock & crypto price watcher and position tracker
* Track value of your stock positions
* Support for multiple cost basis lots
* Support for pre and post market price quotes
* Optional Adanos market sentiment overlay for watchlists

## Install

Expand Down Expand Up @@ -82,10 +83,11 @@ ticker -w NET,AAPL,TSLA
|`watchlist` |-w|--watchlist | |comma separated list of symbols to watch|
|`show-tags` | |--show-tags | |display currency, exchange name, and quote delay for each quote |
|`show-fundamentals`| |--show-fundamentals| |display open price, previous close, and day range |
|`show-sentiment` | |--show-sentiment | |display optional Adanos market sentiment when `ADANOS_API_KEY` or `sentiment-api-key` is configured |
|`show-separator` | |--show-separator | |layout with separators between each quote|
|`show-summary` | |--show-summary | |show total day change, total value, and total value change|
|`show-positions` | |--show-positions | |show positions including weight, average cost, and quantity|
|`sort` | |--sort | |sort quotes on the UI - options are change percent (default), `alpha`, `value`, and `user`|
|`sort` | |--sort | |sort quotes on the UI - options are change percent (default), `alpha`, `value`, `user`, and `sentiment`|
|`version` | |--version | |print the current version number|
|`debug` | | | |enable debug logging to `./ticker-log-<date>.log`|

Expand All @@ -98,11 +100,13 @@ Configuration is not required to watch stock price but is helpful when always wa
show-summary: true
show-tags: true
show-fundamentals: true
show-sentiment: true
show-separator: true
show-positions: true
interval: 5
currency: USD
currency-summary-only: false
sentiment-api-key: "" # optional, or set ADANOS_API_KEY in the environment
watchlist:
- NET
- TEAM
Expand Down Expand Up @@ -133,14 +137,15 @@ groups:
```

* All properties in `.ticker.yaml` are optional
* Adanos sentiment is fully optional and remains disabled unless `show-sentiment` is enabled and an API key is configured
* Symbols not on the watchlist that exists in `lots` are implicitly added to the watchlist
* To add multiple cost basis lots (`quantity`, `unit_cost`) for the same `symbol`, include two or more entries - see `ARKW` example above
* `.ticker.yaml` can be set in user home directory, the current directory, or [XDG config home](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
* Quantities can be negative to represent closed positions (position netting), short positions, borrowed assets, and other concepts

### Display Options

With `--show-summary`, `--show-tags`, `--show-fundamentals`, `--show-positions`, and `--show-separator` options set, the layout and information displayed expands:
With `--show-summary`, `--show-tags`, `--show-fundamentals`, `--show-sentiment`, `--show-positions`, and `--show-separator` options set, the layout and information displayed expands:

<img src="./docs/ticker-all-options.png" />

Expand All @@ -152,6 +157,7 @@ It's possible to set a custom sort order with the `--sort` flag or `sort:` confi
* `alpha` to sort alphabetically by symbol
* `value` to sort by position value
* `user` to sort by the order defined in configuration with positions on first then watched symbols
* `sentiment` to sort by Adanos average buzz score when sentiment is available

### Groups

Expand Down
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ func init() { //nolint: gochecknoinits
rootCmd.Flags().BoolVar(&options.Separate, "show-separator", false, "layout with separators between each quote")
rootCmd.Flags().BoolVar(&options.ExtraInfoExchange, "show-tags", false, "display currency, exchange name, and quote delay for each quote")
rootCmd.Flags().BoolVar(&options.ExtraInfoFundamentals, "show-fundamentals", false, "display open price, high, low, and volume for each quote")
rootCmd.Flags().BoolVar(&options.ShowSentiment, "show-sentiment", false, "display Adanos market sentiment when ADANOS_API_KEY or sentiment-api-key is configured")
rootCmd.Flags().BoolVar(&options.ShowSummary, "show-summary", false, "display summary of total gain and loss for positions")
rootCmd.Flags().BoolVar(&options.ShowPositions, "show-positions", false, "display average unit cost, quantity, portfolio weight")
rootCmd.Flags().BoolVar(&options.ShowHoldings, "show-holdings", false, "display average unit cost, quantity, portfolio weight (deprecated: use --show-positions)")
rootCmd.Flags().StringVar(&options.Sort, "sort", "", "sort quotes on the UI. Set \"alpha\" to sort by ticker name. Set \"value\" to sort by position value. Keep empty to sort according to change percent")
rootCmd.Flags().StringVar(&options.Sort, "sort", "", "sort quotes on the UI. Set \"alpha\" to sort by ticker name. Set \"value\" to sort by position value. Set \"sentiment\" to sort by Adanos buzz. Keep empty to sort according to change percent")

printCmd.PersistentFlags().StringVar(&optionsPrint.Format, "format", "", "output format for printing holdings. Set \"csv\" to print as a CSV or \"json\" for JSON. Defaults to JSON.")
printCmd.PersistentFlags().StringVar(&configPath, "config", "", "config file (default is $HOME/.ticker.yaml)")
Expand Down
6 changes: 6 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Options struct {
Separate bool
ExtraInfoExchange bool
ExtraInfoFundamentals bool
ShowSentiment bool
ShowSummary bool
ShowHoldings bool // Deprecated: use ShowPositions instead, kept for backwards compatibility
ShowPositions bool // Preferred field name
Expand Down Expand Up @@ -121,6 +122,7 @@ func GetDependencies() c.Dependencies {
MonitorYahooSessionConsentURL: "https://consent.yahoo.com",
MonitorPriceCoinbaseBaseURL: "https://api.coinbase.com",
MonitorPriceCoinbaseStreamingURL: "wss://ws-feed.exchange.coinbase.com",
SentimentAdanosBaseURL: "https://api.adanos.org",
}
}

Expand Down Expand Up @@ -211,6 +213,7 @@ func GetConfig(dep c.Dependencies, configPath string, options Options) (c.Config
config.Separate = getBoolOption(options.Separate, config.Separate)
config.ExtraInfoExchange = getBoolOption(options.ExtraInfoExchange, config.ExtraInfoExchange)
config.ExtraInfoFundamentals = getBoolOption(options.ExtraInfoFundamentals, config.ExtraInfoFundamentals)
config.ShowSentiment = getBoolOption(options.ShowSentiment, config.ShowSentiment)
config.ShowSummary = getBoolOption(options.ShowSummary, config.ShowSummary)
// Merge ShowHoldings into ShowPositions with positions taking precedence
// First check if Positions is set (CLI or config), then fall back to Holdings if not
Expand All @@ -227,6 +230,9 @@ func GetConfig(dep c.Dependencies, configPath string, options Options) (c.Config
config.ShowPositions = showHoldingsFromCLI || showHoldingsFromConfig
}
config.Sort = getStringOption(options.Sort, config.Sort)
if config.SentimentAPIKey == "" {
config.SentimentAPIKey = strings.TrimSpace(os.Getenv("ADANOS_API_KEY"))
}

return config, nil
}
Expand Down
29 changes: 29 additions & 0 deletions internal/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,24 @@ var _ = Describe("Cli", func() {
}),
}),

Entry("when show-sentiment is set in config file", Case{
InputOptions: cli.Options{},
InputConfigFileContents: "show-sentiment: true",
AssertionErr: BeNil(),
AssertionConfig: g.MatchFields(g.IgnoreExtras, g.Fields{
"ShowSentiment": Equal(true),
}),
}),

Entry("when show-sentiment is set in options", Case{
InputOptions: cli.Options{ShowSentiment: true},
InputConfigFileContents: "",
AssertionErr: BeNil(),
AssertionConfig: g.MatchFields(g.IgnoreExtras, g.Fields{
"ShowSentiment": Equal(true),
}),
}),

// option: debug
Entry("when debug is set in config file", Case{
InputOptions: cli.Options{},
Expand All @@ -467,6 +485,17 @@ var _ = Describe("Cli", func() {
}),
)

It("should use ADANOS_API_KEY when sentiment-api-key is not set", func() {
Expect(os.Setenv("ADANOS_API_KEY", "sk_test_adanos")).To(Succeed())
DeferCleanup(func() {
Expect(os.Unsetenv("ADANOS_API_KEY")).To(Succeed())
})

outputConfig, outputErr := cli.GetConfig(dep, "", cli.Options{})
Expect(outputErr).To(BeNil())
Expect(outputConfig.SentimentAPIKey).To(Equal("sk_test_adanos"))
})

})

//nolint:errcheck
Expand Down
12 changes: 12 additions & 0 deletions internal/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ type Config struct {
Separate bool `yaml:"show-separator"`
ExtraInfoExchange bool `yaml:"show-tags"`
ExtraInfoFundamentals bool `yaml:"show-fundamentals"`
ShowSentiment bool `yaml:"show-sentiment"`
ShowSummary bool `yaml:"show-summary"`
ShowHoldings bool `yaml:"show-holdings"` // Deprecated: use ShowPositions instead, kept for backwards compatibility
ShowPositions bool `yaml:"show-positions"` // Preferred field name
Sort string `yaml:"sort"`
SentimentAPIKey string `yaml:"sentiment-api-key"`
Currency string `yaml:"currency"`
CurrencyConvertSummaryOnly bool `yaml:"currency-summary-only"`
CurrencyDisableUnitCostConversion bool `yaml:"currency-disable-unit-cost-conversion"`
Expand Down Expand Up @@ -81,6 +83,7 @@ type Dependencies struct {
MonitorYahooSessionRootURL string
MonitorYahooSessionCrumbURL string
MonitorYahooSessionConsentURL string
SentimentAdanosBaseURL string
}

type Monitor interface {
Expand Down Expand Up @@ -217,6 +220,15 @@ type Asset struct {
QuoteSource QuoteSource
Exchange Exchange
Meta Meta
Sentiment MarketSentiment
}

type MarketSentiment struct {
Available bool
AverageBuzz float64
BullishPercent float64
Coverage int
SourceAlignment string
}

type AssetClass int
Expand Down
Loading