mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-19 11:22:06 +08:00
DEV: able to run locally through api- added backtesting as well (WIP)
This commit is contained in:
+207
-3
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user