Merge remote-tracking branch 'origin/strategy_utils' into strategy_utils
# Conflicts: # freqtrade/commands/strategy_utils_commands.py # tests/test_strategy_updater.py
This commit is contained in:
commit
5f8202e1b5
@ -17,8 +17,8 @@ repos:
|
||||
- types-filelock==3.2.7
|
||||
- types-requests==2.28.11.15
|
||||
- types-tabulate==0.9.0.1
|
||||
- types-python-dateutil==2.8.19.9
|
||||
- SQLAlchemy==2.0.4
|
||||
- types-python-dateutil==2.8.19.10
|
||||
- SQLAlchemy==2.0.5.post1
|
||||
# stages: [push]
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
|
@ -1,6 +1,6 @@
|
||||
markdown==3.3.7
|
||||
mkdocs==1.4.2
|
||||
mkdocs-material==9.0.15
|
||||
mkdocs-material==9.1.1
|
||||
mdx_truly_sane_lists==1.3
|
||||
pymdown-extensions==9.9.2
|
||||
pymdown-extensions==9.10
|
||||
jinja2==3.1.2
|
||||
|
@ -2,8 +2,7 @@ from datetime import datetime, timezone
|
||||
from typing import Any, ClassVar, Dict, Optional
|
||||
|
||||
from sqlalchemy import String, or_
|
||||
from sqlalchemy.orm import Mapped, Query, mapped_column
|
||||
from sqlalchemy.orm.scoping import _QueryDescriptorType
|
||||
from sqlalchemy.orm import Mapped, Query, QueryPropertyDescriptor, mapped_column
|
||||
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
||||
from freqtrade.persistence.base import ModelBase, SessionType
|
||||
@ -14,7 +13,7 @@ class PairLock(ModelBase):
|
||||
Pair Locks database model.
|
||||
"""
|
||||
__tablename__ = 'pairlocks'
|
||||
query: ClassVar[_QueryDescriptorType]
|
||||
query: ClassVar[QueryPropertyDescriptor]
|
||||
_session: ClassVar[SessionType]
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
|
@ -8,8 +8,8 @@ from math import isclose
|
||||
from typing import Any, ClassVar, Dict, List, Optional, cast
|
||||
|
||||
from sqlalchemy import Enum, Float, ForeignKey, Integer, String, UniqueConstraint, desc, func
|
||||
from sqlalchemy.orm import Mapped, Query, lazyload, mapped_column, relationship
|
||||
from sqlalchemy.orm.scoping import _QueryDescriptorType
|
||||
from sqlalchemy.orm import (Mapped, Query, QueryPropertyDescriptor, lazyload, mapped_column,
|
||||
relationship)
|
||||
|
||||
from freqtrade.constants import (DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, NON_OPEN_EXCHANGE_STATES,
|
||||
BuySell, LongShort)
|
||||
@ -36,7 +36,7 @@ class Order(ModelBase):
|
||||
Mirrors CCXT Order structure
|
||||
"""
|
||||
__tablename__ = 'orders'
|
||||
query: ClassVar[_QueryDescriptorType]
|
||||
query: ClassVar[QueryPropertyDescriptor]
|
||||
_session: ClassVar[SessionType]
|
||||
|
||||
# Uniqueness should be ensured over pair, order_id
|
||||
@ -1181,7 +1181,7 @@ class Trade(ModelBase, LocalTrade):
|
||||
Note: Fields must be aligned with LocalTrade class
|
||||
"""
|
||||
__tablename__ = 'trades'
|
||||
query: ClassVar[_QueryDescriptorType]
|
||||
query: ClassVar[QueryPropertyDescriptor]
|
||||
_session: ClassVar[SessionType]
|
||||
|
||||
use_db: bool = True
|
||||
|
@ -286,6 +286,7 @@ class OpenTradeSchema(TradeSchema):
|
||||
current_rate: float
|
||||
total_profit_abs: float
|
||||
total_profit_fiat: Optional[float]
|
||||
total_profit_ratio: Optional[float]
|
||||
|
||||
open_order: Optional[str]
|
||||
|
||||
|
@ -42,7 +42,8 @@ logger = logging.getLogger(__name__)
|
||||
# 2.22: Add FreqAI to backtesting
|
||||
# 2.23: Allow plot config request in webserver mode
|
||||
# 2.24: Add cancel_open_order endpoint
|
||||
API_VERSION = 2.24
|
||||
# 2.25: Add several profit values to /status endpoint
|
||||
API_VERSION = 2.25
|
||||
|
||||
# Public API, requires no auth.
|
||||
router_public = APIRouter()
|
||||
|
@ -192,6 +192,11 @@ class RPC:
|
||||
current_profit = trade.close_profit or 0.0
|
||||
current_profit_abs = trade.close_profit_abs or 0.0
|
||||
total_profit_abs = trade.realized_profit + current_profit_abs
|
||||
total_profit_ratio: Optional[float] = None
|
||||
if trade.max_stake_amount:
|
||||
total_profit_ratio = (
|
||||
(total_profit_abs / trade.max_stake_amount) * trade.leverage
|
||||
)
|
||||
|
||||
# Calculate fiat profit
|
||||
if not isnan(current_profit_abs) and self._fiat_converter:
|
||||
@ -224,6 +229,7 @@ class RPC:
|
||||
|
||||
total_profit_abs=total_profit_abs,
|
||||
total_profit_fiat=total_profit_fiat,
|
||||
total_profit_ratio=total_profit_ratio,
|
||||
stoploss_current_dist=stoploss_current_dist,
|
||||
stoploss_current_dist_ratio=round(stoploss_current_dist_ratio, 8),
|
||||
stoploss_current_dist_pct=round(stoploss_current_dist_ratio * 100, 2),
|
||||
|
@ -510,14 +510,14 @@ class Telegram(RPCHandler):
|
||||
if prev_avg_price:
|
||||
minus_on_entry = (cur_entry_average - prev_avg_price) / prev_avg_price
|
||||
|
||||
lines.append(f"*{wording} #{order_nr}:* at {minus_on_entry:.2%} avg profit")
|
||||
lines.append(f"*{wording} #{order_nr}:* at {minus_on_entry:.2%} avg Profit")
|
||||
if is_open:
|
||||
lines.append("({})".format(cur_entry_datetime
|
||||
.humanize(granularity=["day", "hour", "minute"])))
|
||||
lines.append(f"*Amount:* {cur_entry_amount} "
|
||||
f"({round_coin_value(order['cost'], quote_currency)})")
|
||||
lines.append(f"*Average {wording} Price:* {cur_entry_average} "
|
||||
f"({price_to_1st_entry:.2%} from 1st entry rate)")
|
||||
f"({price_to_1st_entry:.2%} from 1st entry Rate)")
|
||||
lines.append(f"*Order filled:* {order['order_filled_date']}")
|
||||
|
||||
# TODO: is this really useful?
|
||||
@ -569,6 +569,8 @@ class Telegram(RPCHandler):
|
||||
and not o['ft_order_side'] == 'stoploss'])
|
||||
r['exit_reason'] = r.get('exit_reason', "")
|
||||
r['stake_amount_r'] = round_coin_value(r['stake_amount'], r['quote_currency'])
|
||||
r['max_stake_amount_r'] = round_coin_value(
|
||||
r['max_stake_amount'] or r['stake_amount'], r['quote_currency'])
|
||||
r['profit_abs_r'] = round_coin_value(r['profit_abs'], r['quote_currency'])
|
||||
r['realized_profit_r'] = round_coin_value(r['realized_profit'], r['quote_currency'])
|
||||
r['total_profit_abs_r'] = round_coin_value(
|
||||
@ -580,31 +582,37 @@ class Telegram(RPCHandler):
|
||||
f"*Direction:* {'`Short`' if r.get('is_short') else '`Long`'}"
|
||||
+ " ` ({leverage}x)`" if r.get('leverage') else "",
|
||||
"*Amount:* `{amount} ({stake_amount_r})`",
|
||||
"*Total invested:* `{max_stake_amount_r}`" if position_adjust else "",
|
||||
"*Enter Tag:* `{enter_tag}`" if r['enter_tag'] else "",
|
||||
"*Exit Reason:* `{exit_reason}`" if r['exit_reason'] else "",
|
||||
]
|
||||
|
||||
if position_adjust:
|
||||
max_buy_str = (f"/{max_entries + 1}" if (max_entries > 0) else "")
|
||||
lines.append("*Number of Entries:* `{num_entries}" + max_buy_str + "`")
|
||||
lines.append("*Number of Exits:* `{num_exits}`")
|
||||
lines.extend([
|
||||
"*Number of Entries:* `{num_entries}" + max_buy_str + "`",
|
||||
"*Number of Exits:* `{num_exits}`"
|
||||
])
|
||||
|
||||
lines.extend([
|
||||
"*Open Rate:* `{open_rate:.8f}`",
|
||||
"*Close Rate:* `{close_rate:.8f}`" if r['close_rate'] else "",
|
||||
"*Open Date:* `{open_date}`",
|
||||
"*Close Date:* `{close_date}`" if r['close_date'] else "",
|
||||
"*Current Rate:* `{current_rate:.8f}`" if r['is_open'] else "",
|
||||
" \n*Current Rate:* `{current_rate:.8f}`" if r['is_open'] else "",
|
||||
("*Unrealized Profit:* " if r['is_open'] else "*Close Profit: *")
|
||||
+ "`{profit_ratio:.2%}` `({profit_abs_r})`",
|
||||
])
|
||||
|
||||
if r['is_open']:
|
||||
if r.get('realized_profit'):
|
||||
lines.append(
|
||||
"*Realized Profit:* `{realized_profit_r} {realized_profit_ratio:.2%}`")
|
||||
lines.append("*Total Profit:* `{total_profit_abs_r}` ")
|
||||
lines.extend([
|
||||
"*Realized Profit:* `{realized_profit_ratio:.2%} ({realized_profit_r})`",
|
||||
"*Total Profit:* `{total_profit_ratio:.2%} ({total_profit_abs_r})`"
|
||||
])
|
||||
|
||||
# Append empty line to improve readability
|
||||
lines.append(" ")
|
||||
if (r['stop_loss_abs'] != r['initial_stop_loss_abs']
|
||||
and r['initial_stop_loss_ratio'] is not None):
|
||||
# Adding initial stoploss only if it is different from stoploss
|
||||
|
@ -7,7 +7,7 @@
|
||||
-r docs/requirements-docs.txt
|
||||
|
||||
coveralls==3.3.1
|
||||
ruff==0.0.253
|
||||
ruff==0.0.254
|
||||
mypy==1.0.1
|
||||
pre-commit==3.1.1
|
||||
pytest==7.2.1
|
||||
@ -29,4 +29,4 @@ types-cachetools==5.3.0.4
|
||||
types-filelock==3.2.7
|
||||
types-requests==2.28.11.15
|
||||
types-tabulate==0.9.0.1
|
||||
types-python-dateutil==2.8.19.9
|
||||
types-python-dateutil==2.8.19.10
|
||||
|
@ -2,10 +2,10 @@ numpy==1.24.2
|
||||
pandas==1.5.3
|
||||
pandas-ta==0.3.14b
|
||||
|
||||
ccxt==2.8.98
|
||||
cryptography==39.0.1
|
||||
ccxt==2.9.4
|
||||
cryptography==39.0.2
|
||||
aiohttp==3.8.4
|
||||
SQLAlchemy==2.0.4
|
||||
SQLAlchemy==2.0.5.post1
|
||||
python-telegram-bot==13.15
|
||||
arrow==1.2.3
|
||||
cachetools==4.2.2
|
||||
@ -28,7 +28,7 @@ py_find_1st==1.1.5
|
||||
# Load ticker files 30% faster
|
||||
python-rapidjson==1.9
|
||||
# Properly format api responses
|
||||
orjson==3.8.6
|
||||
orjson==3.8.7
|
||||
|
||||
# Notify systemd
|
||||
sdnotify==0.3.2
|
||||
@ -45,7 +45,7 @@ psutil==5.9.4
|
||||
colorama==0.4.6
|
||||
# Building config files interactively
|
||||
questionary==1.10.0
|
||||
prompt-toolkit==3.0.37
|
||||
prompt-toolkit==3.0.38
|
||||
# Extensions to datetime library
|
||||
python-dateutil==2.8.2
|
||||
|
||||
|
@ -50,7 +50,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'amount': 91.07468123,
|
||||
'amount_requested': 91.07468124,
|
||||
'stake_amount': 0.001,
|
||||
'max_stake_amount': ANY,
|
||||
'max_stake_amount': None,
|
||||
'trade_duration': None,
|
||||
'trade_duration_s': None,
|
||||
'close_profit': None,
|
||||
@ -79,6 +79,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'realized_profit_ratio': None,
|
||||
'total_profit_abs': -4.09e-06,
|
||||
'total_profit_fiat': ANY,
|
||||
'total_profit_ratio': None,
|
||||
'exchange': 'binance',
|
||||
'leverage': 1.0,
|
||||
'interest_rate': 0.0,
|
||||
@ -168,6 +169,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
results = rpc._rpc_trade_status()
|
||||
|
||||
response = deepcopy(gen_response)
|
||||
response.update({
|
||||
'max_stake_amount': 0.001,
|
||||
'total_profit_ratio': pytest.approx(-0.00409),
|
||||
})
|
||||
assert results[0] == response
|
||||
|
||||
mocker.patch(f'{EXMS}.get_rate',
|
||||
@ -181,10 +186,12 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'stoploss_current_dist': ANY,
|
||||
'stoploss_current_dist_ratio': ANY,
|
||||
'stoploss_current_dist_pct': ANY,
|
||||
'max_stake_amount': 0.001,
|
||||
'profit_ratio': ANY,
|
||||
'profit_pct': ANY,
|
||||
'profit_abs': ANY,
|
||||
'total_profit_abs': ANY,
|
||||
'total_profit_ratio': ANY,
|
||||
'current_rate': ANY,
|
||||
})
|
||||
assert results[0] == response_norate
|
||||
|
@ -1012,6 +1012,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
|
||||
'profit_fiat': ANY,
|
||||
'total_profit_abs': ANY,
|
||||
'total_profit_fiat': ANY,
|
||||
'total_profit_ratio': ANY,
|
||||
'realized_profit': 0.0,
|
||||
'realized_profit_ratio': None,
|
||||
'current_rate': current_rate,
|
||||
|
@ -198,6 +198,7 @@ def test_telegram_status(default_conf, update, mocker) -> None:
|
||||
'current_rate': 1.098e-05,
|
||||
'amount': 90.99181074,
|
||||
'stake_amount': 90.99181074,
|
||||
'max_stake_amount': 90.99181074,
|
||||
'buy_tag': None,
|
||||
'enter_tag': None,
|
||||
'close_profit_ratio': None,
|
||||
|
Loading…
Reference in New Issue
Block a user