mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-12-19 06:11:15 +00:00
Merge branch 'develop' into api-server-list-custom-data
This commit is contained in:
@@ -19,7 +19,7 @@ repos:
|
|||||||
- types-requests==2.32.0.20250306
|
- types-requests==2.32.0.20250306
|
||||||
- types-tabulate==0.9.0.20241207
|
- types-tabulate==0.9.0.20241207
|
||||||
- types-python-dateutil==2.9.0.20241206
|
- types-python-dateutil==2.9.0.20241206
|
||||||
- SQLAlchemy==2.0.38
|
- SQLAlchemy==2.0.39
|
||||||
# stages: [push]
|
# stages: [push]
|
||||||
|
|
||||||
- repo: https://github.com/pycqa/isort
|
- repo: https://github.com/pycqa/isort
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 
|
# 
|
||||||
|
|
||||||
[](https://github.com/freqtrade/freqtrade/actions/)
|
[](https://github.com/freqtrade/freqtrade/actions/)
|
||||||
[](https://doi.org/10.21105/joss.04864)
|
[](https://doi.org/10.21105/joss.04864)
|
||||||
[](https://coveralls.io/github/freqtrade/freqtrade?branch=develop)
|
[](https://coveralls.io/github/freqtrade/freqtrade?branch=develop)
|
||||||
[](https://www.freqtrade.io)
|
[](https://www.freqtrade.io)
|
||||||
|
|||||||
@@ -613,6 +613,14 @@
|
|||||||
"description": "Telegram topic ID - only applicable for group chats",
|
"description": "Telegram topic ID - only applicable for group chats",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"authorized_users": {
|
||||||
|
"description": "Authorized users for the bot.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
"allow_custom_messages": {
|
"allow_custom_messages": {
|
||||||
"description": "Allow sending custom messages from the Strategy.",
|
"description": "Allow sending custom messages from the Strategy.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -1417,8 +1425,7 @@
|
|||||||
},
|
},
|
||||||
"fit_live_predictions_candles": {
|
"fit_live_predictions_candles": {
|
||||||
"description": "Number of historical candles to use for computing target (label) statistics from prediction data, instead of from the training dataset.",
|
"description": "Number of historical candles to use for computing target (label) statistics from prediction data, instead of from the training dataset.",
|
||||||
"type": "boolean",
|
"type": "integer"
|
||||||
"default": false
|
|
||||||
},
|
},
|
||||||
"data_kitchen_thread_count": {
|
"data_kitchen_thread_count": {
|
||||||
"description": "Designate the number of threads you want to use for data processing (outlier methods, normalization, etc.).",
|
"description": "Designate the number of threads you want to use for data processing (outlier methods, normalization, etc.).",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://github.com/freqtrade/freqtrade/actions/)
|
[](https://github.com/freqtrade/freqtrade/actions/)
|
||||||
[](https://doi.org/10.21105/joss.04864)
|
[](https://doi.org/10.21105/joss.04864)
|
||||||
[](https://coveralls.io/github/freqtrade/freqtrade?branch=develop)
|
[](https://coveralls.io/github/freqtrade/freqtrade?branch=develop)
|
||||||
[](https://codeclimate.com/github/freqtrade/freqtrade/maintainability)
|
[](https://codeclimate.com/github/freqtrade/freqtrade/maintainability)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
markdown==3.7
|
markdown==3.7
|
||||||
mkdocs==1.6.1
|
mkdocs==1.6.1
|
||||||
mkdocs-material==9.6.7
|
mkdocs-material==9.6.8
|
||||||
mdx_truly_sane_lists==1.3
|
mdx_truly_sane_lists==1.3
|
||||||
pymdown-extensions==10.14.3
|
pymdown-extensions==10.14.3
|
||||||
jinja2==3.1.6
|
jinja2==3.1.6
|
||||||
|
|||||||
@@ -81,6 +81,19 @@ Without this, the bot will always respond to the general channel in the group if
|
|||||||
|
|
||||||
Similar to the group-id - you can use `/tg_info` from the topic/thread to get the correct topic-id.
|
Similar to the group-id - you can use `/tg_info` from the topic/thread to get the correct topic-id.
|
||||||
|
|
||||||
|
#### Authorized users
|
||||||
|
|
||||||
|
For groups, it can be useful to limit who can send commands to the bot.
|
||||||
|
|
||||||
|
If `"authorized_users": []` is present and empty, no user will be allowed to control the bot.
|
||||||
|
In the below example, only the user with the id "1234567" is allowed to control the bot - all other users will only be able to receive messages.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"chat_id": "-1001332619709",
|
||||||
|
"topic_id": "3",
|
||||||
|
"authorized_users": ["1234567"]
|
||||||
|
```
|
||||||
|
|
||||||
## Control telegram noise
|
## Control telegram noise
|
||||||
|
|
||||||
Freqtrade provides means to control the verbosity of your telegram bot.
|
Freqtrade provides means to control the verbosity of your telegram bot.
|
||||||
|
|||||||
@@ -471,6 +471,12 @@ CONF_SCHEMA = {
|
|||||||
"description": "Telegram topic ID - only applicable for group chats",
|
"description": "Telegram topic ID - only applicable for group chats",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
"authorized_users": {
|
||||||
|
"description": "Authorized users for the bot.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
"uniqueItems": True,
|
||||||
|
},
|
||||||
"allow_custom_messages": {
|
"allow_custom_messages": {
|
||||||
"description": "Allow sending custom messages from the Strategy.",
|
"description": "Allow sending custom messages from the Strategy.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -1026,8 +1032,7 @@ CONF_SCHEMA = {
|
|||||||
"Number of historical candles to use for computing target (label) "
|
"Number of historical candles to use for computing target (label) "
|
||||||
"statistics from prediction data, instead of from the training dataset."
|
"statistics from prediction data, instead of from the training dataset."
|
||||||
),
|
),
|
||||||
"type": "boolean",
|
"type": "integer",
|
||||||
"default": False,
|
|
||||||
},
|
},
|
||||||
"data_kitchen_thread_count": {
|
"data_kitchen_thread_count": {
|
||||||
"description": (
|
"description": (
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2441,8 +2441,8 @@ class Exchange:
|
|||||||
|
|
||||||
return self._exchange_ws.get_ohlcv(pair, timeframe, candle_type, candle_ts)
|
return self._exchange_ws.get_ohlcv(pair, timeframe, candle_type, candle_ts)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Failed to reuse watch {pair}, {timeframe}, {candle_ts < last_refresh_time},"
|
f"Couldn't reuse watch for {pair}, {timeframe}, falling back to REST api. "
|
||||||
f" {candle_ts}, {last_refresh_time}, "
|
f"{candle_ts < last_refresh_time}, {candle_ts}, {last_refresh_time}, "
|
||||||
f"{format_ms_time(candle_ts)}, {format_ms_time(last_refresh_time)} "
|
f"{format_ms_time(candle_ts)}, {format_ms_time(last_refresh_time)} "
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ def _get_resample_from_period(period: str) -> str:
|
|||||||
if period == "month":
|
if period == "month":
|
||||||
return "1ME"
|
return "1ME"
|
||||||
if period == "year":
|
if period == "year":
|
||||||
return "1Y"
|
return "1YE"
|
||||||
raise ValueError(f"Period {period} is not supported.")
|
raise ValueError(f"Period {period} is not supported.")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from telegram import (
|
|||||||
InlineKeyboardButton,
|
InlineKeyboardButton,
|
||||||
InlineKeyboardMarkup,
|
InlineKeyboardMarkup,
|
||||||
KeyboardButton,
|
KeyboardButton,
|
||||||
|
Message,
|
||||||
ReplyKeyboardMarkup,
|
ReplyKeyboardMarkup,
|
||||||
Update,
|
Update,
|
||||||
)
|
)
|
||||||
@@ -96,17 +97,17 @@ def authorized_only(command_handler: Callable[..., Coroutine[Any, Any, None]]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(command_handler)
|
@wraps(command_handler)
|
||||||
async def wrapper(self, *args, **kwargs):
|
async def wrapper(self, *args, **kwargs) -> None:
|
||||||
"""Decorator logic"""
|
"""Decorator logic"""
|
||||||
update = kwargs.get("update") or args[0]
|
update = kwargs.get("update") or args[0]
|
||||||
|
|
||||||
# Reject unauthorized messages
|
# Reject unauthorized messages
|
||||||
if update.callback_query:
|
message: Message = (
|
||||||
cchat_id = int(update.callback_query.message.chat.id)
|
update.message if update.callback_query is None else update.callback_query.message
|
||||||
ctopic_id = update.callback_query.message.message_thread_id
|
)
|
||||||
else:
|
cchat_id: int = int(message.chat_id)
|
||||||
cchat_id = int(update.message.chat_id)
|
ctopic_id: int | None = message.message_thread_id
|
||||||
ctopic_id = update.message.message_thread_id
|
from_user_id: str = str(update.effective_user.id if update.effective_user else "")
|
||||||
|
|
||||||
chat_id = int(self._config["telegram"]["chat_id"])
|
chat_id = int(self._config["telegram"]["chat_id"])
|
||||||
if cchat_id != chat_id:
|
if cchat_id != chat_id:
|
||||||
@@ -118,6 +119,10 @@ def authorized_only(command_handler: Callable[..., Coroutine[Any, Any, None]]):
|
|||||||
logger.debug(f"Rejected message from wrong channel: {cchat_id}, {ctopic_id}")
|
logger.debug(f"Rejected message from wrong channel: {cchat_id}, {ctopic_id}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
authorized = self._config["telegram"].get("authorized_users", None)
|
||||||
|
if authorized is not None and from_user_id not in authorized:
|
||||||
|
logger.info(f"Unauthorized user tried to control the bot: {from_user_id}")
|
||||||
|
return None
|
||||||
# Rollback session to avoid getting data stored in a transaction.
|
# Rollback session to avoid getting data stored in a transaction.
|
||||||
Trade.rollback()
|
Trade.rollback()
|
||||||
logger.debug("Executing handler: %s for chat_id: %s", command_handler.__name__, chat_id)
|
logger.debug("Executing handler: %s for chat_id: %s", command_handler.__name__, chat_id)
|
||||||
@@ -2155,6 +2160,9 @@ class Telegram(RPCHandler):
|
|||||||
return
|
return
|
||||||
chat_id = update.message.chat_id
|
chat_id = update.message.chat_id
|
||||||
topic_id = update.message.message_thread_id
|
topic_id = update.message.message_thread_id
|
||||||
|
user_id = (
|
||||||
|
update.effective_user.id if topic_id is not None and update.effective_user else None
|
||||||
|
)
|
||||||
|
|
||||||
msg = f"""Freqtrade Bot Info:
|
msg = f"""Freqtrade Bot Info:
|
||||||
```json
|
```json
|
||||||
@@ -2162,7 +2170,8 @@ class Telegram(RPCHandler):
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"token": "********",
|
"token": "********",
|
||||||
"chat_id": "{chat_id}",
|
"chat_id": "{chat_id}",
|
||||||
{f'"topic_id": "{topic_id}"' if topic_id else ""}
|
{f'"topic_id": "{topic_id}",' if topic_id else ""}
|
||||||
|
{f'//"authorized_users": ["{user_id}"]' if topic_id and user_id else ""}
|
||||||
}}
|
}}
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ classifiers = [
|
|||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
# from requirements.txt
|
# from requirements.txt
|
||||||
"ccxt>=4.3.24",
|
"ccxt>=4.4.60",
|
||||||
"SQLAlchemy>=2.0.6",
|
"SQLAlchemy>=2.0.6",
|
||||||
"python-telegram-bot>=20.1",
|
"python-telegram-bot>=20.1",
|
||||||
"humanize>=4.0.0",
|
"humanize>=4.0.0",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
-r docs/requirements-docs.txt
|
-r docs/requirements-docs.txt
|
||||||
|
|
||||||
coveralls==4.0.1
|
coveralls==4.0.1
|
||||||
ruff==0.9.10
|
ruff==0.11.0
|
||||||
mypy==1.15.0
|
mypy==1.15.0
|
||||||
pre-commit==4.1.0
|
pre-commit==4.1.0
|
||||||
pytest==8.3.5
|
pytest==8.3.5
|
||||||
|
|||||||
@@ -5,4 +5,4 @@
|
|||||||
scipy==1.15.2
|
scipy==1.15.2
|
||||||
scikit-learn==1.6.1
|
scikit-learn==1.6.1
|
||||||
ft-scikit-optimize==0.9.2
|
ft-scikit-optimize==0.9.2
|
||||||
filelock==3.17.0
|
filelock==3.18.0
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ bottleneck==1.4.2
|
|||||||
numexpr==2.10.2
|
numexpr==2.10.2
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==4.4.65
|
ccxt==4.4.68
|
||||||
cryptography==44.0.2
|
cryptography==44.0.2
|
||||||
aiohttp==3.9.5
|
aiohttp==3.9.5
|
||||||
SQLAlchemy==2.0.38
|
SQLAlchemy==2.0.39
|
||||||
python-telegram-bot==21.11.1
|
python-telegram-bot==22.0
|
||||||
# can't be hard-pinned due to telegram-bot pinning httpx with ~
|
# can't be hard-pinned due to telegram-bot pinning httpx with ~
|
||||||
httpx>=0.24.1
|
httpx>=0.24.1
|
||||||
humanize==4.12.1
|
humanize==4.12.1
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import timedelta
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
from string import ascii_uppercase
|
from string import ascii_uppercase
|
||||||
@@ -16,7 +16,7 @@ import pytest
|
|||||||
import time_machine
|
import time_machine
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from telegram import Chat, Message, ReplyKeyboardMarkup, Update
|
from telegram import Chat, Message, ReplyKeyboardMarkup, Update, User
|
||||||
from telegram.error import BadRequest, NetworkError, TelegramError
|
from telegram.error import BadRequest, NetworkError, TelegramError
|
||||||
|
|
||||||
from freqtrade import __version__
|
from freqtrade import __version__
|
||||||
@@ -67,7 +67,12 @@ def default_conf(default_conf) -> dict:
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def update():
|
def update():
|
||||||
message = Message(0, datetime.now(timezone.utc), Chat(1235, 0))
|
message = Message(
|
||||||
|
0,
|
||||||
|
dt_now(),
|
||||||
|
Chat(1235, 0),
|
||||||
|
from_user=User(5432, "test", is_bot=False),
|
||||||
|
)
|
||||||
_update = Update(0, message=message)
|
_update = Update(0, message=message)
|
||||||
|
|
||||||
return _update
|
return _update
|
||||||
@@ -232,8 +237,12 @@ async def test_authorized_only(default_conf, mocker, caplog, update) -> None:
|
|||||||
async def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
|
async def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
caplog.set_level(logging.DEBUG)
|
caplog.set_level(logging.DEBUG)
|
||||||
chat = Chat(0xDEADBEEF, 0)
|
message = Message(
|
||||||
message = Message(randint(1, 100), datetime.now(timezone.utc), chat)
|
randint(1, 100),
|
||||||
|
dt_now(),
|
||||||
|
Chat(0xDEADBEEF, 0),
|
||||||
|
from_user=User(5432, "test", is_bot=False),
|
||||||
|
)
|
||||||
update = Update(randint(1, 100), message=message)
|
update = Update(randint(1, 100), message=message)
|
||||||
|
|
||||||
default_conf["telegram"]["enabled"] = False
|
default_conf["telegram"]["enabled"] = False
|
||||||
@@ -249,6 +258,42 @@ async def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> Non
|
|||||||
assert not log_has("Exception occurred within Telegram module", caplog)
|
assert not log_has("Exception occurred within Telegram module", caplog)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_authorized_users(default_conf, mocker, caplog, update) -> None:
|
||||||
|
patch_exchange(mocker)
|
||||||
|
caplog.set_level(logging.DEBUG)
|
||||||
|
default_conf["telegram"]["enabled"] = False
|
||||||
|
default_conf["telegram"]["authorized_users"] = ["5432"]
|
||||||
|
bot = FreqtradeBot(default_conf)
|
||||||
|
rpc = RPC(bot)
|
||||||
|
dummy = DummyCls(rpc, default_conf)
|
||||||
|
|
||||||
|
await dummy.dummy_handler(update=update, context=MagicMock())
|
||||||
|
assert dummy.state["called"] is True
|
||||||
|
assert log_has("Executing handler: dummy_handler for chat_id: 1235", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
# Test empty case
|
||||||
|
default_conf["telegram"]["authorized_users"] = []
|
||||||
|
dummy1 = DummyCls(rpc, default_conf)
|
||||||
|
await dummy1.dummy_handler(update=update, context=MagicMock())
|
||||||
|
assert dummy1.state["called"] is False
|
||||||
|
assert log_has_re(r"Unauthorized user tried to .*5432", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
# Test wrong user
|
||||||
|
default_conf["telegram"]["authorized_users"] = ["1234"]
|
||||||
|
dummy1 = DummyCls(rpc, default_conf)
|
||||||
|
await dummy1.dummy_handler(update=update, context=MagicMock())
|
||||||
|
assert dummy1.state["called"] is False
|
||||||
|
assert log_has_re(r"Unauthorized user tried to .*5432", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
# Test reverse case again
|
||||||
|
default_conf["telegram"]["authorized_users"] = ["5432"]
|
||||||
|
dummy1 = DummyCls(rpc, default_conf)
|
||||||
|
await dummy1.dummy_handler(update=update, context=MagicMock())
|
||||||
|
assert dummy1.state["called"] is True
|
||||||
|
assert not log_has_re(r"Unauthorized user tried to .*5432", caplog)
|
||||||
|
|
||||||
|
|
||||||
async def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None:
|
async def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None:
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
|
||||||
@@ -638,7 +683,7 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
|
|||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
assert "Daily Profit over the last 2 days</b>:" in msg_mock.call_args_list[0][0][0]
|
assert "Daily Profit over the last 2 days</b>:" in msg_mock.call_args_list[0][0][0]
|
||||||
assert "Day " in msg_mock.call_args_list[0][0][0]
|
assert "Day " in msg_mock.call_args_list[0][0][0]
|
||||||
assert str(datetime.now(timezone.utc).date()) in msg_mock.call_args_list[0][0][0]
|
assert str(dt_now().date()) in msg_mock.call_args_list[0][0][0]
|
||||||
assert " 6.83 USDT" in msg_mock.call_args_list[0][0][0]
|
assert " 6.83 USDT" in msg_mock.call_args_list[0][0][0]
|
||||||
assert " 7.51 USD" in msg_mock.call_args_list[0][0][0]
|
assert " 7.51 USD" in msg_mock.call_args_list[0][0][0]
|
||||||
assert "(2)" in msg_mock.call_args_list[0][0][0]
|
assert "(2)" in msg_mock.call_args_list[0][0][0]
|
||||||
@@ -651,11 +696,8 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
|
|||||||
await telegram._daily(update=update, context=context)
|
await telegram._daily(update=update, context=context)
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
assert "Daily Profit over the last 7 days</b>:" in msg_mock.call_args_list[0][0][0]
|
assert "Daily Profit over the last 7 days</b>:" in msg_mock.call_args_list[0][0][0]
|
||||||
assert str(datetime.now(timezone.utc).date()) in msg_mock.call_args_list[0][0][0]
|
assert str(dt_now().date()) in msg_mock.call_args_list[0][0][0]
|
||||||
assert (
|
assert str((dt_now() - timedelta(days=5)).date()) in msg_mock.call_args_list[0][0][0]
|
||||||
str((datetime.now(timezone.utc) - timedelta(days=5)).date())
|
|
||||||
in msg_mock.call_args_list[0][0][0]
|
|
||||||
)
|
|
||||||
assert " 6.83 USDT" in msg_mock.call_args_list[0][0][0]
|
assert " 6.83 USDT" in msg_mock.call_args_list[0][0][0]
|
||||||
assert " 7.51 USD" in msg_mock.call_args_list[0][0][0]
|
assert " 7.51 USD" in msg_mock.call_args_list[0][0][0]
|
||||||
assert "(2)" in msg_mock.call_args_list[0][0][0]
|
assert "(2)" in msg_mock.call_args_list[0][0][0]
|
||||||
@@ -725,7 +767,7 @@ async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, tim
|
|||||||
in msg_mock.call_args_list[0][0][0]
|
in msg_mock.call_args_list[0][0][0]
|
||||||
)
|
)
|
||||||
assert "Monday " in msg_mock.call_args_list[0][0][0]
|
assert "Monday " in msg_mock.call_args_list[0][0][0]
|
||||||
today = datetime.now(timezone.utc).date()
|
today = dt_now().date()
|
||||||
first_iso_day_of_current_week = today - timedelta(days=today.weekday())
|
first_iso_day_of_current_week = today - timedelta(days=today.weekday())
|
||||||
assert str(first_iso_day_of_current_week) in msg_mock.call_args_list[0][0][0]
|
assert str(first_iso_day_of_current_week) in msg_mock.call_args_list[0][0][0]
|
||||||
assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
|
assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
|
||||||
@@ -793,7 +835,7 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti
|
|||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
assert "Monthly Profit over the last 2 months</b>:" in msg_mock.call_args_list[0][0][0]
|
assert "Monthly Profit over the last 2 months</b>:" in msg_mock.call_args_list[0][0][0]
|
||||||
assert "Month " in msg_mock.call_args_list[0][0][0]
|
assert "Month " in msg_mock.call_args_list[0][0][0]
|
||||||
today = datetime.now(timezone.utc).date()
|
today = dt_now().date()
|
||||||
current_month = f"{today.year}-{today.month:02} "
|
current_month = f"{today.year}-{today.month:02} "
|
||||||
assert current_month in msg_mock.call_args_list[0][0][0]
|
assert current_month in msg_mock.call_args_list[0][0][0]
|
||||||
assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
|
assert " 2.74 USDT" in msg_mock.call_args_list[0][0][0]
|
||||||
@@ -898,7 +940,7 @@ async def test_telegram_profit_handle(
|
|||||||
trade.orders.append(oobj)
|
trade.orders.append(oobj)
|
||||||
trade.update_trade(oobj)
|
trade.update_trade(oobj)
|
||||||
|
|
||||||
trade.close_date = datetime.now(timezone.utc)
|
trade.close_date = dt_now()
|
||||||
trade.is_open = False
|
trade.is_open = False
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user