mop

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 5, 2021 License: MIT Imports: 15 Imported by: 0

README

Mop: track stocks the hacker way

Mop is a command-line utility that displays continuous up-to-date information about the U.S. markets and individual stocks.

image

Installing Mop

Ensure GO language is installed. Download from: https://go.dev/dl/

go install github.com/mop-tracker/mop/cmd/mop@latest

Alternatively, make sure $GOPATH is set then:

git clone https://github.com/mop-tracker/mop
cd mop
go build ./cmd/mop
./mop

Using Mop

For demonstration purposes Mop comes preconfigured with a number of stock tickers. You can easily change the default list by using the following keyboard commands:

+       Add stocks to the list.
-       Remove stocks from the list.
o       Change column sort order.
g       Group stocks by advancing/declining issues.
f       Set a filtering expression.
F       Unset a filtering expression.
?       Display help screen.
esc     Quit mop.

When prompted please enter comma-delimited list of stock tickers. The list and other settings are stored in the profile file (default: .moprc in your $HOME directory)

Expression-based Filtering

Mop has an in realtime expression-based filtering engine that is very easy to use.

At the main screen, press f and a prompt will appear. Write an expression that uses the stock properties.

Example:

last <= 5

This expression will make Mop show only the stocks whose last values are less than $5.

The available properties are: last, change, changePercent, open, low, high, low52, high52, volume, avgVolume, pe, peX, dividend, yield, mktCap, mktCapX and advancing.

The expression must return a boolean value, otherwise it will fail.

For detailed information about the syntax, please refer to Knetic/govaluate#what-operators-and-types-does-this-support.

To clear the filter, press Shift+F.

You can specify the profile you want to use by passing -profile <filename> to the command-line.

Contributing

  • Pull requests accepted.
  • Commit without changing program version or commit history.

License

Copyright (c) 2013-2019 by Michael Dvorkin and contributors. All Rights Reserved. "mike" + "@dvorkin" + ".net" || "twitter.com/mid"

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Column

type Column struct {
	// contains filtered or unexported fields
}

Column describes formatting rules for individual column within the list of stock quotes.

type ColumnEditor

type ColumnEditor struct {
	// contains filtered or unexported fields
}

ColumnEditor handles column sort order. When activated it highlights current column name in the header, then waits for arrow keys (choose another column), Enter (reverse sort order), or Esc (exit).

func NewColumnEditor added in v0.2.0

func NewColumnEditor(screen *Screen, quotes *Quotes) *ColumnEditor

Returns new initialized ColumnEditor struct. As part of initialization it highlights current column name (as stored in Profile).

func (*ColumnEditor) Handle

func (editor *ColumnEditor) Handle(event termbox.Event) bool

Handle takes over the keyboard events and dispatches them to appropriate column editor handlers. It returns true when user presses Esc.

type Filter added in v1.0.0

type Filter struct {
	// contains filtered or unexported fields
}

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.

func NewFilter added in v1.0.0

func NewFilter(profile *Profile) *Filter

Returns new Filter struct.

func (*Filter) Apply added in v1.0.0

func (filter *Filter) Apply(stocks []Stock) []Stock

Apply builds a list of sort interface based on current sort order, then calls sort.Sort to do the actual job.

type Layout

type Layout struct {
	// contains filtered or unexported fields
}

Layout is used to format and display all the collected data, i.e. market updates and the list of stock quotes.

func NewLayout added in v0.2.0

func NewLayout() *Layout

Creates the layout and assigns the default values that stay unchanged.

func (*Layout) Header

func (layout *Layout) Header(profile *Profile) string

Header iterates over column titles and formats the header line. The formatting includes placing an arrow next to the sorted column title. When the column editor is active it knows how to highlight currently selected column title.

func (*Layout) Market

func (layout *Layout) Market(market *Market) string

Market merges given market data structure with the market template and returns formatted string that includes highlighting markup.

func (*Layout) Quotes

func (layout *Layout) Quotes(quotes *Quotes) string

Quotes uses quotes template to format timestamp, stock quotes header, and the list of given stock quotes. It returns formatted string with all the necessary markup.

func (*Layout) TotalColumns

func (layout *Layout) TotalColumns() int

TotalColumns is the utility method for the column editor that returns total number of columns.

type LineEditor

type LineEditor struct {
	// contains filtered or unexported fields
}

LineEditor kicks in when user presses '+' or '-' to add or delete stock tickers. The data structure and methods are used to collect the input data and keep track of cursor movements (left, right, beginning of the line, end of the line, and backspace).

func NewLineEditor added in v0.2.0

func NewLineEditor(screen *Screen, quotes *Quotes) *LineEditor

Returns new initialized LineEditor struct.

func (*LineEditor) Handle

func (editor *LineEditor) Handle(ev termbox.Event) bool

Handle takes over the keyboard events and dispatches them to appropriate line editor handlers. As user types or edits the text cursor movements are tracked in `editor.cursor` while the text itself is stored in `editor.input`. The method returns true when user presses Esc (discard) or Enter (process).

