Stop and start calls added
Along with refactoring to base line and use decorators.
Also modules loaded optionally if enabled in config or not
binds to ip / port set from config.json with warning if not localhost

TODO:
 - use argparse in client, and generally clean client up
 - create unit test
 - documentation
 - extend to other RCP commands, after feedback
This commit is contained in:
creslinux 2018-06-14 15:38:26 +00:00
parent 6bb1ad288e
commit fb60f684f7
3 changed files with 131 additions and 86 deletions

View File

@ -5,43 +5,11 @@ import json
from flask import Flask, request
from flask_restful import Resource, Api
from json import dumps
from freqtrade.rpc.rpc import RPC
from freqtrade.rpc.rpc import RPC, RPCException
logger = logging.getLogger(__name__)
class Daily(Resource):
# called by http://127.0.0.1:/daily?timescale=7
# where 7 is the number of days to report back with
def __init__(self, freqtrade) -> None:
"""
Initializes all enabled rpc modules
:param freqtrade: Instance of a freqtrade bot
:return: None
"""
self.freqtrade = freqtrade
self._config = freqtrade.config
def get(self):
timescale = request.args.get('timescale')
logger.info("LocalRPC - Daily Command Called")
timescale = int(timescale)
(error, stats) = RPC.rpc_daily_profit(self, timescale,
self._config['stake_currency'],
self._config['fiat_display_currency']
)
if error == False:
stats = dumps(stats, indent=4, sort_keys=True, default=str)
return stats
else:
json.dumps(error)
return error
class LocalRestSuperWrap(RPC):
"""
This class is for REST cmd line client
@ -63,21 +31,83 @@ class LocalRestSuperWrap(RPC):
thread.daemon = True # Daemonize thread
thread.start() # Start the execution
def run(self, freqtrade):
""" Method that runs forever """
self._config = freqtrade.config
# TODO add IP address / port to bind to in config.json and use in below.
logger.info('Starting Local Rest Server')
my_freqtrade = freqtrade
app = Flask(__name__)
api = Api(app)
# Our resources for restful apps go here, pass freqtrade object across
api.add_resource(Daily, '/daily', methods=['GET'],
resource_class_kwargs={'freqtrade': my_freqtrade}) # Route for returning daily
"""
Define the application routes here
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'
#run the server
app.run(port='5002')
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
@app.route('/daily', methods=['GET'])
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
@app.route('/start', methods=['GET'])
def start():
"""
Handler for /start.
Starts TradeThread
"""
msg = self._rpc_start()
print("msg is", msg)
return msg
@app.route('/stop', methods=['GET'])
def stop():
"""
Handler for /stop.
Stops TradeThread
"""
msg = self._rpc_stop()
print("msg is", msg)
return msg
"""
Section to handle configuration and running of the Rest serve
also to check and warn if not bound to 127.0.0.1 as a security risk
"""
rest_ip = self._config['rest_cmd_line']['listen_ip_address']
rest_port = self._config['rest_cmd_line']['listen_port']
if rest_ip != "127.0.0.1":
i=0
while i < 10:
logger.info("SECURITY WARNING - Local Rest Server listening to external connections")
logger.info("SECURITY WARNING - This is insecure please set to 127.0.0.1 in config.json")
i += 1
# Run the Server
logger.info('Starting Local Rest Server')
app.run(host=rest_ip, port=rest_port)

View File

@ -4,12 +4,20 @@ 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":
@ -20,4 +28,26 @@ if len(argv) == 3 and argv[1] == "daily":
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)

View File

@ -1,13 +1,10 @@
"""
This module contains class to manage RPC communications (Telegram, Slack, ....)
This module contains class to manage RPC communications (Telegram, Slack, Rest ...)
"""
from typing import Any, List
import logging
import time
from freqtrade.rpc.telegram import Telegram
from freqtrade.rpc.local_rest_server import LocalRestSuperWrap
from typing import List
from freqtrade.rpc.rpc import RPC
logger = logging.getLogger(__name__)
@ -17,42 +14,29 @@ class RPCManager(object):
Class to manage RPC objects (Telegram, Slack, ...)
"""
def __init__(self, freqtrade) -> None:
"""
Initializes all enabled rpc modules
:param config: config to use
:return: None
"""
self.freqtrade = freqtrade
""" Initializes all enabled rpc modules """
self.registered_modules: List[RPC] = []
self.registered_modules: List[str] = []
self.telegram: Any = None
self._init()
def _init(self) -> None:
"""
Init RPC modules
:return:
"""
if self.freqtrade.config['telegram'].get('enabled', False):
# Enable telegram
if freqtrade.config['telegram'].get('enabled', False):
logger.info('Enabling rpc.telegram ...')
self.registered_modules.append('telegram')
self.telegram = Telegram(self.freqtrade)
from freqtrade.rpc.telegram import Telegram
self.registered_modules.append(Telegram(freqtrade))
# Added another RPC client - for cmdline local client.
# Uses existing superclass RPC build for Telegram
if self.freqtrade.config['rest_cmd'].get('enabled', False):
self.localRPC = LocalRestSuperWrap(self.freqtrade)
time.sleep(1)
# Enable local rest server for cmd line control
if freqtrade.config['rest_cmd_line'].get('enabled', False):
logger.info('Enabling rpc.local_rest_server ...')
from freqtrade.rpc.local_rest_server import LocalRestSuperWrap
self.registered_modules.append(LocalRestSuperWrap(freqtrade))
def cleanup(self) -> None:
"""
Stops all enabled rpc modules
:return: None
"""
if 'telegram' in self.registered_modules:
logger.info('Cleaning up rpc.telegram ...')
self.registered_modules.remove('telegram')
self.telegram.cleanup()
""" Stops all enabled rpc modules """
logger.info('Cleaning up rpc modules ...')
while self.registered_modules:
mod = self.registered_modules.pop()
logger.debug('Cleaning up rpc.%s ...', mod.name)
mod.cleanup()
del mod
def send_msg(self, msg: str) -> None:
"""
@ -60,6 +44,7 @@ class RPCManager(object):
:param msg: message
:return: None
"""
logger.info(msg)
if 'telegram' in self.registered_modules:
self.telegram.send_msg(msg)
logger.info('Sending rpc message: %s', msg)
for mod in self.registered_modules:
logger.debug('Forwarding message to rpc.%s', mod.name)
mod.send_msg(msg)