mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 02:12:01 +00:00
Merge pull request #10023 from freqtrade/feat/lock_api
Add lock post endpoint
This commit is contained in:
commit
fcfd25d50b
|
@ -166,6 +166,7 @@ freqtrade-client --config rest_config.json <command> [optional parameters]
|
|||
| `mix_tags [pair]` | Shows profit statistics for each combinations of enter tag + exit reasons for given pair (or all pairs if pair isn't given). Pair is optional.
|
||||
| `locks` | Displays currently locked pairs.
|
||||
| `delete_lock <lock_id>` | Deletes (disables) the lock by id.
|
||||
| `locks add <pair>, <until>, [side], [reason]` | Locks a pair until "until". (Until will be rounded up to the nearest timeframe).
|
||||
| `profit` | Display a summary of your profit/loss from close trades and some stats about your performance.
|
||||
| `forceexit <trade_id>` | Instantly exits the given trade (Ignoring `minimum_roi`).
|
||||
| `forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`).
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import date, datetime
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from pydantic import BaseModel, RootModel, SerializeAsAny
|
||||
from pydantic import AwareDatetime, BaseModel, RootModel, SerializeAsAny
|
||||
|
||||
from freqtrade.constants import IntOrInf
|
||||
from freqtrade.enums import MarginMode, OrderTypeValues, SignalDirection, TradingMode
|
||||
|
@ -378,6 +378,13 @@ class Locks(BaseModel):
|
|||
locks: List[LockModel]
|
||||
|
||||
|
||||
class LocksPayload(BaseModel):
|
||||
pair: str
|
||||
side: str = '*' # Default to both sides
|
||||
until: AwareDatetime
|
||||
reason: Optional[str] = None
|
||||
|
||||
|
||||
class DeleteLockRequest(BaseModel):
|
||||
pair: Optional[str] = None
|
||||
lockid: Optional[int] = None
|
||||
|
|
|
@ -15,10 +15,10 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, Blac
|
|||
DeleteLockRequest, DeleteTrade, Entry,
|
||||
ExchangeListResponse, Exit, ForceEnterPayload,
|
||||
ForceEnterResponse, ForceExitPayload,
|
||||
FreqAIModelListResponse, Health, Locks, Logs,
|
||||
MixTag, OpenTradeSchema, PairHistory,
|
||||
PerformanceEntry, Ping, PlotConfig, Profit,
|
||||
ResultMsg, ShowConfig, Stats, StatusMsg,
|
||||
FreqAIModelListResponse, Health, Locks,
|
||||
LocksPayload, Logs, MixTag, OpenTradeSchema,
|
||||
PairHistory, PerformanceEntry, Ping, PlotConfig,
|
||||
Profit, ResultMsg, ShowConfig, Stats, StatusMsg,
|
||||
StrategyListResponse, StrategyResponse, SysInfo,
|
||||
Version, WhitelistResponse)
|
||||
from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional
|
||||
|
@ -255,6 +255,13 @@ def delete_lock_pair(payload: DeleteLockRequest, rpc: RPC = Depends(get_rpc)):
|
|||
return rpc._rpc_delete_lock(lockid=payload.lockid, pair=payload.pair)
|
||||
|
||||
|
||||
@router.post('/locks', response_model=Locks, tags=['info', 'locks'])
|
||||
def add_locks(payload: List[LocksPayload], rpc: RPC = Depends(get_rpc)):
|
||||
for lock in payload:
|
||||
rpc._rpc_add_lock(lock.pair, lock.until, lock.reason, lock.side)
|
||||
return rpc._rpc_locks()
|
||||
|
||||
|
||||
@router.get('/logs', response_model=Logs, tags=['info'])
|
||||
def logs(limit: Optional[int] = None):
|
||||
return RPC._rpc_get_logs(limit)
|
||||
|
|
|
@ -1104,6 +1104,16 @@ class RPC:
|
|||
|
||||
return self._rpc_locks()
|
||||
|
||||
def _rpc_add_lock(
|
||||
self, pair: str, until: datetime, reason: Optional[str], side: str) -> PairLock:
|
||||
lock = PairLocks.lock_pair(
|
||||
pair=pair,
|
||||
until=until,
|
||||
reason=reason,
|
||||
side=side,
|
||||
)
|
||||
return lock
|
||||
|
||||
def _rpc_whitelist(self) -> Dict:
|
||||
""" Returns the currently active whitelist"""
|
||||
res = {'method': self._freqtrade.pairlists.name_list,
|
||||
|
|
|
@ -7,7 +7,7 @@ so it can be used as a standalone script, and can be installed independently.
|
|||
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from urllib.parse import urlencode, urlparse, urlunparse
|
||||
|
||||
import requests
|
||||
|
@ -16,6 +16,9 @@ from requests.exceptions import ConnectionError
|
|||
|
||||
logger = logging.getLogger("ft_rest_client")
|
||||
|
||||
ParamsT = Optional[Dict[str, Any]]
|
||||
PostDataT = Optional[Union[Dict[str, Any], List[Dict[str, Any]]]]
|
||||
|
||||
|
||||
class FtRestClient:
|
||||
|
||||
|
@ -58,13 +61,13 @@ class FtRestClient:
|
|||
except ConnectionError:
|
||||
logger.warning("Connection error")
|
||||
|
||||
def _get(self, apipath, params: Optional[dict] = None):
|
||||
def _get(self, apipath, params: ParamsT = None):
|
||||
return self._call("GET", apipath, params=params)
|
||||
|
||||
def _delete(self, apipath, params: Optional[dict] = None):
|
||||
def _delete(self, apipath, params: ParamsT = None):
|
||||
return self._call("DELETE", apipath, params=params)
|
||||
|
||||
def _post(self, apipath, params: Optional[dict] = None, data: Optional[dict] = None):
|
||||
def _post(self, apipath, params: ParamsT = None, data: PostDataT = None):
|
||||
return self._call("POST", apipath, params=params, data=data)
|
||||
|
||||
def start(self):
|
||||
|
@ -148,6 +151,25 @@ class FtRestClient:
|
|||
"""
|
||||
return self._delete(f"locks/{lock_id}")
|
||||
|
||||
def lock_add(self, pair: str, until: str, side: str = '*', reason: str = ''):
|
||||
"""Lock pair
|
||||
|
||||
:param pair: Pair to lock
|
||||
:param until: Lock until this date (format "2024-03-30 16:00:00Z")
|
||||
:param side: Side to lock (long, short, *)
|
||||
:param reason: Reason for the lock
|
||||
:return: json object
|
||||
"""
|
||||
data = [
|
||||
{
|
||||
"pair": pair,
|
||||
"until": until,
|
||||
"side": side,
|
||||
"reason": reason
|
||||
}
|
||||
]
|
||||
return self._post("locks", data=data)
|
||||
|
||||
def daily(self, days=None):
|
||||
"""Return the profits for each day, and amount of trades.
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ def test_FtRestClient_call_invalid(caplog):
|
|||
('exits', []),
|
||||
('mix_tags', []),
|
||||
('locks', []),
|
||||
('lock_add', ["XRP/USDT", '2024-01-01 20:00:00Z', '*', 'rand']),
|
||||
('delete_lock', [2]),
|
||||
('daily', []),
|
||||
('daily', [15]),
|
||||
|
|
|
@ -88,7 +88,8 @@ ignore_missing_imports = true
|
|||
namespace_packages = false
|
||||
warn_unused_ignores = true
|
||||
exclude = [
|
||||
'^build_helpers\.py$'
|
||||
'^build_helpers\.py$',
|
||||
'^ft_client/build/.*$',
|
||||
]
|
||||
plugins = [
|
||||
"sqlalchemy.ext.mypy.plugin"
|
||||
|
|
|
@ -11,7 +11,6 @@ from freqtrade.enums import SignalDirection, State, TradingMode
|
|||
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
|
||||
from freqtrade.persistence import Order, Trade
|
||||
from freqtrade.persistence.key_value_store import set_startup_time
|
||||
from freqtrade.persistence.pairlock_middleware import PairLocks
|
||||
from freqtrade.rpc import RPC, RPCException
|
||||
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
||||
from tests.conftest import (EXMS, create_mock_trades, create_mock_trades_usdt,
|
||||
|
@ -1171,14 +1170,15 @@ def test_rpc_force_entry_wrong_mode(mocker, default_conf) -> None:
|
|||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_rpc_delete_lock(mocker, default_conf):
|
||||
def test_rpc_add_and_delete_lock(mocker, default_conf):
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
rpc = RPC(freqtradebot)
|
||||
pair = 'ETH/BTC'
|
||||
|
||||
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=4))
|
||||
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=5))
|
||||
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=10))
|
||||
rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=4), '', '*')
|
||||
rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=5), '', '*')
|
||||
rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=10), '', '*')
|
||||
|
||||
locks = rpc._rpc_locks()
|
||||
assert locks['lock_count'] == 3
|
||||
locks1 = rpc._rpc_delete_lock(lockid=locks['locks'][0]['id'])
|
||||
|
|
|
@ -23,12 +23,13 @@ from freqtrade.enums import CandleType, RunMode, State, TradingMode
|
|||
from freqtrade.exceptions import DependencyException, ExchangeError, OperationalException
|
||||
from freqtrade.loggers import setup_logging, setup_logging_pre
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
from freqtrade.persistence import PairLocks, Trade
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.rpc import RPC
|
||||
from freqtrade.rpc.api_server import ApiServer
|
||||
from freqtrade.rpc.api_server.api_auth import create_token, get_user_from_token
|
||||
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
|
||||
from freqtrade.rpc.api_server.webserver_bgwork import ApiBG
|
||||
from freqtrade.util.datetime_helpers import format_date
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, EXMS, create_mock_trades, get_mock_coro,
|
||||
get_patched_freqtradebot, log_has, log_has_re, patch_get_signal)
|
||||
|
||||
|
@ -553,8 +554,19 @@ def test_api_locks(botclient):
|
|||
assert rc.json()['lock_count'] == 0
|
||||
assert rc.json()['lock_count'] == len(rc.json()['locks'])
|
||||
|
||||
PairLocks.lock_pair('ETH/BTC', datetime.now(timezone.utc) + timedelta(minutes=4), 'randreason')
|
||||
PairLocks.lock_pair('XRP/BTC', datetime.now(timezone.utc) + timedelta(minutes=20), 'deadbeef')
|
||||
rc = client_post(client, f"{BASE_URI}/locks", [
|
||||
{
|
||||
"pair": "ETH/BTC",
|
||||
"until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=4))}Z",
|
||||
"reason": "randreason"
|
||||
}, {
|
||||
"pair": "XRP/BTC",
|
||||
"until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=20))}Z",
|
||||
"reason": "deadbeef"
|
||||
}
|
||||
])
|
||||
assert_response(rc)
|
||||
assert rc.json()['lock_count'] == 2
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/locks")
|
||||
assert_response(rc)
|
||||
|
|
Loading…
Reference in New Issue
Block a user