mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 02:12:01 +00:00
ruff format: update ft_client
This commit is contained in:
parent
4f5bf632fc
commit
15f32be176
|
@ -1,26 +1,37 @@
|
|||
from freqtrade_client.ft_rest_client import FtRestClient
|
||||
|
||||
|
||||
__version__ = '2024.5-dev'
|
||||
__version__ = "2024.5-dev"
|
||||
|
||||
if 'dev' in __version__:
|
||||
if "dev" in __version__:
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
freqtrade_basedir = Path(__file__).parent
|
||||
|
||||
__version__ = __version__ + '-' + subprocess.check_output(
|
||||
['git', 'log', '--format="%h"', '-n 1'],
|
||||
stderr=subprocess.DEVNULL, cwd=freqtrade_basedir).decode("utf-8").rstrip().strip('"')
|
||||
__version__ = (
|
||||
__version__
|
||||
+ "-"
|
||||
+ subprocess.check_output(
|
||||
["git", "log", '--format="%h"', "-n 1"],
|
||||
stderr=subprocess.DEVNULL,
|
||||
cwd=freqtrade_basedir,
|
||||
)
|
||||
.decode("utf-8")
|
||||
.rstrip()
|
||||
.strip('"')
|
||||
)
|
||||
|
||||
except Exception: # pragma: no cover
|
||||
# git not available, ignore
|
||||
try:
|
||||
# Try Fallback to freqtrade_commit file (created by CI while building docker image)
|
||||
versionfile = Path('./freqtrade_commit')
|
||||
versionfile = Path("./freqtrade_commit")
|
||||
if versionfile.is_file():
|
||||
__version__ = f"docker-{__version__}-{versionfile.read_text()[:8]}"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
__all__ = ['FtRestClient']
|
||||
__all__ = ["FtRestClient"]
|
||||
|
|
|
@ -15,7 +15,7 @@ from freqtrade_client.ft_rest_client import FtRestClient
|
|||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
)
|
||||
logger = logging.getLogger("ft_rest_client")
|
||||
|
||||
|
@ -25,30 +25,33 @@ def add_arguments(args: Any = None):
|
|||
prog="freqtrade-client",
|
||||
description="Client for the freqtrade REST API",
|
||||
)
|
||||
parser.add_argument("command",
|
||||
help="Positional argument defining the command to execute.",
|
||||
nargs="?"
|
||||
parser.add_argument(
|
||||
"command", help="Positional argument defining the command to execute.", nargs="?"
|
||||
)
|
||||
parser.add_argument('-V', '--version', action='version', version=f'%(prog)s {__version__}')
|
||||
parser.add_argument('--show',
|
||||
help='Show possible methods with this client',
|
||||
dest='show',
|
||||
action='store_true',
|
||||
default=False
|
||||
parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}")
|
||||
parser.add_argument(
|
||||
"--show",
|
||||
help="Show possible methods with this client",
|
||||
dest="show",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
|
||||
parser.add_argument('-c', '--config',
|
||||
help='Specify configuration file (default: %(default)s). ',
|
||||
dest='config',
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
help="Specify configuration file (default: %(default)s). ",
|
||||
dest="config",
|
||||
type=str,
|
||||
metavar='PATH',
|
||||
default='config.json'
|
||||
metavar="PATH",
|
||||
default="config.json",
|
||||
)
|
||||
|
||||
parser.add_argument("command_arguments",
|
||||
parser.add_argument(
|
||||
"command_arguments",
|
||||
help="Positional arguments for the parameters for [command]",
|
||||
nargs="*",
|
||||
default=[]
|
||||
default=[],
|
||||
)
|
||||
|
||||
pargs = parser.parse_args(args)
|
||||
|
@ -59,8 +62,9 @@ def load_config(configfile):
|
|||
file = Path(configfile)
|
||||
if file.is_file():
|
||||
with file.open("r") as f:
|
||||
config = rapidjson.load(f, parse_mode=rapidjson.PM_COMMENTS |
|
||||
rapidjson.PM_TRAILING_COMMAS)
|
||||
config = rapidjson.load(
|
||||
f, parse_mode=rapidjson.PM_COMMENTS | rapidjson.PM_TRAILING_COMMAS
|
||||
)
|
||||
return config
|
||||
else:
|
||||
logger.warning(f"Could not load config file {file}.")
|
||||
|
@ -72,27 +76,26 @@ def print_commands():
|
|||
client = FtRestClient(None)
|
||||
print("Possible commands:\n")
|
||||
for x, _ in inspect.getmembers(client):
|
||||
if not x.startswith('_'):
|
||||
doc = re.sub(':return:.*', '', getattr(client, x).__doc__, flags=re.MULTILINE).rstrip()
|
||||
if not x.startswith("_"):
|
||||
doc = re.sub(":return:.*", "", getattr(client, x).__doc__, flags=re.MULTILINE).rstrip()
|
||||
print(f"{x}\n\t{doc}\n")
|
||||
|
||||
|
||||
def main_exec(args: Dict[str, Any]):
|
||||
|
||||
if args.get("show"):
|
||||
print_commands()
|
||||
sys.exit()
|
||||
|
||||
config = load_config(args['config'])
|
||||
url = config.get('api_server', {}).get('listen_ip_address', '127.0.0.1')
|
||||
port = config.get('api_server', {}).get('listen_port', '8080')
|
||||
username = config.get('api_server', {}).get('username')
|
||||
password = config.get('api_server', {}).get('password')
|
||||
config = load_config(args["config"])
|
||||
url = config.get("api_server", {}).get("listen_ip_address", "127.0.0.1")
|
||||
port = config.get("api_server", {}).get("listen_port", "8080")
|
||||
username = config.get("api_server", {}).get("username")
|
||||
password = config.get("api_server", {}).get("password")
|
||||
|
||||
server_url = f"http://{url}:{port}"
|
||||
client = FtRestClient(server_url, username, password)
|
||||
|
||||
m = [x for x, y in inspect.getmembers(client) if not x.startswith('_')]
|
||||
m = [x for x, y in inspect.getmembers(client) if not x.startswith("_")]
|
||||
command = args["command"]
|
||||
if command not in m:
|
||||
logger.error(f"Command {command} not defined")
|
||||
|
|
|
@ -21,31 +21,26 @@ PostDataT = Optional[Union[Dict[str, Any], List[Dict[str, Any]]]]
|
|||
|
||||
|
||||
class FtRestClient:
|
||||
|
||||
def __init__(self, serverurl, username=None, password=None, *,
|
||||
pool_connections=10, pool_maxsize=10):
|
||||
|
||||
def __init__(
|
||||
self, serverurl, username=None, password=None, *, pool_connections=10, pool_maxsize=10
|
||||
):
|
||||
self._serverurl = serverurl
|
||||
self._session = requests.Session()
|
||||
|
||||
# allow configuration of pool
|
||||
adapter = requests.adapters.HTTPAdapter(
|
||||
pool_connections=pool_connections,
|
||||
pool_maxsize=pool_maxsize
|
||||
pool_connections=pool_connections, pool_maxsize=pool_maxsize
|
||||
)
|
||||
self._session.mount('http://', adapter)
|
||||
self._session.mount("http://", adapter)
|
||||
|
||||
self._session.auth = (username, password)
|
||||
|
||||
def _call(self, method, apipath, params: Optional[dict] = None, data=None, files=None):
|
||||
|
||||
if str(method).upper() not in ('GET', 'POST', 'PUT', 'DELETE'):
|
||||
raise ValueError(f'invalid method <{method}>')
|
||||
if str(method).upper() not in ("GET", "POST", "PUT", "DELETE"):
|
||||
raise ValueError(f"invalid method <{method}>")
|
||||
basepath = f"{self._serverurl}/api/v1/{apipath}"
|
||||
|
||||
hd = {"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
hd = {"Accept": "application/json", "Content-Type": "application/json"}
|
||||
|
||||
# Split url
|
||||
schema, netloc, path, par, query, fragment = urlparse(basepath)
|
||||
|
@ -151,7 +146,7 @@ class FtRestClient:
|
|||
"""
|
||||
return self._delete(f"locks/{lock_id}")
|
||||
|
||||
def lock_add(self, pair: str, until: str, side: str = '*', reason: str = ''):
|
||||
def lock_add(self, pair: str, until: str, side: str = "*", reason: str = ""):
|
||||
"""Lock pair
|
||||
|
||||
:param pair: Pair to lock
|
||||
|
@ -160,14 +155,7 @@ class FtRestClient:
|
|||
:param reason: Reason for the lock
|
||||
:return: json object
|
||||
"""
|
||||
data = [
|
||||
{
|
||||
"pair": pair,
|
||||
"until": until,
|
||||
"side": side,
|
||||
"reason": reason
|
||||
}
|
||||
]
|
||||
data = [{"pair": pair, "until": until, "side": side, "reason": reason}]
|
||||
return self._post("locks", data=data)
|
||||
|
||||
def daily(self, days=None):
|
||||
|
@ -234,7 +222,7 @@ class FtRestClient:
|
|||
return self._get("version")
|
||||
|
||||
def show_config(self):
|
||||
""" Returns part of the configuration, relevant for trading operations.
|
||||
"""Returns part of the configuration, relevant for trading operations.
|
||||
:return: json object containing the version
|
||||
"""
|
||||
return self._get("show_config")
|
||||
|
@ -244,7 +232,7 @@ class FtRestClient:
|
|||
configstatus = self.show_config()
|
||||
if not configstatus:
|
||||
return {"status": "not_running"}
|
||||
elif configstatus['state'] == "running":
|
||||
elif configstatus["state"] == "running":
|
||||
return {"status": "pong"}
|
||||
else:
|
||||
return {"status": "not_running"}
|
||||
|
@ -266,9 +254,9 @@ class FtRestClient:
|
|||
"""
|
||||
params = {}
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
params["limit"] = limit
|
||||
if offset:
|
||||
params['offset'] = offset
|
||||
params["offset"] = offset
|
||||
return self._get("trades", params)
|
||||
|
||||
def trade(self, trade_id):
|
||||
|
@ -321,9 +309,7 @@ class FtRestClient:
|
|||
:param price: Optional - price to buy
|
||||
:return: json object of the trade
|
||||
"""
|
||||
data = {"pair": pair,
|
||||
"price": price
|
||||
}
|
||||
data = {"pair": pair, "price": price}
|
||||
return self._post("forcebuy", data=data)
|
||||
|
||||
def forceenter(self, pair, side, price=None):
|
||||
|
@ -334,11 +320,12 @@ class FtRestClient:
|
|||
:param price: Optional - price to buy
|
||||
:return: json object of the trade
|
||||
"""
|
||||
data = {"pair": pair,
|
||||
data = {
|
||||
"pair": pair,
|
||||
"side": side,
|
||||
}
|
||||
if price:
|
||||
data['price'] = price
|
||||
data["price"] = price
|
||||
return self._post("forceenter", data=data)
|
||||
|
||||
def forceexit(self, tradeid, ordertype=None, amount=None):
|
||||
|
@ -350,11 +337,14 @@ class FtRestClient:
|
|||
:return: json object
|
||||
"""
|
||||
|
||||
return self._post("forceexit", data={
|
||||
return self._post(
|
||||
"forceexit",
|
||||
data={
|
||||
"tradeid": tradeid,
|
||||
"ordertype": ordertype,
|
||||
"amount": amount,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
def strategies(self):
|
||||
"""Lists available strategies
|
||||
|
@ -392,10 +382,13 @@ class FtRestClient:
|
|||
:param stake_currency: Only pairs that include this timeframe
|
||||
:return: json object
|
||||
"""
|
||||
return self._get("available_pairs", params={
|
||||
"stake_currency": stake_currency if timeframe else '',
|
||||
"timeframe": timeframe if timeframe else '',
|
||||
})
|
||||
return self._get(
|
||||
"available_pairs",
|
||||
params={
|
||||
"stake_currency": stake_currency if timeframe else "",
|
||||
"timeframe": timeframe if timeframe else "",
|
||||
},
|
||||
)
|
||||
|
||||
def pair_candles(self, pair, timeframe, limit=None, columns=None):
|
||||
"""Return live dataframe for <pair><timeframe>.
|
||||
|
@ -411,14 +404,11 @@ class FtRestClient:
|
|||
"timeframe": timeframe,
|
||||
}
|
||||
if limit:
|
||||
params['limit'] = limit
|
||||
params["limit"] = limit
|
||||
|
||||
if columns is not None:
|
||||
params['columns'] = columns
|
||||
return self._post(
|
||||
"pair_candles",
|
||||
data=params
|
||||
)
|
||||
params["columns"] = columns
|
||||
return self._post("pair_candles", data=params)
|
||||
|
||||
return self._get("pair_candles", params=params)
|
||||
|
||||
|
@ -432,13 +422,16 @@ class FtRestClient:
|
|||
:param timerange: Timerange to get data for (same format than --timerange endpoints)
|
||||
:return: json object
|
||||
"""
|
||||
return self._get("pair_history", params={
|
||||
return self._get(
|
||||
"pair_history",
|
||||
params={
|
||||
"pair": pair,
|
||||
"timeframe": timeframe,
|
||||
"strategy": strategy,
|
||||
"freqaimodel": freqaimodel,
|
||||
"timerange": timerange if timerange else '',
|
||||
})
|
||||
"timerange": timerange if timerange else "",
|
||||
},
|
||||
)
|
||||
|
||||
def sysinfo(self):
|
||||
"""Provides system information (CPU, RAM usage)
|
||||
|
|
|
@ -14,7 +14,7 @@ def log_has_re(line, logs):
|
|||
|
||||
|
||||
def get_rest_client():
|
||||
client = FtRestClient('http://localhost:8080', 'freqtrader', 'password')
|
||||
client = FtRestClient("http://localhost:8080", "freqtrader", "password")
|
||||
client._session = MagicMock()
|
||||
request_mock = MagicMock()
|
||||
client._session.request = request_mock
|
||||
|
@ -22,93 +22,96 @@ def get_rest_client():
|
|||
|
||||
|
||||
def test_FtRestClient_init():
|
||||
client = FtRestClient('http://localhost:8080', 'freqtrader', 'password')
|
||||
client = FtRestClient("http://localhost:8080", "freqtrader", "password")
|
||||
assert client is not None
|
||||
assert client._serverurl == 'http://localhost:8080'
|
||||
assert client._serverurl == "http://localhost:8080"
|
||||
assert client._session is not None
|
||||
assert client._session.auth is not None
|
||||
assert client._session.auth == ('freqtrader', 'password')
|
||||
assert client._session.auth == ("freqtrader", "password")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('method', ['GET', 'POST', 'DELETE'])
|
||||
@pytest.mark.parametrize("method", ["GET", "POST", "DELETE"])
|
||||
def test_FtRestClient_call(method):
|
||||
client, mock = get_rest_client()
|
||||
client._call(method, '/dummytest')
|
||||
client._call(method, "/dummytest")
|
||||
assert mock.call_count == 1
|
||||
|
||||
getattr(client, f"_{method.lower()}")('/dummytest')
|
||||
getattr(client, f"_{method.lower()}")("/dummytest")
|
||||
assert mock.call_count == 2
|
||||
|
||||
|
||||
def test_FtRestClient_call_invalid(caplog):
|
||||
client, _ = get_rest_client()
|
||||
with pytest.raises(ValueError):
|
||||
client._call('PUTTY', '/dummytest')
|
||||
client._call("PUTTY", "/dummytest")
|
||||
|
||||
client._session.request = MagicMock(side_effect=ConnectionError())
|
||||
client._call('GET', '/dummytest')
|
||||
client._call("GET", "/dummytest")
|
||||
|
||||
assert log_has_re('Connection error', caplog)
|
||||
assert log_has_re("Connection error", caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('method,args', [
|
||||
('start', []),
|
||||
('stop', []),
|
||||
('stopbuy', []),
|
||||
('reload_config', []),
|
||||
('balance', []),
|
||||
('count', []),
|
||||
('entries', []),
|
||||
('exits', []),
|
||||
('mix_tags', []),
|
||||
('locks', []),
|
||||
('lock_add', ["XRP/USDT", '2024-01-01 20:00:00Z', '*', 'rand']),
|
||||
('delete_lock', [2]),
|
||||
('daily', []),
|
||||
('daily', [15]),
|
||||
('weekly', []),
|
||||
('weekly', [15]),
|
||||
('monthly', []),
|
||||
('monthly', [12]),
|
||||
('edge', []),
|
||||
('profit', []),
|
||||
('stats', []),
|
||||
('performance', []),
|
||||
('status', []),
|
||||
('version', []),
|
||||
('show_config', []),
|
||||
('ping', []),
|
||||
('logs', []),
|
||||
('logs', [55]),
|
||||
('trades', []),
|
||||
('trades', [5]),
|
||||
('trades', [5, 5]), # With offset
|
||||
('trade', [1]),
|
||||
('delete_trade', [1]),
|
||||
('cancel_open_order', [1]),
|
||||
('whitelist', []),
|
||||
('blacklist', []),
|
||||
('blacklist', ['XRP/USDT']),
|
||||
('blacklist', ['XRP/USDT', 'BTC/USDT']),
|
||||
('forcebuy', ['XRP/USDT']),
|
||||
('forcebuy', ['XRP/USDT', 1.5]),
|
||||
('forceenter', ['XRP/USDT', 'short']),
|
||||
('forceenter', ['XRP/USDT', 'short', 1.5]),
|
||||
('forceexit', [1]),
|
||||
('forceexit', [1, 'limit']),
|
||||
('forceexit', [1, 'limit', 100]),
|
||||
('strategies', []),
|
||||
('strategy', ['sampleStrategy']),
|
||||
('pairlists_available', []),
|
||||
('plot_config', []),
|
||||
('available_pairs', []),
|
||||
('available_pairs', ['5m']),
|
||||
('pair_candles', ['XRP/USDT', '5m']),
|
||||
('pair_candles', ['XRP/USDT', '5m', 500]),
|
||||
('pair_history', ['XRP/USDT', '5m', 'SampleStrategy']),
|
||||
('sysinfo', []),
|
||||
('health', []),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"method,args",
|
||||
[
|
||||
("start", []),
|
||||
("stop", []),
|
||||
("stopbuy", []),
|
||||
("reload_config", []),
|
||||
("balance", []),
|
||||
("count", []),
|
||||
("entries", []),
|
||||
("exits", []),
|
||||
("mix_tags", []),
|
||||
("locks", []),
|
||||
("lock_add", ["XRP/USDT", "2024-01-01 20:00:00Z", "*", "rand"]),
|
||||
("delete_lock", [2]),
|
||||
("daily", []),
|
||||
("daily", [15]),
|
||||
("weekly", []),
|
||||
("weekly", [15]),
|
||||
("monthly", []),
|
||||
("monthly", [12]),
|
||||
("edge", []),
|
||||
("profit", []),
|
||||
("stats", []),
|
||||
("performance", []),
|
||||
("status", []),
|
||||
("version", []),
|
||||
("show_config", []),
|
||||
("ping", []),
|
||||
("logs", []),
|
||||
("logs", [55]),
|
||||
("trades", []),
|
||||
("trades", [5]),
|
||||
("trades", [5, 5]), # With offset
|
||||
("trade", [1]),
|
||||
("delete_trade", [1]),
|
||||
("cancel_open_order", [1]),
|
||||
("whitelist", []),
|
||||
("blacklist", []),
|
||||
("blacklist", ["XRP/USDT"]),
|
||||
("blacklist", ["XRP/USDT", "BTC/USDT"]),
|
||||
("forcebuy", ["XRP/USDT"]),
|
||||
("forcebuy", ["XRP/USDT", 1.5]),
|
||||
("forceenter", ["XRP/USDT", "short"]),
|
||||
("forceenter", ["XRP/USDT", "short", 1.5]),
|
||||
("forceexit", [1]),
|
||||
("forceexit", [1, "limit"]),
|
||||
("forceexit", [1, "limit", 100]),
|
||||
("strategies", []),
|
||||
("strategy", ["sampleStrategy"]),
|
||||
("pairlists_available", []),
|
||||
("plot_config", []),
|
||||
("available_pairs", []),
|
||||
("available_pairs", ["5m"]),
|
||||
("pair_candles", ["XRP/USDT", "5m"]),
|
||||
("pair_candles", ["XRP/USDT", "5m", 500]),
|
||||
("pair_history", ["XRP/USDT", "5m", "SampleStrategy"]),
|
||||
("sysinfo", []),
|
||||
("health", []),
|
||||
],
|
||||
)
|
||||
def test_FtRestClient_call_explicit_methods(method, args):
|
||||
client, mock = get_rest_client()
|
||||
exec = getattr(client, method)
|
||||
|
@ -118,37 +121,30 @@ def test_FtRestClient_call_explicit_methods(method, args):
|
|||
|
||||
def test_ft_client(mocker, capsys, caplog):
|
||||
with pytest.raises(SystemExit):
|
||||
args = add_arguments(['-V'])
|
||||
args = add_arguments(["-V"])
|
||||
|
||||
args = add_arguments(['--show'])
|
||||
args = add_arguments(["--show"])
|
||||
assert isinstance(args, dict)
|
||||
assert args['show'] is True
|
||||
assert args["show"] is True
|
||||
with pytest.raises(SystemExit):
|
||||
main_exec(args)
|
||||
captured = capsys.readouterr()
|
||||
assert 'Possible commands' in captured.out
|
||||
assert "Possible commands" in captured.out
|
||||
|
||||
mock = mocker.patch('freqtrade_client.ft_client.FtRestClient._call')
|
||||
args = add_arguments([
|
||||
'--config',
|
||||
'tests/testdata/testconfigs/main_test_config.json',
|
||||
'ping'
|
||||
])
|
||||
mock = mocker.patch("freqtrade_client.ft_client.FtRestClient._call")
|
||||
args = add_arguments(["--config", "tests/testdata/testconfigs/main_test_config.json", "ping"])
|
||||
main_exec(args)
|
||||
captured = capsys.readouterr()
|
||||
assert mock.call_count == 1
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
args = add_arguments(['--config', 'tests/testdata/testconfigs/nonexisting.json'])
|
||||
args = add_arguments(["--config", "tests/testdata/testconfigs/nonexisting.json"])
|
||||
main_exec(args)
|
||||
|
||||
assert log_has_re(r'Could not load config file .*nonexisting\.json\.',
|
||||
caplog)
|
||||
assert log_has_re(r"Could not load config file .*nonexisting\.json\.", caplog)
|
||||
|
||||
args = add_arguments([
|
||||
'--config',
|
||||
'tests/testdata/testconfigs/main_test_config.json',
|
||||
'whatever'
|
||||
])
|
||||
args = add_arguments(
|
||||
["--config", "tests/testdata/testconfigs/main_test_config.json", "whatever"]
|
||||
)
|
||||
main_exec(args)
|
||||
assert log_has_re('Command whatever not defined', caplog)
|
||||
assert log_has_re("Command whatever not defined", caplog)
|
||||
|
|
Loading…
Reference in New Issue
Block a user