DEV: able to run locally through api- added backtesting as well (WIP)

This commit is contained in:
AvishaiW
2018-03-06 16:02:02 +02:00
parent 3335ef16a4
commit b172f6b7b0
2 changed files with 367 additions and 22 deletions
+207 -3
View File
@@ -506,6 +506,197 @@ def live(ctx,
return perf
@main.command(name='serve')
@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(
'--data-frequency',
type=click.Choice({'daily', 'minute'}),
default='daily',
show_default=True,
help='The data frequency of the simulation.',
)
@click.option(
'--capital-base',
type=float,
show_default=True,
help='The starting capital for the simulation.',
)
@click.option(
'-b',
'--bundle',
default='poloniex',
metavar='BUNDLE-NAME',
show_default=True,
help='The data bundle to use for the simulation.',
)
@click.option(
'--bundle-timestamp',
type=Timestamp(),
default=pd.Timestamp.utcnow(),
show_default=False,
help='The date to lookup data on or before.\n'
'[default: <current-time>]'
)
@click.option(
'-s',
'--start',
type=Date(tz='utc', as_timestamp=True),
help='The start date of the simulation.',
)
@click.option(
'-e',
'--end',
type=Date(tz='utc', as_timestamp=True),
help='The end date of the simulation.',
)
@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',
help='The name of the targeted exchange.',
)
@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.pass_context
def run(ctx,
algofile,
algotext,
define,
data_frequency,
capital_base,
bundle,
bundle_timestamp,
start,
end,
output,
print_algo,
local_namespace,
exchange_name,
algo_namespace,
base_currency):
"""Run a backtest for the given algorithm on the server.
"""
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'"
" in backtest mode",
)
if start is None:
ctx.fail("must specify a start date with '-s' / '--start'"
" in backtest mode")
if end is None:
ctx.fail("must specify an end date with '-e' / '--end'"
" in backtest mode")
if exchange_name is None:
ctx.fail("must specify an exchange name '-x'")
if base_currency is None:
ctx.fail("must specify a base currency with '-c' in backtest mode")
if capital_base is None:
ctx.fail("must specify a capital base with '--capital-base'")
click.echo('Running in backtesting mode.', sys.stdout)
perf = run_server(
initialize=None,
handle_data=None,
before_trading_start=None,
analyze=None,
algofile=algofile,
algotext=algotext,
defines=define,
data_frequency=data_frequency,
capital_base=capital_base,
data=None,
bundle=bundle,
bundle_timestamp=bundle_timestamp,
start=start,
end=end,
output=output,
print_algo=print_algo,
local_namespace=local_namespace,
environ=os.environ,
live=False,
exchange=exchange_name,
algo_namespace=algo_namespace,
base_currency=base_currency,
analyze_live=None,
live_graph=False,
simulate_orders=True,
auth_aliases=None,
stats_output=None,
)
if output == '-':
click.echo(str(perf), sys.stdout)
elif output != os.devnull: # make the catalyst magic not write any data
perf.to_pickle(output)
return perf
@main.command(name='serve-live')
@click.option(
'-f',
@@ -642,20 +833,33 @@ def serve_live(ctx,
click.echo('Running in live trading mode.', sys.stdout)
perf = run_server(
algofile=algofile,
initialize=None,
handle_data=None,
before_trading_start=None,
analyze=None,
algofile=algofile,
algotext=algotext,
define=define,
defines=define,
data_frequency=None,
capital_base=capital_base,
data=None,
bundle=None,
bundle_timestamp=None,
start=None,
end=end,
output=output,
print_algo=print_algo,
live=False,
local_namespace=local_namespace,
environ=os.environ,
live=True,
exchange=exchange_name,
algo_namespace=algo_namespace,
base_currency=base_currency,
live_graph=live_graph,
analyze_live=None,
simulate_orders=simulate_orders,
auth_aliases=auth_aliases,
stats_output=None,
)
if output == '-':
+160 -19
View File
@@ -1,40 +1,181 @@
import requests
#!flask/bin/python
import base64
import requests
import pandas as pd
import json
from flask import (
Flask, jsonify, abort,
make_response, request,
url_for,
)
from catalyst.utils.run_algo import _run
def convert_date(date):
if isinstance(date, pd.Timestamp):
return date.__str__()
def run_server(
initialize,
handle_data,
before_trading_start,
analyze,
algofile,
algotext,
define,
defines,
data_frequency,
capital_base,
data,
bundle,
bundle_timestamp,
start,
end,
output,
print_algo,
local_namespace,
environ,
live,
exchange,
algo_namespace,
base_currency,
live_graph,
analyze_live,
simulate_orders,
auth_aliases,
):
stats_output,
):
if algotext:
algotext = base64.b64encode(algotext)
else:
algotext = base64.b64encode(algofile.read())
algofile = None
json_file = {'arguments': {
'--algofile': base64.b64encode(algofile.read()),
'--algotext': algotext,
'--define': define,
'--capital-base': capital_base,
'--end': end,
'--output': output,
'print-algo': print_algo,
'live': True,
'--exchange': exchange,
'--algo-namespace': algo_namespace,
'--base-currency': base_currency,
'live-graph': live_graph,
'simulate-orders': simulate_orders,
'--auth-aliases': auth_aliases,
'initialize': initialize,
'handle_data': handle_data,
'before_trading_start': before_trading_start,
'analyze': analyze,
'algotext': algotext,
'defines': defines,
'data_frequency': data_frequency,
'capital_base': capital_base,
'data': data,
'bundle': bundle,
'bundle_timestamp': bundle_timestamp,
'start': start,
'end': end,
'local_namespace': local_namespace,
'environ': None,
'analyze_live': analyze_live,
'stats_output': stats_output,
'algofile': algofile,
'output': output,
'print_algo': print_algo,
'live': live,
'exchange': exchange,
'algo_namespace': algo_namespace,
'base_currency': base_currency,
'live_graph': live_graph,
'simulate_orders': simulate_orders,
'auth_aliases': auth_aliases,
}}
url = 'http://127.0.0.1:5000/todo/api/v1.0/tasks'
response = requests.post(url, json=json_file)
url = 'http://127.0.0.1:5000/api/v1.0/tasks'
response = requests.post(url, json=json.dumps(
json_file, default=convert_date))
app = Flask(__name__)
def exec_catalyst(arguments):
arguments['algotext'] = base64.b64decode(arguments['algotext'])
if arguments['start'] is not None:
arguments['start'] = pd.Timestamp(arguments['start'])
if arguments['end'] is not None:
arguments['end'] = pd.Timestamp(arguments['end'])
_run(**arguments)
def make_public_task(task):
new_task = {}
for field in task:
if field == 'id':
new_task['uri'] = url_for('get_task', task_id=task['id'], _external=True)
else:
new_task[field] = task[field]
return new_task
# @app.route('/')
# def index():
# return "Hello, World!"
# @app.route('/api/v1.0/tasks/<int:task_id>', methods=['GET'])
# def get_task(task_id):
# task = [task for task in tasks if task['id'] == task_id]
# if len(task) == 0:
# abort(404)
# return jsonify({'task': make_public_task(task[0])})
#
#
# @app.route('/api/v1.0/tasks', methods=['GET'])
# def get_tasks():
# return jsonify({'tasks': [make_public_task(task) for task in tasks]})
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 400)
@app.route('/api/v1.0/tasks', methods=['POST'])
def create_task():
if not request.json or 'arguments' not in request.json:
abort(400)
arguments = json.loads(request.json)['arguments']
exec_catalyst(arguments)
# task = {
# 'id': tasks[-1]['id'] + 1,
# 'arguments': request.json['arguments'],
# 'done': False
# }
# tasks.append(task)
#
# return jsonify({'task': [make_public_task(task)]}), 201
return jsonify({"success": "man"}), 201
# @app.route('/api/v1.0/tasks/<int:task_id>', methods=['PUT'])
# def update_task(task_id):
# task = [task for task in tasks if task['id'] == task_id]
# if len(task) == 0:
# abort(404)
# if not request.json:
# abort(400)
# if 'arguments' in request.json and type(request.json['arguments']) != unicode:
# abort(400)
# if 'description' in request.json and type(request.json['description']) is not unicode:
# abort(400)
# if 'done' in request.json and type(request.json['done']) is not bool:
# abort(400)
# task[0]['arguments'] = request.json.get('arguments', task[0]['arguments'])
# task[0]['done'] = request.json.get('done', task[0]['done'])
# return jsonify({'task': [make_public_task(task[0])]})
#
#
# @app.route('/api/v1.0/tasks/<int:task_id>', methods=['DELETE'])
# def delete_task(task_id):
# task = [task for task in tasks if task['id'] == task_id]
# if len(task) == 0:
# abort(404)
# tasks.remove(task[0])
# return jsonify({'result': True})
if __name__ == '__main__':
app.run(debug=True, threaded=True)