Merge branch 'flask_rest' of https://github.com/creslinux/freqtrade into feature/flask-rest
This commit is contained in:
commit
089f633c06
@ -6,7 +6,7 @@ import json
|
||||
import logging
|
||||
from argparse import Namespace
|
||||
from typing import Optional, Dict, Any
|
||||
from jsonschema import Draft4Validator, validate
|
||||
from jsonschema import Draft4Validator, validate, draft4_format_checker
|
||||
from jsonschema.exceptions import ValidationError, best_match
|
||||
import ccxt
|
||||
|
||||
@ -202,7 +202,7 @@ class Configuration(object):
|
||||
:return: Returns the config if valid, otherwise throw an exception
|
||||
"""
|
||||
try:
|
||||
validate(conf, constants.CONF_SCHEMA)
|
||||
validate(conf, constants.CONF_SCHEMA, format_checker=draft4_format_checker)
|
||||
return conf
|
||||
except ValidationError as exception:
|
||||
logger.critical(
|
||||
@ -210,7 +210,9 @@ class Configuration(object):
|
||||
exception
|
||||
)
|
||||
raise ValidationError(
|
||||
best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message
|
||||
best_match(Draft4Validator(constants.CONF_SCHEMA,
|
||||
format_checker=draft4_format_checker)
|
||||
.iter_errors(conf)).message
|
||||
)
|
||||
|
||||
def get_config(self) -> Dict[str, Any]:
|
||||
|
@ -85,6 +85,19 @@ CONF_SCHEMA = {
|
||||
},
|
||||
'required': ['enabled', 'token', 'chat_id']
|
||||
},
|
||||
'api_server': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'enabled': {'type': 'boolean'},
|
||||
'listen_ip_address': { "format": "ipv4"},
|
||||
'listen_port': {
|
||||
'type': 'integer',
|
||||
"minimum": 1024,
|
||||
"maximum": 65535
|
||||
},
|
||||
},
|
||||
'required': ['enabled', 'listen_ip_address', 'listen_port']
|
||||
},
|
||||
'db_url': {'type': 'string'},
|
||||
'initial_state': {'type': 'string', 'enum': ['running', 'stopped']},
|
||||
'internals': {
|
||||
|
118
freqtrade/rpc/api_server.py
Normal file
118
freqtrade/rpc/api_server.py
Normal file
@ -0,0 +1,118 @@
|
||||
import threading
|
||||
import logging
|
||||
# import json
|
||||
|
||||
from flask import Flask, request
|
||||
# from flask_restful import Resource, Api
|
||||
from json import dumps
|
||||
from freqtrade.rpc.rpc import RPC, RPCException
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
app = Flask(__name__)
|
||||
|
||||
class ApiServerSuperWrap(RPC):
|
||||
"""
|
||||
This class is for REST calls across api server
|
||||
"""
|
||||
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.interval = 1
|
||||
|
||||
thread = threading.Thread(target=self.run, args=(freqtrade,)) # extra comma as ref ! Tuple
|
||||
thread.daemon = True # Daemonize thread
|
||||
thread.start() # Start the execution
|
||||
|
||||
|
||||
def run(self, freqtrade):
|
||||
""" Method that runs forever """
|
||||
self._config = freqtrade.config
|
||||
|
||||
"""
|
||||
Define the application methods here, called by app.add_url_rule
|
||||
each Telegram command should have a like local substitute
|
||||
"""
|
||||
# @app.route("/")
|
||||
def hello():
|
||||
# For simple rest server testing via browser
|
||||
# cmds = 'Try uri:/daily?timescale=7 /profit /balance /status
|
||||
# /status /table /performance /count,
|
||||
# /start /stop /help'
|
||||
|
||||
rest_cmds ='Commands implemented: <br>' \
|
||||
'<a href=/daily?timescale=7>/daily?timescale=7</a>' \
|
||||
'<br>' \
|
||||
'<a href=/stop>/stop</a>' \
|
||||
'<br>' \
|
||||
'<a href=/start>/start</a>'
|
||||
return rest_cmds
|
||||
|
||||
def daily():
|
||||
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']
|
||||
)
|
||||
|
||||
stats = dumps(stats, indent=4, sort_keys=True, default=str)
|
||||
return stats
|
||||
except RPCException as e:
|
||||
return e
|
||||
|
||||
def start():
|
||||
"""
|
||||
Handler for /start.
|
||||
Starts TradeThread
|
||||
"""
|
||||
msg = self._rpc_start()
|
||||
return msg
|
||||
|
||||
def stop():
|
||||
"""
|
||||
Handler for /stop.
|
||||
Stops TradeThread
|
||||
"""
|
||||
msg = self._rpc_stop()
|
||||
return msg
|
||||
|
||||
## defines the url rules available on the api server
|
||||
'''
|
||||
First two arguments passed are /URL and 'Label'
|
||||
Label can be used as a shortcut when refactoring
|
||||
'''
|
||||
app.add_url_rule('/', 'hello', view_func=hello, methods=['GET'])
|
||||
app.add_url_rule('/stop', 'stop', view_func=stop, methods=['GET'])
|
||||
app.add_url_rule('/start', 'start', view_func=start, methods=['GET'])
|
||||
app.add_url_rule('/daily', 'daily', view_func=daily, methods=['GET'])
|
||||
|
||||
|
||||
"""
|
||||
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']
|
||||
|
||||
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:
|
||||
logger.exception("Api server failed to start, exception message is:")
|
||||
|
||||
|
53
freqtrade/rpc/rest_client.py
Executable file
53
freqtrade/rpc/rest_client.py
Executable file
@ -0,0 +1,53 @@
|
||||
#!/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)
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""
|
||||
This module contains class to manage RPC communications (Telegram, Slack, ...)
|
||||
This module contains class to manage RPC communications (Telegram, Slack, Rest ....)
|
||||
"""
|
||||
import logging
|
||||
from typing import List
|
||||
@ -23,6 +23,13 @@ class RPCManager(object):
|
||||
from freqtrade.rpc.telegram import Telegram
|
||||
self.registered_modules.append(Telegram(freqtrade))
|
||||
|
||||
# Enable local rest api server for cmd line control
|
||||
if freqtrade.config['api_server'].get('enabled', False):
|
||||
logger.info('Enabling rpc.api_server')
|
||||
from freqtrade.rpc.api_server import ApiServerSuperWrap
|
||||
self.registered_modules.append(ApiServerSuperWrap(freqtrade))
|
||||
|
||||
|
||||
def cleanup(self) -> None:
|
||||
""" Stops all enabled rpc modules """
|
||||
logger.info('Cleaning up rpc modules ...')
|
||||
|
@ -23,3 +23,8 @@ coinmarketcap==5.0.3
|
||||
|
||||
# Required for plotting data
|
||||
#plotly==2.3.0
|
||||
|
||||
#Added for local rest client
|
||||
Flask==1.0.2
|
||||
flask-jsonpify==1.5.0
|
||||
flask-restful==0.3.6
|
||||
|
Loading…
Reference in New Issue
Block a user