func (*LineEditor) Prompt

func (editor *LineEditor) Prompt(command rune) *LineEditor

Prompt displays a prompt in response to '+' or '-' commands. Unknown commands are simply ignored. The prompt is displayed on the 3rd line (between the market data and the stock quotes).

type Market

type Market struct {
	IsClosed  bool              // True when U.S. markets are closed.
	Dow       map[string]string // Hash of Dow Jones indicators.
	Nasdaq    map[string]string // Hash of NASDAQ indicators.
	Sp500     map[string]string // Hash of S&P 500 indicators.
	Tokyo     map[string]string
	HongKong  map[string]string
	London    map[string]string
	Frankfurt map[string]string
	Yield     map[string]string
	Oil       map[string]string
	Yen       map[string]string
	Euro      map[string]string
	Gold      map[string]string
	// contains filtered or unexported fields
}

Market stores current market information displayed in the top three lines of the screen. The market data is fetched and parsed from the HTML page above.

func NewMarket added in v0.2.0

func NewMarket() *Market

Returns new initialized Market struct.

func (*Market) Fetch

func (market *Market) Fetch() (self *Market)

Fetch downloads HTML page from the 'marketURL', parses it, and stores resulting data in internal hashes. If download or data parsing fails Fetch populates 'market.errors'.

func (*Market) Ok

func (market *Market) Ok() (bool, string)

Ok returns two values: 1) boolean indicating whether the error has occured, and 2) the error text itself.

type Markup

type Markup struct {
	Foreground   termbox.Attribute // Foreground color.
	Background   termbox.Attribute // Background color (so far always termbox.ColorDefault).
	RightAligned bool              // True when the string is right aligned.
	// contains filtered or unexported fields
}

Markup implements some minimalistic text formatting conventions that get translated to Termbox colors and attributes. To colorize a string wrap it in <color-name>...</> tags. Unlike HTML each tag sets a new color whereas the </> tag changes color back to default. For example:

<green>Hello, <red>world!</>

The color tags could be combined with the attributes: <b>...</b> for bold, <u>...</u> for underline, and <r>...</r> for reverse. Unlike colors the attributes require matching closing tag.

The <right>...</right> tag is used to right align the enclosed string (ex. when displaying current time in the upper right corner).

func NewMarkup added in v0.2.0

func NewMarkup() *Markup

Creates markup to define tag to Termbox translation rules and store default colors and column alignments.

func (*Markup) IsTag

func (markup *Markup) IsTag(str string) bool

IsTag returns true when the given string looks like markup tag. When the tag name matches one of the markup-supported tags it gets translated to relevant Termbox attributes and colors.

func (*Markup) Tokenize

func (markup *Markup) Tokenize(str string) []string

Tokenize works just like strings.Split() except the resulting array includes the delimiters. For example, the "<green>Hello, <red>world!</>" string when tokenized by tags produces the following:

[0] "<green>"
[1] "Hello, "
[2] "<red>"
[3] "world!"
[4] "</>"

type Profile

type Profile struct {
	Tickers       []string // List of stock tickers to display.
	MarketRefresh int      // Time interval to refresh market data.
	QuotesRefresh int      // Time interval to refresh stock quotes.
	SortColumn    int      // Column number by which we sort stock quotes.
	Ascending     bool     // True when sort order is ascending.
	Grouped       bool     // True when stocks are grouped by advancing/declining.
	Filter        string   // Filter in human form
	// contains filtered or unexported fields
}

Profile manages Mop program settings as defined by user (ex. list of stock tickers). The settings are serialized using JSON and saved in the ~/.moprc file.

func NewProfile added in v0.2.0

func NewProfile(filename string) *Profile

Creates the profile and attempts to load the settings from ~/.moprc file. If the file is not there it gets created with default values.

func (*Profile) AddTickers

func (profile *Profile) AddTickers(tickers []string) (added int, err error)

AddTickers updates the list of existing tikers to add the new ones making sure there are no duplicates.

func (*Profile) Regroup

func (profile *Profile) Regroup() error

Regroup flips the flag that controls whether the stock quotes are grouped by advancing/declining issues.

func (*Profile) RemoveTickers

func (profile *Profile) RemoveTickers(tickers []string) (removed int, err error)

RemoveTickers removes requested stock tickers from the list we track.

func (*Profile) Reorder

func (profile *Profile) Reorder() error

Reorder gets called by the column editor to either reverse sorting order for the current column, or to pick another sort column.

func (*Profile) Save

func (profile *Profile) Save() error

Save serializes settings using JSON and saves them in ~/.moprc file.

func (*Profile) SetFilter added in v1.0.0

func (profile *Profile) SetFilter(filter string)

SetFilter creates a govaluate.EvaluableExpression.

type Quotes

type Quotes struct {
	// contains filtered or unexported fields
}

Quotes stores relevant pointers as well as the array of stock quotes for the tickers we are tracking.

