mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
Merge branch 'develop' into ci/ccxt.pro
This commit is contained in:
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -533,12 +533,12 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
- name: Publish to PyPI (Test)
|
- name: Publish to PyPI (Test)
|
||||||
uses: pypa/gh-action-pypi-publish@v1.8.14
|
uses: pypa/gh-action-pypi-publish@v1.9.0
|
||||||
with:
|
with:
|
||||||
repository-url: https://test.pypi.org/legacy/
|
repository-url: https://test.pypi.org/legacy/
|
||||||
|
|
||||||
- name: Publish to PyPI
|
- name: Publish to PyPI
|
||||||
uses: pypa/gh-action-pypi-publish@v1.8.14
|
uses: pypa/gh-action-pypi-publish@v1.9.0
|
||||||
|
|
||||||
|
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pycqa/flake8
|
- repo: https://github.com/pycqa/flake8
|
||||||
rev: "7.0.0"
|
rev: "7.1.0"
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies: [Flake8-pyproject]
|
additional_dependencies: [Flake8-pyproject]
|
||||||
@@ -31,7 +31,7 @@ repos:
|
|||||||
|
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: 'v0.4.8'
|
rev: 'v0.4.9'
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build_helpers/TA_Lib-0.4.31-cp310-cp310-win_amd64.whl
Normal file
BIN
build_helpers/TA_Lib-0.4.31-cp310-cp310-win_amd64.whl
Normal file
Binary file not shown.
BIN
build_helpers/TA_Lib-0.4.31-cp311-cp311-linux_armv7l.whl
Normal file
BIN
build_helpers/TA_Lib-0.4.31-cp311-cp311-linux_armv7l.whl
Normal file
Binary file not shown.
Binary file not shown.
BIN
build_helpers/TA_Lib-0.4.31-cp312-cp312-win_amd64.whl
Normal file
BIN
build_helpers/TA_Lib-0.4.31-cp312-cp312-win_amd64.whl
Normal file
Binary file not shown.
BIN
build_helpers/TA_Lib-0.4.31-cp39-cp39-linux_armv7l.whl
Normal file
BIN
build_helpers/TA_Lib-0.4.31-cp39-cp39-linux_armv7l.whl
Normal file
Binary file not shown.
BIN
build_helpers/TA_Lib-0.4.31-cp39-cp39-win_amd64.whl
Normal file
BIN
build_helpers/TA_Lib-0.4.31-cp39-cp39-win_amd64.whl
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
markdown==3.6
|
markdown==3.6
|
||||||
mkdocs==1.6.0
|
mkdocs==1.6.0
|
||||||
mkdocs-material==9.5.26
|
mkdocs-material==9.5.27
|
||||||
mdx_truly_sane_lists==1.3
|
mdx_truly_sane_lists==1.3
|
||||||
pymdown-extensions==10.8.1
|
pymdown-extensions==10.8.1
|
||||||
jinja2==3.1.4
|
jinja2==3.1.4
|
||||||
|
|||||||
@@ -118,6 +118,14 @@ By default, the script assumes `127.0.0.1` (localhost) and port `8080` to be use
|
|||||||
freqtrade-client --config rest_config.json <command> [optional parameters]
|
freqtrade-client --config rest_config.json <command> [optional parameters]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Commands with many arguments may require keyword arguments (for clarity) - which can be provided as follows:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
freqtrade-client --config rest_config.json forceenter BTC/USDT long enter_tag=GutFeeling
|
||||||
|
```
|
||||||
|
|
||||||
|
This method will work for all arguments - check the "show" command for a list of available parameters.
|
||||||
|
|
||||||
??? Note "Programmatic use"
|
??? Note "Programmatic use"
|
||||||
The `freqtrade-client` package (installable independent of freqtrade) can be used in your own scripts to interact with the freqtrade API.
|
The `freqtrade-client` package (installable independent of freqtrade) can be used in your own scripts to interact with the freqtrade API.
|
||||||
to do so, please use the following:
|
to do so, please use the following:
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ class Exchange:
|
|||||||
self._exchange_ws.cleanup()
|
self._exchange_ws.cleanup()
|
||||||
logger.debug("Exchange object destroyed, closing async loop")
|
logger.debug("Exchange object destroyed, closing async loop")
|
||||||
if (
|
if (
|
||||||
self._api_async
|
getattr(self, "_api_async", None)
|
||||||
and inspect.iscoroutinefunction(self._api_async.close)
|
and inspect.iscoroutinefunction(self._api_async.close)
|
||||||
and self._api_async.session
|
and self._api_async.session
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -1466,6 +1466,8 @@ class RPC:
|
|||||||
from freqtrade.resolvers.strategy_resolver import StrategyResolver
|
from freqtrade.resolvers.strategy_resolver import StrategyResolver
|
||||||
|
|
||||||
strategy = StrategyResolver.load_strategy(config)
|
strategy = StrategyResolver.load_strategy(config)
|
||||||
|
# Manually load hyperparameters, as we don't call the bot-start callback.
|
||||||
|
strategy.ft_load_hyper_params(False)
|
||||||
|
|
||||||
if strategy.plot_config and "subplots" not in strategy.plot_config:
|
if strategy.plot_config and "subplots" not in strategy.plot_config:
|
||||||
strategy.plot_config["subplots"] = {}
|
strategy.plot_config["subplots"] = {}
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ def print_commands():
|
|||||||
print(f"{x}\n\t{doc}\n")
|
print(f"{x}\n\t{doc}\n")
|
||||||
|
|
||||||
|
|
||||||
def main_exec(args: Dict[str, Any]):
|
def main_exec(parsed: Dict[str, Any]):
|
||||||
if args.get("show"):
|
if parsed.get("show"):
|
||||||
print_commands()
|
print_commands()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
config = load_config(args["config"])
|
config = load_config(parsed["config"])
|
||||||
url = config.get("api_server", {}).get("listen_ip_address", "127.0.0.1")
|
url = config.get("api_server", {}).get("listen_ip_address", "127.0.0.1")
|
||||||
port = config.get("api_server", {}).get("listen_port", "8080")
|
port = config.get("api_server", {}).get("listen_port", "8080")
|
||||||
username = config.get("api_server", {}).get("username")
|
username = config.get("api_server", {}).get("username")
|
||||||
@@ -96,13 +96,24 @@ def main_exec(args: Dict[str, Any]):
|
|||||||
client = FtRestClient(server_url, username, password)
|
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"]
|
command = parsed["command"]
|
||||||
if command not in m:
|
if command not in m:
|
||||||
logger.error(f"Command {command} not defined")
|
logger.error(f"Command {command} not defined")
|
||||||
print_commands()
|
print_commands()
|
||||||
return
|
return
|
||||||
|
|
||||||
print(json.dumps(getattr(client, command)(*args["command_arguments"])))
|
# Split arguments with = into key/value pairs
|
||||||
|
kwargs = {x.split("=")[0]: x.split("=")[1] for x in parsed["command_arguments"] if "=" in x}
|
||||||
|
args = [x for x in parsed["command_arguments"] if "=" not in x]
|
||||||
|
try:
|
||||||
|
res = getattr(client, command)(*args, **kwargs)
|
||||||
|
print(json.dumps(res))
|
||||||
|
except TypeError as e:
|
||||||
|
logger.error(f"Error executing command {command}: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fatal Error executing command {command}: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class FtRestClient:
|
|||||||
# return resp.text
|
# return resp.text
|
||||||
return resp.json()
|
return resp.json()
|
||||||
except ConnectionError:
|
except ConnectionError:
|
||||||
logger.warning("Connection error")
|
logger.warning(f"Connection error - could not connect to {netloc}.")
|
||||||
|
|
||||||
def _get(self, apipath, params: ParamsT = None):
|
def _get(self, apipath, params: ParamsT = None):
|
||||||
return self._call("GET", apipath, params=params)
|
return self._call("GET", apipath, params=params)
|
||||||
@@ -312,20 +312,48 @@ class FtRestClient:
|
|||||||
data = {"pair": pair, "price": price}
|
data = {"pair": pair, "price": price}
|
||||||
return self._post("forcebuy", data=data)
|
return self._post("forcebuy", data=data)
|
||||||
|
|
||||||
def forceenter(self, pair, side, price=None):
|
def forceenter(
|
||||||
|
self,
|
||||||
|
pair,
|
||||||
|
side,
|
||||||
|
price=None,
|
||||||
|
*,
|
||||||
|
order_type=None,
|
||||||
|
stake_amount=None,
|
||||||
|
leverage=None,
|
||||||
|
enter_tag=None,
|
||||||
|
):
|
||||||
"""Force entering a trade
|
"""Force entering a trade
|
||||||
|
|
||||||
:param pair: Pair to buy (ETH/BTC)
|
:param pair: Pair to buy (ETH/BTC)
|
||||||
:param side: 'long' or 'short'
|
:param side: 'long' or 'short'
|
||||||
:param price: Optional - price to buy
|
:param price: Optional - price to buy
|
||||||
|
:param order_type: Optional keyword argument - 'limit' or 'market'
|
||||||
|
:param stake_amount: Optional keyword argument - stake amount (as float)
|
||||||
|
:param leverage: Optional keyword argument - leverage (as float)
|
||||||
|
:param enter_tag: Optional keyword argument - entry tag (as string, default: 'force_enter')
|
||||||
:return: json object of the trade
|
:return: json object of the trade
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
"pair": pair,
|
"pair": pair,
|
||||||
"side": side,
|
"side": side,
|
||||||
}
|
}
|
||||||
|
|
||||||
if price:
|
if price:
|
||||||
data["price"] = price
|
data["price"] = price
|
||||||
|
|
||||||
|
if order_type:
|
||||||
|
data["ordertype"] = order_type
|
||||||
|
|
||||||
|
if stake_amount:
|
||||||
|
data["stakeamount"] = stake_amount
|
||||||
|
|
||||||
|
if leverage:
|
||||||
|
data["leverage"] = leverage
|
||||||
|
|
||||||
|
if enter_tag:
|
||||||
|
data["entry_tag"] = enter_tag
|
||||||
|
|
||||||
return self._post("forceenter", data=data)
|
return self._post("forceenter", data=data)
|
||||||
|
|
||||||
def forceexit(self, tradeid, ordertype=None, amount=None):
|
def forceexit(self, tradeid, ordertype=None, amount=None):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import ANY, MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from requests.exceptions import ConnectionError
|
from requests.exceptions import ConnectionError
|
||||||
@@ -52,70 +52,89 @@ def test_FtRestClient_call_invalid(caplog):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"method,args",
|
"method,args,kwargs",
|
||||||
[
|
[
|
||||||
("start", []),
|
("start", [], {}),
|
||||||
("stop", []),
|
("stop", [], {}),
|
||||||
("stopbuy", []),
|
("stopbuy", [], {}),
|
||||||
("reload_config", []),
|
("reload_config", [], {}),
|
||||||
("balance", []),
|
("balance", [], {}),
|
||||||
("count", []),
|
("count", [], {}),
|
||||||
("entries", []),
|
("entries", [], {}),
|
||||||
("exits", []),
|
("exits", [], {}),
|
||||||
("mix_tags", []),
|
("mix_tags", [], {}),
|
||||||
("locks", []),
|
("locks", [], {}),
|
||||||
("lock_add", ["XRP/USDT", "2024-01-01 20:00:00Z", "*", "rand"]),
|
("lock_add", ["XRP/USDT", "2024-01-01 20:00:00Z", "*", "rand"], {}),
|
||||||
("delete_lock", [2]),
|
("delete_lock", [2], {}),
|
||||||
("daily", []),
|
("daily", [], {}),
|
||||||
("daily", [15]),
|
("daily", [15], {}),
|
||||||
("weekly", []),
|
("weekly", [], {}),
|
||||||
("weekly", [15]),
|
("weekly", [15], {}),
|
||||||
("monthly", []),
|
("monthly", [], {}),
|
||||||
("monthly", [12]),
|
("monthly", [12], {}),
|
||||||
("edge", []),
|
("edge", [], {}),
|
||||||
("profit", []),
|
("profit", [], {}),
|
||||||
("stats", []),
|
("stats", [], {}),
|
||||||
("performance", []),
|
("performance", [], {}),
|
||||||
("status", []),
|
("status", [], {}),
|
||||||
("version", []),
|
("version", [], {}),
|
||||||
("show_config", []),
|
("show_config", [], {}),
|
||||||
("ping", []),
|
("ping", [], {}),
|
||||||
("logs", []),
|
("logs", [], {}),
|
||||||
("logs", [55]),
|
("logs", [55], {}),
|
||||||
("trades", []),
|
("trades", [], {}),
|
||||||
("trades", [5]),
|
("trades", [5], {}),
|
||||||
("trades", [5, 5]), # With offset
|
("trades", [5, 5], {}), # With offset
|
||||||
("trade", [1]),
|
("trade", [1], {}),
|
||||||
("delete_trade", [1]),
|
("delete_trade", [1], {}),
|
||||||
("cancel_open_order", [1]),
|
("cancel_open_order", [1], {}),
|
||||||
("whitelist", []),
|
("whitelist", [], {}),
|
||||||
("blacklist", []),
|
("blacklist", [], {}),
|
||||||
("blacklist", ["XRP/USDT"]),
|
("blacklist", ["XRP/USDT"], {}),
|
||||||
("blacklist", ["XRP/USDT", "BTC/USDT"]),
|
("blacklist", ["XRP/USDT", "BTC/USDT"], {}),
|
||||||
("forcebuy", ["XRP/USDT"]),
|
("forcebuy", ["XRP/USDT"], {}),
|
||||||
("forcebuy", ["XRP/USDT", 1.5]),
|
("forcebuy", ["XRP/USDT", 1.5], {}),
|
||||||
("forceenter", ["XRP/USDT", "short"]),
|
("forceenter", ["XRP/USDT", "short"], {}),
|
||||||
("forceenter", ["XRP/USDT", "short", 1.5]),
|
("forceenter", ["XRP/USDT", "short", 1.5], {}),
|
||||||
("forceexit", [1]),
|
("forceenter", ["XRP/USDT", "short", 1.5], {"order_type": "market"}),
|
||||||
("forceexit", [1, "limit"]),
|
("forceenter", ["XRP/USDT", "short", 1.5], {"order_type": "market", "stake_amount": 100}),
|
||||||
("forceexit", [1, "limit", 100]),
|
(
|
||||||
("strategies", []),
|
"forceenter",
|
||||||
("strategy", ["sampleStrategy"]),
|
["XRP/USDT", "short", 1.5],
|
||||||
("pairlists_available", []),
|
{"order_type": "market", "stake_amount": 100, "leverage": 10.0},
|
||||||
("plot_config", []),
|
),
|
||||||
("available_pairs", []),
|
(
|
||||||
("available_pairs", ["5m"]),
|
"forceenter",
|
||||||
("pair_candles", ["XRP/USDT", "5m"]),
|
["XRP/USDT", "short", 1.5],
|
||||||
("pair_candles", ["XRP/USDT", "5m", 500]),
|
{
|
||||||
("pair_history", ["XRP/USDT", "5m", "SampleStrategy"]),
|
"order_type": "market",
|
||||||
("sysinfo", []),
|
"stake_amount": 100,
|
||||||
("health", []),
|
"leverage": 10.0,
|
||||||
|
"enter_tag": "test_force_enter",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
("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_candles", ["XRP/USDT", "5m", 500], {"columns": ["close_time,close"]}),
|
||||||
|
("pair_history", ["XRP/USDT", "5m", "SampleStrategy"], {}),
|
||||||
|
("pair_history", ["XRP/USDT", "5m"], {"strategy": "SampleStrategy"}),
|
||||||
|
("sysinfo", [], {}),
|
||||||
|
("health", [], {}),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_FtRestClient_call_explicit_methods(method, args):
|
def test_FtRestClient_call_explicit_methods(method, args, kwargs):
|
||||||
client, mock = get_rest_client()
|
client, mock = get_rest_client()
|
||||||
exec = getattr(client, method)
|
exec = getattr(client, method)
|
||||||
exec(*args)
|
exec(*args, **kwargs)
|
||||||
assert mock.call_count == 1
|
assert mock.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
@@ -148,3 +167,40 @@ def test_ft_client(mocker, capsys, caplog):
|
|||||||
)
|
)
|
||||||
main_exec(args)
|
main_exec(args)
|
||||||
assert log_has_re("Command whatever not defined", caplog)
|
assert log_has_re("Command whatever not defined", caplog)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"params, expected_args, expected_kwargs",
|
||||||
|
[
|
||||||
|
("forceenter BTC/USDT long", ["BTC/USDT", "long"], {}),
|
||||||
|
("forceenter BTC/USDT long limit", ["BTC/USDT", "long", "limit"], {}),
|
||||||
|
(
|
||||||
|
# Skip most parameters, only providing enter_tag
|
||||||
|
"forceenter BTC/USDT long enter_tag=deadBeef",
|
||||||
|
["BTC/USDT", "long"],
|
||||||
|
{"enter_tag": "deadBeef"},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"forceenter BTC/USDT long invalid_key=123",
|
||||||
|
[],
|
||||||
|
SystemExit,
|
||||||
|
# {"invalid_key": "deadBeef"},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_ft_client_argparsing(mocker, params, expected_args, expected_kwargs, caplog):
|
||||||
|
mocked_method = params.split(" ")[0]
|
||||||
|
mocker.patch("freqtrade_client.ft_client.load_config", return_value={}, autospec=True)
|
||||||
|
mm = mocker.patch(
|
||||||
|
f"freqtrade_client.ft_client.FtRestClient.{mocked_method}", return_value={}, autospec=True
|
||||||
|
)
|
||||||
|
args = add_arguments(params.split(" "))
|
||||||
|
if isinstance(expected_kwargs, dict):
|
||||||
|
main_exec(args)
|
||||||
|
mm.assert_called_once_with(ANY, *expected_args, **expected_kwargs)
|
||||||
|
else:
|
||||||
|
with pytest.raises(expected_kwargs):
|
||||||
|
main_exec(args)
|
||||||
|
|
||||||
|
assert log_has_re(f"Error executing command {mocked_method}: got an unexpected .*", caplog)
|
||||||
|
mm.assert_not_called()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
-r docs/requirements-docs.txt
|
-r docs/requirements-docs.txt
|
||||||
|
|
||||||
coveralls==4.0.1
|
coveralls==4.0.1
|
||||||
ruff==0.4.8
|
ruff==0.4.9
|
||||||
mypy==1.10.0
|
mypy==1.10.0
|
||||||
pre-commit==3.7.1
|
pre-commit==3.7.1
|
||||||
pytest==8.2.2
|
pytest==8.2.2
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
-r requirements-freqai.txt
|
-r requirements-freqai.txt
|
||||||
|
|
||||||
# Required for freqai-rl
|
# Required for freqai-rl
|
||||||
torch==2.2.2
|
torch==2.3.1; sys_platform != 'darwin' or platform_machine != 'x86_64'
|
||||||
|
torch==2.2.2; sys_platform == 'darwin' and platform_machine == 'x86_64'
|
||||||
gymnasium==0.29.1
|
gymnasium==0.29.1
|
||||||
stable_baselines3==2.3.2
|
stable_baselines3==2.3.2
|
||||||
sb3_contrib>=2.2.1
|
sb3_contrib>=2.2.1
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
scikit-learn==1.5.0
|
scikit-learn==1.5.0
|
||||||
joblib==1.4.2
|
joblib==1.4.2
|
||||||
catboost==1.2.5; 'arm' not in platform_machine
|
catboost==1.2.5; 'arm' not in platform_machine
|
||||||
lightgbm==4.3.0
|
lightgbm==4.4.0
|
||||||
xgboost==2.0.3
|
xgboost==2.0.3
|
||||||
tensorboard==2.17.0
|
tensorboard==2.17.0
|
||||||
datasieve==0.1.7
|
datasieve==0.1.7
|
||||||
|
|||||||
@@ -5,4 +5,4 @@
|
|||||||
scipy==1.13.1
|
scipy==1.13.1
|
||||||
scikit-learn==1.5.0
|
scikit-learn==1.5.0
|
||||||
ft-scikit-optimize==0.9.2
|
ft-scikit-optimize==0.9.2
|
||||||
filelock==3.14.0
|
filelock==3.15.1
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ bottleneck==1.3.8
|
|||||||
numexpr==2.10.0
|
numexpr==2.10.0
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==4.3.42
|
ccxt==4.3.46
|
||||||
cryptography==42.0.8
|
cryptography==42.0.8
|
||||||
aiohttp==3.9.5
|
aiohttp==3.9.5
|
||||||
SQLAlchemy==2.0.30
|
SQLAlchemy==2.0.30
|
||||||
@@ -14,9 +14,9 @@ httpx>=0.24.1
|
|||||||
humanize==4.9.0
|
humanize==4.9.0
|
||||||
cachetools==5.3.3
|
cachetools==5.3.3
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
urllib3==2.2.1
|
urllib3==2.2.2
|
||||||
jsonschema==4.22.0
|
jsonschema==4.22.0
|
||||||
TA-Lib==0.4.30
|
TA-Lib==0.4.31
|
||||||
technical==1.4.3
|
technical==1.4.3
|
||||||
tabulate==0.9.0
|
tabulate==0.9.0
|
||||||
pycoingecko==3.1.0
|
pycoingecko==3.1.0
|
||||||
@@ -32,14 +32,14 @@ py_find_1st==1.1.6
|
|||||||
# Load ticker files 30% faster
|
# Load ticker files 30% faster
|
||||||
python-rapidjson==1.17
|
python-rapidjson==1.17
|
||||||
# Properly format api responses
|
# Properly format api responses
|
||||||
orjson==3.10.3
|
orjson==3.10.5
|
||||||
|
|
||||||
# Notify systemd
|
# Notify systemd
|
||||||
sdnotify==0.3.2
|
sdnotify==0.3.2
|
||||||
|
|
||||||
# API Server
|
# API Server
|
||||||
fastapi==0.111.0
|
fastapi==0.111.0
|
||||||
pydantic==2.7.3
|
pydantic==2.7.4
|
||||||
uvicorn==0.30.1
|
uvicorn==0.30.1
|
||||||
pyjwt==2.8.0
|
pyjwt==2.8.0
|
||||||
aiofiles==23.2.1
|
aiofiles==23.2.1
|
||||||
|
|||||||
Reference in New Issue
Block a user