Get new files from old branch

This commit is contained in:
Matthias 2019-04-04 07:08:24 +02:00
parent e0310906c7
commit c3c745ca19
3 changed files with 324 additions and 0 deletions

203
freqtrade/rpc/api_server.py Normal file
View File

@ -0,0 +1,203 @@
import json
import threading
import logging
# import json
from typing import Dict
from flask import Flask, request
# from flask_restful import Resource, Api
from freqtrade.rpc.rpc import RPC, RPCException
from ipaddress import IPv4Address
logger = logging.getLogger(__name__)
app = Flask(__name__)
class ApiServer(RPC):
"""
This class runs api server and provides rpc.rpc functionality to it
This class starts a none blocking thread the api server runs within
"""
def __init__(self, freqtrade) -> None:
"""
Init the api server, and init the super class RPC
:param freqtrade: Instance of a freqtrade bot
:return: None
"""
super().__init__(freqtrade)
self._config = freqtrade.config
# Register application handling
self.register_rest_other()
self.register_rest_rpc_urls()
thread = threading.Thread(target=self.run, daemon=True)
thread.start()
def register_rest_other(self):
"""
Registers flask app URLs that are not calls to functionality in rpc.rpc.
:return:
"""
app.register_error_handler(404, self.page_not_found)
app.add_url_rule('/', 'hello', view_func=self.hello, methods=['GET'])
def register_rest_rpc_urls(self):
"""
Registers flask app URLs that are calls to functonality in rpc.rpc.
First two arguments passed are /URL and 'Label'
Label can be used as a shortcut when refactoring
:return:
"""
app.add_url_rule('/stop', 'stop', view_func=self.stop, methods=['GET'])
app.add_url_rule('/start', 'start', view_func=self.start, methods=['GET'])
app.add_url_rule('/daily', 'daily', view_func=self.daily, methods=['GET'])
app.add_url_rule('/profit', 'profit', view_func=self.profit, methods=['GET'])
app.add_url_rule('/status_table', 'status_table',
view_func=self.status_table, methods=['GET'])
def run(self):
""" Method that runs flask app in its own thread forever """
"""
Section to handle configuration and running of the Rest server
also to check and warn if not bound to a loopback, warn on security risk.
"""
rest_ip = self._config['api_server']['listen_ip_address']
rest_port = self._config['api_server']['listen_port']
logger.info('Starting HTTP Server at {}:{}'.format(rest_ip, rest_port))
if not IPv4Address(rest_ip).is_loopback:
logger.info("SECURITY WARNING - Local Rest Server listening to external connections")
logger.info("SECURITY WARNING - This is insecure please set to your loopback,"
"e.g 127.0.0.1 in config.json")
# Run the Server
logger.info('Starting Local Rest Server')
try:
app.run(host=rest_ip, port=rest_port)
except Exception:
logger.exception("Api server failed to start, exception message is:")
def cleanup(self) -> None:
pass
def send_msg(self, msg: Dict[str, str]) -> None:
pass
"""
Define the application methods here, called by app.add_url_rule
each Telegram command should have a like local substitute
"""
def page_not_found(self, error):
"""
Return "404 not found", 404.
"""
return json.dumps({
'status': 'error',
'reason': '''There's no API call for %s''' % request.base_url,
'code': 404
}), 404
def hello(self):
"""
None critical but helpful default index page.
That lists URLs added to the flask server.
This may be deprecated at any time.
:return: index.html
"""
rest_cmds = 'Commands implemented: <br>' \
'<a href=/daily?timescale=7>Show 7 days of stats</a>' \
'<br>' \
'<a href=/stop>Stop the Trade thread</a>' \
'<br>' \
'<a href=/start>Start the Traded thread</a>' \
'<br>' \
'<a href=/profit>Show profit summary</a>' \
'<br>' \
'<a href=/status_table>Show status table - Open trades</a>' \
'<br>' \
'<a href=/paypal> 404 page does not exist</a>' \
'<br>'
return rest_cmds
def daily(self):
"""
Returns the last X days trading stats summary.
:return: stats
"""
try:
timescale = request.args.get('timescale')
logger.info("LocalRPC - Daily Command Called")
timescale = int(timescale)
stats = self._rpc_daily_profit(timescale,
self._config['stake_currency'],
self._config['fiat_display_currency']
)
return json.dumps(stats, indent=4, sort_keys=True, default=str)
except RPCException as e:
logger.exception("API Error querying daily:", e)
return "Error querying daily"
def profit(self):
"""
Handler for /profit.
Returns a cumulative profit statistics
:return: stats
"""
try:
logger.info("LocalRPC - Profit Command Called")
stats = self._rpc_trade_statistics(self._config['stake_currency'],
self._config['fiat_display_currency']
)
return json.dumps(stats, indent=4, sort_keys=True, default=str)
except RPCException as e:
logger.exception("API Error calling profit", e)
return "Error querying closed trades - maybe there are none"
def status_table(self):
"""
Handler for /status table.
Returns the current TradeThread status in table format
:return: results
"""
try:
results = self._rpc_trade_status()
return json.dumps(results, indent=4, sort_keys=True, default=str)
except RPCException as e:
logger.exception("API Error calling status table", e)
return "Error querying open trades - maybe there are none."
def start(self):
"""
Handler for /start.
Starts TradeThread in bot if stopped.
"""
msg = self._rpc_start()
return json.dumps(msg)
def stop(self):
"""
Handler for /stop.
Stops TradeThread in bot if running
"""
msg = self._rpc_stop()
return json.dumps(msg)