func NewQuotes added in v0.2.0

func NewQuotes(market *Market, profile *Profile) *Quotes

Sets the initial values and returns new Quotes struct.

func (*Quotes) AddTickers

func (quotes *Quotes) AddTickers(tickers []string) (added int, err error)

AddTickers saves the list of tickers and refreshes the stock data if new tickers have been added. The function gets called from the line editor when user adds new stock tickers.

func (*Quotes) Fetch

func (quotes *Quotes) Fetch() (self *Quotes)

Fetch the latest stock quotes and parse raw fetched data into array of []Stock structs.

func (*Quotes) Ok

func (quotes *Quotes) Ok() (bool, string)

Ok returns two values: 1) boolean indicating whether the error has occured, and 2) the error text itself.

func (*Quotes) RemoveTickers

func (quotes *Quotes) RemoveTickers(tickers []string) (removed int, err error)

RemoveTickers saves the list of tickers and refreshes the stock data if some tickers have been removed. The function gets called from the line editor when user removes existing stock tickers.

type Screen

type Screen struct {
	// contains filtered or unexported fields
}

Screen is thin wrapper aroung Termbox library to provide basic display capabilities as requied by Mop.

func NewScreen added in v0.2.0

func NewScreen() *Screen

Initializes Termbox, creates screen along with layout and markup, and calculates current screen dimensions. Once initialized the screen is ready for display.

func (*Screen) Clear

func (screen *Screen) Clear() *Screen

Clear makes the entire screen blank using default background color.

func (*Screen) ClearLine

func (screen *Screen) ClearLine(x int, y int) *Screen

ClearLine erases the contents of the line starting from (x,y) coordinate till the end of the line.

func (*Screen) Close

func (screen *Screen) Close() *Screen

Close gets called upon program termination to close the Termbox.

func (*Screen) Draw

func (screen *Screen) Draw(objects ...interface{}) *Screen

Draw accepts variable number of arguments and knows how to display the market data, stock quotes, current time, and an arbitrary string.

func (*Screen) DrawLine

func (screen *Screen) DrawLine(x int, y int, str string)

DrawLine takes the incoming string, tokenizes it to extract markup elements, and displays it all starting at (x,y) location.

func (*Screen) Pause added in v0.2.0

func (screen *Screen) Pause(pause bool) *Screen

Pause is a toggle function that either creates a timestamp of the pause request or resets it to nil.

func (*Screen) Resize

func (screen *Screen) Resize() *Screen

Resize gets called when the screen is being resized. It recalculates screen dimensions and requests to clear the screen on next update.

type Sorter

type Sorter struct {
	// contains filtered or unexported fields
}

Sorter 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.

func NewSorter added in v0.2.0

func NewSorter(profile *Profile) *Sorter

Returns new Sorter struct.

func (*Sorter) SortByCurrentColumn

func (sorter *Sorter) SortByCurrentColumn(stocks []Stock) *Sorter

SortByCurrentColumn builds a list of sort interface based on current sort order, then calls sort.Sort to do the actual job.

type Stock

type Stock struct {
	Ticker     string `json:"symbol"`                      // Stock ticker.
	LastTrade  string `json:"regularMarketPrice"`          // l1: last trade.
	Change     string `json:"regularMarketChange"`         // c6: change real time.
	ChangePct  string `json:"regularMarketChangePercent"`  // k2: percent change real time.
	Open       string `json:"regularMarketOpen"`           // o: market open price.
	Low        string `json:"regularMarketDayLow"`         // g: day's low.
	High       string `json:"regularMarketDayHigh"`        // h: day's high.
	Low52      string `json:"fiftyTwoWeekLow"`             // j: 52-weeks low.
	High52     string `json:"fiftyTwoWeekHigh"`            // k: 52-weeks high.
	Volume     string `json:"regularMarketVolume"`         // v: volume.
	AvgVolume  string `json:"averageDailyVolume10Day"`     // a2: average volume.
	PeRatio    string `json:"trailingPE"`                  // r2: P/E ration real time.
	PeRatioX   string `json:"trailingPE"`                  // r: P/E ration (fallback when real time is N/A).
	Dividend   string `json:"trailingAnnualDividendRate"`  // d: dividend.
	Yield      string `json:"trailingAnnualDividendYield"` // y: dividend yield.
	MarketCap  string `json:"marketCap"`                   // j3: market cap real time.
	MarketCapX string `json:"marketCap"`                   // j1: market cap (fallback when real time is N/A).
	Currency   string `json:"currency"`                    // String code for currency of stock.
	Advancing  bool   // True when change is >= $0.
	PreOpen    string `json:"preMarketChangePercent,omitempty"`
	AfterHours string `json:"postMarketChangePercent,omitempty"`
}

Stock stores quote information for the particular stock ticker. The data for all the fields except 'Advancing' is fetched using Yahoo market API.

Directories

Path Synopsis
cmd
mop

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL