Merge pull request #9137 from froggleston/frog-update-rpc

Implment weekly/monthly RPC endpoints
This commit is contained in:
Matthias
2023-09-03 10:22:37 +02:00
committed by GitHub
7 changed files with 82 additions and 11 deletions
+2
View File
@@ -151,6 +151,8 @@ python3 scripts/rest_client.py --config rest_config.json <command> [optional par
| `performance` | Show performance of each finished trade grouped by pair. | `performance` | Show performance of each finished trade grouped by pair.
| `balance` | Show account balance per currency. | `balance` | Show account balance per currency.
| `daily <n>` | Shows profit or loss per day, over the last n days (n defaults to 7). | `daily <n>` | Shows profit or loss per day, over the last n days (n defaults to 7).
| `weekly <n>` | Shows profit or loss per week, over the last n days (n defaults to 4).
| `monthly <n>` | Shows profit or loss per month, over the last n days (n defaults to 3).
| `stats` | Display a summary of profit / loss reasons as well as average holding times. | `stats` | Display a summary of profit / loss reasons as well as average holding times.
| `whitelist` | Show the current whitelist. | `whitelist` | Show the current whitelist.
| `blacklist [pair]` | Show the current blacklist, or adds a pair to the blacklist. | `blacklist [pair]` | Show the current blacklist, or adds a pair to the blacklist.
+3 -3
View File
@@ -157,7 +157,7 @@ class Stats(BaseModel):
durations: Dict[str, Optional[float]] durations: Dict[str, Optional[float]]
class DailyRecord(BaseModel): class DailyWeeklyMonthlyRecord(BaseModel):
date: date date: date
abs_profit: float abs_profit: float
rel_profit: float rel_profit: float
@@ -166,8 +166,8 @@ class DailyRecord(BaseModel):
trade_count: int trade_count: int
class Daily(BaseModel): class DailyWeeklyMonthly(BaseModel):
data: List[DailyRecord] data: List[DailyWeeklyMonthlyRecord]
fiat_display_currency: str fiat_display_currency: str
stake_currency: str stake_currency: str
+16 -3
View File
@@ -11,7 +11,7 @@ from freqtrade.enums import CandleType, TradingMode
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.rpc import RPC from freqtrade.rpc import RPC
from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload, from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload,
BlacklistResponse, Count, Daily, BlacklistResponse, Count, DailyWeeklyMonthly,
DeleteLockRequest, DeleteTrade, DeleteLockRequest, DeleteTrade,
ExchangeListResponse, ForceEnterPayload, ExchangeListResponse, ForceEnterPayload,
ForceEnterResponse, ForceExitPayload, ForceEnterResponse, ForceExitPayload,
@@ -51,7 +51,8 @@ logger = logging.getLogger(__name__)
# 2.30: new /pairlists endpoint # 2.30: new /pairlists endpoint
# 2.31: new /backtest/history/ delete endpoint # 2.31: new /backtest/history/ delete endpoint
# 2.32: new /backtest/history/ patch endpoint # 2.32: new /backtest/history/ patch endpoint
API_VERSION = 2.32 # 2.33: Additional weekly/monthly metrics
API_VERSION = 2.33
# Public API, requires no auth. # Public API, requires no auth.
router_public = APIRouter() router_public = APIRouter()
@@ -99,12 +100,24 @@ def stats(rpc: RPC = Depends(get_rpc)):
return rpc._rpc_stats() return rpc._rpc_stats()
@router.get('/daily', response_model=Daily, tags=['info']) @router.get('/daily', response_model=DailyWeeklyMonthly, tags=['info'])
def daily(timescale: int = 7, rpc: RPC = Depends(get_rpc), config=Depends(get_config)): def daily(timescale: int = 7, rpc: RPC = Depends(get_rpc), config=Depends(get_config)):
return rpc._rpc_timeunit_profit(timescale, config['stake_currency'], return rpc._rpc_timeunit_profit(timescale, config['stake_currency'],
config.get('fiat_display_currency', '')) config.get('fiat_display_currency', ''))
@router.get('/weekly', response_model=DailyWeeklyMonthly, tags=['info'])
def weekly(timescale: int = 4, rpc: RPC = Depends(get_rpc), config=Depends(get_config)):
return rpc._rpc_timeunit_profit(timescale, config['stake_currency'],
config.get('fiat_display_currency', ''), 'weeks')
@router.get('/monthly', response_model=DailyWeeklyMonthly, tags=['info'])
def monthly(timescale: int = 3, rpc: RPC = Depends(get_rpc), config=Depends(get_config)):
return rpc._rpc_timeunit_profit(timescale, config['stake_currency'],
config.get('fiat_display_currency', ''), 'months')
@router.get('/status', response_model=List[OpenTradeSchema], tags=['info']) @router.get('/status', response_model=List[OpenTradeSchema], tags=['info'])
def status(rpc: RPC = Depends(get_rpc)): def status(rpc: RPC = Depends(get_rpc)):
try: try:
+1 -1
View File
@@ -364,7 +364,7 @@ class RPC:
data = [ data = [
{ {
'date': f"{key.year}-{key.month:02d}" if timeunit == 'months' else key, 'date': key,
'abs_profit': value["amount"], 'abs_profit': value["amount"],
'starting_balance': value["daily_stake"], 'starting_balance': value["daily_stake"],
'rel_profit': value["rel_profit"], 'rel_profit': value["rel_profit"],
+5 -4
View File
@@ -51,6 +51,7 @@ class TimeunitMappings:
message2: str message2: str
callback: str callback: str
default: int default: int
dateformat: str
def authorized_only(command_handler: Callable[..., Coroutine[Any, Any, None]]): def authorized_only(command_handler: Callable[..., Coroutine[Any, Any, None]]):
@@ -736,10 +737,10 @@ class Telegram(RPCHandler):
""" """
vals = { vals = {
'days': TimeunitMappings('Day', 'Daily', 'days', 'update_daily', 7), 'days': TimeunitMappings('Day', 'Daily', 'days', 'update_daily', 7, '%Y-%m-%d'),
'weeks': TimeunitMappings('Monday', 'Weekly', 'weeks (starting from Monday)', 'weeks': TimeunitMappings('Monday', 'Weekly', 'weeks (starting from Monday)',
'update_weekly', 8), 'update_weekly', 8, '%Y-%m-%d'),
'months': TimeunitMappings('Month', 'Monthly', 'months', 'update_monthly', 6), 'months': TimeunitMappings('Month', 'Monthly', 'months', 'update_monthly', 6, '%Y-%m'),
} }
val = vals[unit] val = vals[unit]
@@ -756,7 +757,7 @@ class Telegram(RPCHandler):
unit unit
) )
stats_tab = tabulate( stats_tab = tabulate(
[[f"{period['date']} ({period['trade_count']})", [[f"{period['date']:{val.dateformat}} ({period['trade_count']})",
f"{round_coin_value(period['abs_profit'], stats['stake_currency'])}", f"{round_coin_value(period['abs_profit'], stats['stake_currency'])}",
f"{period['fiat_value']:.2f} {stats['fiat_display_currency']}", f"{period['fiat_value']:.2f} {stats['fiat_display_currency']}",
f"{period['rel_profit']:.2%}", f"{period['rel_profit']:.2%}",
+14
View File
@@ -134,6 +134,20 @@ class FtRestClient:
""" """
return self._get("daily", params={"timescale": days} if days else None) return self._get("daily", params={"timescale": days} if days else None)
def weekly(self, weeks=None):
"""Return the profits for each week, and amount of trades.
:return: json object
"""
return self._get("weekly", params={"timescale": weeks} if weeks else None)
def monthly(self, months=None):
"""Return the profits for each month, and amount of trades.
:return: json object
"""
return self._get("monthly", params={"timescale": months} if months else None)
def edge(self): def edge(self):
"""Return information about edge. """Return information about edge.
+41
View File
@@ -617,6 +617,47 @@ def test_api_daily(botclient, mocker, ticker, fee, markets):
assert rc.json()['data'][0]['date'] == str(datetime.now(timezone.utc).date()) assert rc.json()['data'][0]['date'] == str(datetime.now(timezone.utc).date())
def test_api_weekly(botclient, mocker, ticker, fee, markets, time_machine):
ftbot, client = botclient
patch_get_signal(ftbot)
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
markets=PropertyMock(return_value=markets)
)
time_machine.move_to("2023-03-31 21:45:05 +00:00")
rc = client_get(client, f"{BASE_URI}/weekly")
assert_response(rc)
assert len(rc.json()['data']) == 4
assert rc.json()['stake_currency'] == 'BTC'
assert rc.json()['fiat_display_currency'] == 'USD'
# Moved to monday
assert rc.json()['data'][0]['date'] == '2023-03-27'
assert rc.json()['data'][1]['date'] == '2023-03-20'
def test_api_monthly(botclient, mocker, ticker, fee, markets, time_machine):
ftbot, client = botclient
patch_get_signal(ftbot)
mocker.patch.multiple(
EXMS,
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
markets=PropertyMock(return_value=markets)
)
time_machine.move_to("2023-03-31 21:45:05 +00:00")
rc = client_get(client, f"{BASE_URI}/monthly")
assert_response(rc)
assert len(rc.json()['data']) == 3
assert rc.json()['stake_currency'] == 'BTC'
assert rc.json()['fiat_display_currency'] == 'USD'
assert rc.json()['data'][0]['date'] == '2023-03-01'
assert rc.json()['data'][1]['date'] == '2023-02-01'
@pytest.mark.parametrize('is_short', [True, False]) @pytest.mark.parametrize('is_short', [True, False])
def test_api_trades(botclient, mocker, fee, markets, is_short): def test_api_trades(botclient, mocker, fee, markets, is_short):
ftbot, client = botclient ftbot, client = botclient