diff --git a/catalyst/__init__.py b/catalyst/__init__.py index 508137df..f16c8762 100644 --- a/catalyst/__init__.py +++ b/catalyst/__init__.py @@ -79,6 +79,7 @@ __all__ = [ 'gens', 'run_algorithm', 'utils', + 'exchange', ] from ._version import get_versions diff --git a/catalyst/__main__.py b/catalyst/__main__.py index 7dfe91b1..154db69d 100644 --- a/catalyst/__main__.py +++ b/catalyst/__main__.py @@ -28,9 +28,9 @@ except NameError: '--strict-extensions/--non-strict-extensions', is_flag=True, help='If --strict-extensions is passed then catalyst will not run if it' - ' cannot load all of the specified extensions. If this is not passed or' - ' --non-strict-extensions is passed then the failure will be logged but' - ' execution will continue.', + ' cannot load all of the specified extensions. If this is not passed or' + ' --non-strict-extensions is passed then the failure will be logged but' + ' execution will continue.', ) @click.option( '--default-extension/--no-default-extension', @@ -64,6 +64,7 @@ def extract_option_object(option): option_object : click.Option The option object that this decorator will create. """ + @option def opt(): pass @@ -95,7 +96,9 @@ def ipython_only(option): def _(*args, **kwargs): kwargs[argname] = None return f(*args, **kwargs) + return _ + return d @@ -117,9 +120,9 @@ def ipython_only(option): '--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.", + " 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( '--data-frequency', @@ -149,7 +152,7 @@ def ipython_only(option): default=pd.Timestamp.utcnow(), show_default=False, help='The date to lookup data on or before.\n' - '[default: ]' + '[default: ]' ) @click.option( '-s', @@ -170,7 +173,7 @@ def ipython_only(option): metavar='FILENAME', show_default=True, help="The location to write the perf data. If this is '-' the perf will" - " be written to stdout.", + " be written to stdout.", ) @click.option( '--print-algo/--no-print-algo', @@ -178,12 +181,35 @@ def ipython_only(option): default=False, help='Print the algorithm to stdout.', ) +@click.option( + '-s', + '--start', + type=Date(tz='utc', as_timestamp=True), + help='The start date of the simulation.', +) @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( + '--live/--no-live', + is_flag=True, + default=False, + help='Enable live trading.', +) +@click.option( + '-x', + '--exchange-name', + type=click.Choice({'bitfinex'}), + help='The name of the exchange (supported: bitfinex).', +) +@click.option( + '-n', + '--algo-name', + help='A label assigned to the algorithm for tracking purposes.', +) @click.pass_context def run(ctx, algofile, @@ -197,21 +223,33 @@ def run(ctx, end, output, print_algo, - local_namespace): + local_namespace, + live, + exchange_name, + algo_namespace): """Run a backtest for the given algorithm. """ - # 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 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'") + 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( @@ -238,6 +276,9 @@ def run(ctx, print_algo=print_algo, local_namespace=local_namespace, environ=os.environ, + live=live, + exchange=exchange_name, + algo_namespace=algo_namespace ) if output == '-': @@ -265,11 +306,11 @@ def catalyst_magic(line, cell=None): '--algotext', cell, '--output', os.devnull, # don't write the results by default ] + ([ - # these options are set when running in line magic mode - # set a non None algo text to use the ipython user_ns - '--algotext', '', - '--local-namespace', - ] if cell is None else []) + line.split(), + # these options are set when running in line magic mode + # set a non None algo text to use the ipython user_ns + '--algotext', '', + '--local-namespace', + ] if cell is None else []) + line.split(), '%s%%catalyst' % ((cell or '') and '%'), # don't use system exit and propogate errors to the caller standalone_mode=False, @@ -335,14 +376,14 @@ def ingest(bundle, compile_locally, assets_version, show_progress): '--before', type=Timestamp(), help='Clear all data before TIMESTAMP.' - ' This may not be passed with -k / --keep-last', + ' This may not be passed with -k / --keep-last', ) @click.option( '-a', '--after', type=Timestamp(), help='Clear all data after TIMESTAMP' - ' This may not be passed with -k / --keep-last', + ' This may not be passed with -k / --keep-last', ) @click.option( '-k', @@ -350,7 +391,7 @@ def ingest(bundle, compile_locally, assets_version, show_progress): type=int, metavar='N', help='Clear all but the last N downloads.' - ' This may not be passed with -e / --before or -a / --after', + ' This may not be passed with -e / --before or -a / --after', ) def clean(bundle, before, after, keep_last): """Clean up data downloaded with the ingest command. diff --git a/catalyst/utils/run_algo.py b/catalyst/utils/run_algo.py index ac251e5c..7edc7dcb 100644 --- a/catalyst/utils/run_algo.py +++ b/catalyst/utils/run_algo.py @@ -527,6 +527,7 @@ def run_algorithm(initialize, print_algo=False, local_namespace=False, environ=environ, + live=live, exchange=exchange, algo_namespace=algo_namespace )