diff --git a/catalyst/__main__.py b/catalyst/__main__.py index bdd9e537..c6322f37 100644 --- a/catalyst/__main__.py +++ b/catalyst/__main__.py @@ -38,6 +38,8 @@ except NameError: default=True, help="Don't load the default catalyst extension.py file in $ZIPLINE_HOME.", ) +@click.version_option() + def main(extension, strict_extensions, default_extension): """Top level catalyst entry point. """ @@ -187,12 +189,6 @@ def ipython_only(option): default=None, help='Should the algorithm methods be resolved in the local namespace.' )) -@click.option( - '--live/--no-live', - is_flag=True, - default=False, - help='Enable live trading.', -) @click.option( '-x', '--exchange-name', @@ -210,12 +206,6 @@ def ipython_only(option): help='The base currency used to calculate statistics ' '(e.g. usd, btc, eth).', ) -@click.option( - '--live-graph/--no-live-graph', - is_flag=True, - default=False, - help='Display live graph.', -) @click.pass_context def run(ctx, algofile, @@ -230,44 +220,34 @@ def run(ctx, output, print_algo, local_namespace, - live, exchange_name, algo_namespace, - base_currency, - live_graph): + base_currency): """Run a backtest for the given algorithm. """ - if live: - if exchange_name is None: - ctx.fail("must specify an exchange name '-x' in live execution " - "mode '--live'") - if algo_namespace is None: - ctx.fail("must specify an algorithm name '-n' in live execution " - "mode '--live'") - if base_currency is None: - ctx.fail("must specify a base currency '-c' in live " - "execution mode '--live'") - else: - # check that the start and end dates are passed correctly - if start is None and end is None: - # check both at the same time to avoid the case where a user - # does not pass either of these and then passes the first only - # to be told they need to pass the second argument also - ctx.fail( - "must specify dates with '-s' / '--start' and '-e' / '--end'", - ) - if start is None: - ctx.fail("must specify a start date with '-s' / '--start'") - if end is None: - ctx.fail("must specify an end date with '-e' / '--end'") - if (algotext is not None) == (algofile is not None): ctx.fail( "must specify exactly one of '-f' / '--algofile' or" " '-t' / '--algotext'", ) + # check that the start and end dates are passed correctly + if start is None and end is None: + # check both at the same time to avoid the case where a user + # does not pass either of these and then passes the first only + # to be told they need to pass the second argument also + ctx.fail( + "must specify dates with '-s' / '--start' and '-e' / '--end'", + ) + if start is None: + ctx.fail("must specify a start date with '-s' / '--start'") + if end is None: + ctx.fail("must specify an end date with '-e' / '--end'") + + if exchange_name is None: + ctx.fail("must specify an exchange name '-x'") + perf = _run( initialize=None, handle_data=None, @@ -287,11 +267,11 @@ def run(ctx, print_algo=print_algo, local_namespace=local_namespace, environ=os.environ, - live=live, + live=False, exchange=exchange_name, algo_namespace=algo_namespace, base_currency=base_currency, - live_graph=live_graph + live_graph=False ) if output == '-': @@ -334,6 +314,133 @@ def catalyst_magic(line, cell=None): if e.code: raise ValueError('main returned non-zero status code: %d' % e.code) +@main.command() +@click.option( + '-f', + '--algofile', + default=None, + type=click.File('r'), + help='The file that contains the algorithm to run.', +) +@click.option( + '-t', + '--algotext', + help='The algorithm script to run.', +) +@click.option( + '-D', + '--define', + multiple=True, + help="Define a name to be bound in the namespace before executing" + " the algotext. For example '-Dname=value'. The value may be any python" + " expression. These are evaluated in order so they may refer to previously" + " defined names.", +) +@click.option( + '-o', + '--output', + default='-', + metavar='FILENAME', + show_default=True, + help="The location to write the perf data. If this is '-' the perf will" + " be written to stdout.", +) +@click.option( + '--print-algo/--no-print-algo', + is_flag=True, + default=False, + help='Print the algorithm to stdout.', +) +@ipython_only(click.option( + '--local-namespace/--no-local-namespace', + is_flag=True, + default=None, + help='Should the algorithm methods be resolved in the local namespace.' +)) +@click.option( + '-x', + '--exchange-name', + type=click.Choice({'bitfinex', 'bittrex', 'poloniex'}), + help='The name of the targeted exchange (supported: bitfinex, bittrex, poloniex).', +) +@click.option( + '-n', + '--algo-namespace', + help='A label assigned to the algorithm for data storage purposes.' +) +@click.option( + '-c', + '--base-currency', + help='The base currency used to calculate statistics ' + '(e.g. usd, btc, eth).', +) +@click.option( + '--live-graph/--no-live-graph', + is_flag=True, + default=False, + help='Display live graph.', +) + +@click.pass_context +def live(ctx, + algofile, + algotext, + define, + output, + print_algo, + local_namespace, + exchange_name, + algo_namespace, + base_currency, + live_graph): + """Trade live with the given algorithm. + """ + if (algotext is not None) == (algofile is not None): + ctx.fail( + "must specify exactly one of '-f' / '--algofile' or" + " '-t' / '--algotext'", + ) + + if exchange_name is None: + ctx.fail("must specify an exchange name '-x'") + if algo_namespace is None: + ctx.fail("must specify an algorithm name '-n' in live execution mode") + if base_currency is None: + ctx.fail("must specify a base currency '-c' in live execution mode") + + perf = _run( + initialize=None, + handle_data=None, + before_trading_start=None, + analyze=None, + algofile=algofile, + algotext=algotext, + defines=define, + data_frequency=None, + capital_base=None, + data=None, + bundle=None, + bundle_timestamp=None, + start=None, + end=None, + output=output, + print_algo=print_algo, + local_namespace=local_namespace, + environ=os.environ, + live=True, + exchange=exchange_name, + algo_namespace=algo_namespace, + base_currency=base_currency, + live_graph=live_graph + ) + + if output == '-': + click.echo(str(perf)) + elif output != os.devnull: # make the catalyst magic not write any data + perf.to_pickle(output) + + return perf + @main.command() @click.option( @@ -416,7 +523,6 @@ def clean(bundle, before, after, keep_last): keep_last, ) - @main.command() def bundles(): """List all of the available data bundles. diff --git a/catalyst/exchange/exchange_utils.py b/catalyst/exchange/exchange_utils.py index ebb33023..2d3729ed 100644 --- a/catalyst/exchange/exchange_utils.py +++ b/catalyst/exchange/exchange_utils.py @@ -80,6 +80,9 @@ def get_algo_folder(algo_name, environ=None): def get_algo_object(algo_name, key, environ=None, rel_path=None): + if algo_name is None: + return None + folder = get_algo_folder(algo_name, environ) if rel_path is not None: diff --git a/catalyst/exchange/live_graph_clock.py b/catalyst/exchange/live_graph_clock.py index eab0d58c..06b1086c 100644 --- a/catalyst/exchange/live_graph_clock.py +++ b/catalyst/exchange/live_graph_clock.py @@ -54,6 +54,7 @@ class LiveGraphClock(object): def __init__(self, sessions, context, time_skew=pd.Timedelta('0s')): + global mdates, plt #TODO: Could be cleaner import matplotlib.dates as mdates from matplotlib import pyplot as plt from matplotlib import style