diff --git a/piker/brokers/cli.py b/piker/brokers/cli.py index ac707686..f5608f43 100644 --- a/piker/brokers/cli.py +++ b/piker/brokers/cli.py @@ -3,7 +3,6 @@ Console interface to broker client/daemons. """ import os from functools import partial -import json from operator import attrgetter from operator import itemgetter @@ -32,7 +31,7 @@ _watchlists_data_path = os.path.join(_config_dir, 'watchlists.json') @click.argument('kwargs', nargs=-1) @click.pass_obj def api(config, meth, kwargs, keys): - """client for testing broker API methods with pretty printing of output. + """Make a broker-client API method call """ # global opts broker = config['broker'] @@ -69,8 +68,7 @@ def api(config, meth, kwargs, keys): @click.argument('tickers', nargs=-1, required=True) @click.pass_obj def quote(config, tickers, df_output): - """Retreive symbol quotes on the console in either json or dataframe - format. + """Print symbol quotes to the console """ # global opts brokermod = config['brokermod'] @@ -106,8 +104,7 @@ def quote(config, tickers, df_output): @click.argument('symbol', required=True) @click.pass_obj def bars(config, symbol, count, df_output): - """Retreive 1m bars for symbol and print on the console in json - format. + """Retreive 1m bars for symbol and print on the console """ # global opts brokermod = config['brokermod'] @@ -142,7 +139,7 @@ def bars(config, symbol, count, df_output): @click.argument('name', nargs=1, required=True) @click.pass_obj def monitor(config, rate, name, dhost, test, tl): - """Spawn a real-time watchlist. + """Start a real-time watchlist UI """ # global opts brokermod = config['brokermod'] @@ -186,7 +183,7 @@ def monitor(config, rate, name, dhost, test, tl): @click.argument('name', nargs=1, required=True) @click.pass_obj def record(config, rate, name, dhost, filename): - """Record client side quotes to file + """Record client side quotes to a file on disk """ # global opts brokermod = config['brokermod'] @@ -215,96 +212,6 @@ def record(config, rate, name, dhost, filename): click.echo(f"Data feed recording saved to {filename}") -@cli.group() -@click.option('--config_dir', '-d', default=_watchlists_data_path, - help='Path to piker configuration directory') -@click.pass_context -def watchlists(ctx, config_dir): - """Watchlists commands and operations - """ - loglevel = ctx.parent.params['loglevel'] - get_console_log(loglevel) # activate console logging - - wl.make_config_dir(_config_dir) - ctx.ensure_object(dict) - ctx.obj = {'path': config_dir, - 'watchlist': wl.ensure_watchlists(config_dir)} - - -@watchlists.command(help='show watchlist') -@click.argument('name', nargs=1, required=False) -@click.pass_context -def show(ctx, name): - watchlist = wl.merge_watchlist(ctx.obj['watchlist'], wl._builtins) - click.echo(colorize_json( - watchlist if name is None else watchlist[name])) - - -@watchlists.command(help='load passed in watchlist') -@click.argument('data', nargs=1, required=True) -@click.pass_context -def load(ctx, data): - try: - wl.write_to_file(json.loads(data), ctx.obj['path']) - except (json.JSONDecodeError, IndexError): - click.echo('You have passed an invalid text respresentation of a ' - 'JSON object. Try again.') - - -@watchlists.command(help='add ticker to watchlist') -@click.argument('name', nargs=1, required=True) -@click.argument('ticker_names', nargs=-1, required=True) -@click.pass_context -def add(ctx, name, ticker_names): - for ticker in ticker_names: - watchlist = wl.add_ticker( - name, ticker, ctx.obj['watchlist']) - wl.write_to_file(watchlist, ctx.obj['path']) - - -@watchlists.command(help='remove ticker from watchlist') -@click.argument('name', nargs=1, required=True) -@click.argument('ticker_name', nargs=1, required=True) -@click.pass_context -def remove(ctx, name, ticker_name): - try: - watchlist = wl.remove_ticker(name, ticker_name, ctx.obj['watchlist']) - except KeyError: - log.error(f"No watchlist with name `{name}` could be found?") - except ValueError: - if name in wl._builtins and ticker_name in wl._builtins[name]: - log.error(f"Can not remove ticker `{ticker_name}` from built-in " - f"list `{name}`") - else: - log.error(f"Ticker `{ticker_name}` not found in list `{name}`") - else: - wl.write_to_file(watchlist, ctx.obj['path']) - - -@watchlists.command(help='delete watchlist group') -@click.argument('name', nargs=1, required=True) -@click.pass_context -def delete(ctx, name): - watchlist = wl.delete_group(name, ctx.obj['watchlist']) - wl.write_to_file(watchlist, ctx.obj['path']) - - -@watchlists.command(help='merge a watchlist from another user') -@click.argument('watchlist_to_merge', nargs=1, required=True) -@click.pass_context -def merge(ctx, watchlist_to_merge): - merged_watchlist = wl.merge_watchlist(json.loads(watchlist_to_merge), - ctx.obj['watchlist']) - wl.write_to_file(merged_watchlist, ctx.obj['path']) - - -@watchlists.command(help='dump text respresentation of a watchlist to console') -@click.argument('name', nargs=1, required=False) -@click.pass_context -def dump(ctx, name): - click.echo(json.dumps(ctx.obj['watchlist'])) - - # options utils @cli.command() @@ -315,7 +222,8 @@ def dump(ctx, name): @click.argument('symbol', required=True) @click.pass_context def contracts(ctx, loglevel, broker, symbol, ids): - + """Get list of all option contracts for symbol + """ brokermod = get_brokermod(broker) get_console_log(loglevel) @@ -338,8 +246,7 @@ def contracts(ctx, loglevel, broker, symbol, ids): @click.argument('symbol', required=True) @click.pass_obj def optsquote(config, symbol, df_output, date): - """Retreive symbol quotes on the console in either - json or dataframe format. + """Retreive symbol option quotes on the console """ # global opts brokermod = config['brokermod'] @@ -371,7 +278,7 @@ def optsquote(config, symbol, df_output, date): @click.argument('symbol', required=True) @click.pass_obj def optschain(config, symbol, date, tl, rate, test): - """Start the real-time option chain UI. + """Start an option chain UI """ # global opts loglevel = config['loglevel'] @@ -382,7 +289,7 @@ def optschain(config, symbol, date, tl, rate, test): async def main(tries): async with maybe_spawn_brokerd_as_subactor( tries=tries, loglevel=loglevel - ) as portal: + ): # run app "main" await _async_main( symbol, diff --git a/piker/cli/__init__.py b/piker/cli/__init__.py index fe0710ba..ba5be310 100644 --- a/piker/cli/__init__.py +++ b/piker/cli/__init__.py @@ -65,3 +65,4 @@ def cli(ctx, broker, loglevel, configdir): # load downstream cli modules from ..brokers import cli as _ +from ..watchlists import cli as _ diff --git a/piker/watchlists.py b/piker/watchlists/__init__.py similarity index 97% rename from piker/watchlists.py rename to piker/watchlists/__init__.py index 7fc532ba..707c602f 100644 --- a/piker/watchlists.py +++ b/piker/watchlists/__init__.py @@ -2,7 +2,7 @@ import os import json from collections import defaultdict -from .log import get_logger +from ..log import get_logger log = get_logger(__name__) diff --git a/piker/watchlists/cli.py b/piker/watchlists/cli.py new file mode 100644 index 00000000..7ed089d1 --- /dev/null +++ b/piker/watchlists/cli.py @@ -0,0 +1,107 @@ + +""" +Watchlist management commands. +""" +import os +import json + +import click + +from .. import watchlists as wl +from ..cli import cli +from ..log import get_console_log, colorize_json, get_logger + +log = get_logger('watchlist-cli') + +_config_dir = click.get_app_dir('piker') +_watchlists_data_path = os.path.join(_config_dir, 'watchlists.json') + + +@cli.group() +@click.option('--config_dir', '-d', default=_watchlists_data_path, + help='Path to piker configuration directory') +@click.pass_context +def watchlists(ctx, config_dir): + """Watchlists commands and operations + """ + loglevel = ctx.parent.params['loglevel'] + get_console_log(loglevel) # activate console logging + + wl.make_config_dir(_config_dir) + ctx.ensure_object(dict) + ctx.obj = {'path': config_dir, + 'watchlist': wl.ensure_watchlists(config_dir)} + + +@watchlists.command(help='show watchlist') +@click.argument('name', nargs=1, required=False) +@click.pass_context +def show(ctx, name): + watchlist = wl.merge_watchlist(ctx.obj['watchlist'], wl._builtins) + click.echo(colorize_json( + watchlist if name is None else watchlist[name])) + + +@watchlists.command(help='load passed in watchlist') +@click.argument('data', nargs=1, required=True) +@click.pass_context +def load(ctx, data): + try: + wl.write_to_file(json.loads(data), ctx.obj['path']) + except (json.JSONDecodeError, IndexError): + click.echo('You have passed an invalid text respresentation of a ' + 'JSON object. Try again.') + + +@watchlists.command(help='add ticker to watchlist') +@click.argument('name', nargs=1, required=True) +@click.argument('ticker_names', nargs=-1, required=True) +@click.pass_context +def add(ctx, name, ticker_names): + for ticker in ticker_names: + watchlist = wl.add_ticker( + name, ticker, ctx.obj['watchlist']) + wl.write_to_file(watchlist, ctx.obj['path']) + + +@watchlists.command(help='remove ticker from watchlist') +@click.argument('name', nargs=1, required=True) +@click.argument('ticker_name', nargs=1, required=True) +@click.pass_context +def remove(ctx, name, ticker_name): + try: + watchlist = wl.remove_ticker(name, ticker_name, ctx.obj['watchlist']) + except KeyError: + log.error(f"No watchlist with name `{name}` could be found?") + except ValueError: + if name in wl._builtins and ticker_name in wl._builtins[name]: + log.error(f"Can not remove ticker `{ticker_name}` from built-in " + f"list `{name}`") + else: + log.error(f"Ticker `{ticker_name}` not found in list `{name}`") + else: + wl.write_to_file(watchlist, ctx.obj['path']) + + +@watchlists.command(help='delete watchlist group') +@click.argument('name', nargs=1, required=True) +@click.pass_context +def delete(ctx, name): + watchlist = wl.delete_group(name, ctx.obj['watchlist']) + wl.write_to_file(watchlist, ctx.obj['path']) + + +@watchlists.command(help='merge a watchlist from another user') +@click.argument('watchlist_to_merge', nargs=1, required=True) +@click.pass_context +def merge(ctx, watchlist_to_merge): + merged_watchlist = wl.merge_watchlist(json.loads(watchlist_to_merge), + ctx.obj['watchlist']) + wl.write_to_file(merged_watchlist, ctx.obj['path']) + + +@watchlists.command(help='dump text respresentation of a watchlist to console') +@click.argument('name', nargs=1, required=False) +@click.pass_context +def dump(ctx, name): + click.echo(json.dumps(ctx.obj['watchlist']))