View File

@ -0,0 +1,74 @@
import logging
import flask
from flask import request, jsonify
logger = logging.getLogger(__name__)
class MyApiApp(flask.Flask):
def __init__(self, import_name):
"""
Contains common rest routes and resource that do not need
to access to rpc.rpc functionality
"""
super(MyApiApp, self).__init__(import_name)
"""
Registers flask app URLs that are not calls to functionality in rpc.rpc.
:return:
"""
self.before_request(self.my_preprocessing)
self.register_error_handler(404, self.page_not_found)
self.add_url_rule('/', 'hello', view_func=self.hello, methods=['GET'])
self.add_url_rule('/stop_api', 'stop_api', view_func=self.stop_api, methods=['GET'])
def my_preprocessing(self):
# Do stuff to flask.request
pass
def page_not_found(self, error):
# Return "404 not found", 404.
return jsonify({'status': 'error',
'reason': '''There's no API call for %s''' % request.base_url,
'code': 404}), 404
def hello(self):
"""
None critical but helpful default index page.
That lists URLs added to the flask server.
This may be deprecated at any time.
:return: index.html
"""
rest_cmds = 'Commands implemented: <br>' \
'<a href=/daily?timescale=7>Show 7 days of stats</a>' \
'<br>' \
'<a href=/stop>Stop the Trade thread</a>' \
'<br>' \
'<a href=/start>Start the Traded thread</a>' \
'<br>' \
'<a href=/paypal> 404 page does not exist</a>' \
'<br>' \
'<br>' \
'<a href=/stop_api>Shut down the api server - be sure</a>'
return rest_cmds
def stop_api(self):
""" For calling shutdown_api_server over via api server HTTP"""
self.shutdown_api_server()
return 'Api Server shutting down... '
def shutdown_api_server(self):
"""
Stop the running flask application
Records the shutdown in logger.info
:return:
"""
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running the Flask Werkzeug Server')
if func is not None:
logger.info('Stopping the Local Rest Server')
func()
return

47
freqtrade/rpc/rest_client.py Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""
Simple command line client into RPC commands
Can be used as an alternate to Telegram
"""
import time
from requests import get
from sys import argv
# TODO - use argparse to clean this up
# TODO - use IP and Port from config.json not hardcode
if len(argv) == 1:
print('\nThis script accepts the following arguments')
print('- daily (int) - Where int is the number of days to report back. daily 3')
print('- start - this will start the trading thread')
print('- stop - this will start the trading thread')
print('- there will be more....\n')
if len(argv) == 3 and argv[1] == "daily":
if str.isnumeric(argv[2]):
get_url = 'http://localhost:5002/daily?timescale=' + argv[2]
d = get(get_url).json()
print(d)
else:
print("\nThe second argument to daily must be an integer, 1,2,3 etc")
if len(argv) == 2 and argv[1] == "start":
get_url = 'http://localhost:5002/start'
d = get(get_url).text
print(d)
if "already" not in d:
time.sleep(2)
d = get(get_url).text
print(d)
if len(argv) == 2 and argv[1] == "stop":
get_url = 'http://localhost:5002/stop'
d = get(get_url).text
print(d)
if "already" not in d:
time.sleep(2)
d = get(get_url).text
print(d)