From 54d467d2bf6876826985a2cf30a5ec53cb6ec8ce Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 28 Apr 2024 10:30:27 +0200 Subject: [PATCH] Add Post endpoint for to filter dataframe by columns --- freqtrade/rpc/api_server/api_schemas.py | 8 +++++++ freqtrade/rpc/api_server/api_v1.py | 23 ++++++++++++------ freqtrade/rpc/rpc.py | 32 +++++++++++++++++-------- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 97f851b1d..cd60b3d92 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -489,12 +489,20 @@ class AvailablePairs(BaseModel): pair_interval: List[List[str]] +class PairHistoryRequest(BaseModel): + pair: str + timeframe: str + limit: Optional[int] = None + columns: Optional[List[str]] = None + + class PairHistory(BaseModel): strategy: str pair: str timeframe: str timeframe_ms: int columns: List[str] + selected_columns: List[str] = [] data: SerializeAsAny[List[Any]] length: int buy_signals: int diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 8146fe276..c180012e2 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -17,10 +17,11 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, Blac ForceEnterResponse, ForceExitPayload, FreqAIModelListResponse, Health, Locks, LocksPayload, Logs, MixTag, OpenTradeSchema, - PairHistory, PerformanceEntry, Ping, PlotConfig, - Profit, ResultMsg, ShowConfig, Stats, StatusMsg, - StrategyListResponse, StrategyResponse, SysInfo, - Version, WhitelistResponse) + PairHistory, PairHistoryRequest, 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 from freqtrade.rpc.rpc import RPCException @@ -53,7 +54,8 @@ logger = logging.getLogger(__name__) # 2.32: new /backtest/history/ patch endpoint # 2.33: Additional weekly/monthly metrics # 2.34: new entries/exits/mix_tags endpoints -API_VERSION = 2.34 +# 2.35: pair_candles and pair_history endpoints as Post variant +API_VERSION = 2.35 # Public API, requires no auth. router_public = APIRouter() @@ -291,7 +293,14 @@ def reload_config(rpc: RPC = Depends(get_rpc)): @router.get('/pair_candles', response_model=PairHistory, tags=['candle data']) def pair_candles( pair: str, timeframe: str, limit: Optional[int] = None, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_analysed_dataframe(pair, timeframe, limit) + return rpc._rpc_analysed_dataframe(pair, timeframe, limit, None) + + +@router.post('/pair_candles', response_model=PairHistory, tags=['candle data']) +def pair_candles_filtered(payload: PairHistoryRequest, rpc: RPC = Depends(get_rpc)): + # Advanced pair_candles endpoint with column filtering + return rpc._rpc_analysed_dataframe( + payload.pair, payload.timeframe, payload.limit, payload.columns) @router.get('/pair_history', response_model=PairHistory, tags=['candle data']) @@ -307,7 +316,7 @@ def pair_history(pair: str, timeframe: str, timerange: str, strategy: str, 'freqaimodel': freqaimodel if freqaimodel else config.get('freqaimodel'), }) try: - return RPC._rpc_analysed_history_full(config, pair, timeframe, exchange) + return RPC._rpc_analysed_history_full(config, pair, timeframe, exchange, None) except Exception as e: raise HTTPException(status_code=502, detail=str(e)) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 43be0fd94..7ab9f9413 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -16,7 +16,7 @@ from sqlalchemy import func, select from freqtrade import __version__ from freqtrade.configuration.timerange import TimeRange -from freqtrade.constants import CANCEL_REASON, Config +from freqtrade.constants import CANCEL_REASON, DEFAULT_DATAFRAME_COLUMNS, Config from freqtrade.data.history import load_data from freqtrade.data.metrics import calculate_expectancy, calculate_max_drawdown from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, MarketDirection, SignalDirection, @@ -1190,9 +1190,11 @@ class RPC: return self._freqtrade.edge.accepted_pairs() @staticmethod - def _convert_dataframe_to_dict(strategy: str, pair: str, timeframe: str, dataframe: DataFrame, - last_analyzed: datetime) -> Dict[str, Any]: + def _convert_dataframe_to_dict( + strategy: str, pair: str, timeframe: str, dataframe: DataFrame, + last_analyzed: datetime, selected_cols: Optional[List[str]]) -> Dict[str, Any]: has_content = len(dataframe) != 0 + dataframe_columns = list(dataframe.columns) signals = { 'enter_long': 0, 'exit_long': 0, @@ -1200,6 +1202,11 @@ class RPC: 'exit_short': 0, } if has_content: + if selected_cols: + # Ensure OHLCV columns are always present + cols_set = set(DEFAULT_DATAFRAME_COLUMNS + selected_cols) + df_cols = [col for col in dataframe_columns if col in cols_set] + dataframe = dataframe.loc[:, df_cols] dataframe.loc[:, '__date_ts'] = dataframe.loc[:, 'date'].astype(int64) // 1000 // 1000 # Move signal close to separate column when signal for easy plotting @@ -1224,7 +1231,8 @@ class RPC: 'timeframe': timeframe, 'timeframe_ms': timeframe_to_msecs(timeframe), 'strategy': strategy, - 'columns': list(dataframe.columns), + 'columns': dataframe_columns, + 'selected_columns': list(dataframe.columns), 'data': dataframe.values.tolist(), 'length': len(dataframe), 'buy_signals': signals['enter_long'], # Deprecated @@ -1249,13 +1257,16 @@ class RPC: }) return res - def _rpc_analysed_dataframe(self, pair: str, timeframe: str, - limit: Optional[int]) -> Dict[str, Any]: + def _rpc_analysed_dataframe( + self, pair: str, timeframe: str, limit: Optional[int], + selected_cols: Optional[List[str]]) -> Dict[str, Any]: """ Analyzed dataframe in Dict form """ _data, last_analyzed = self.__rpc_analysed_dataframe_raw(pair, timeframe, limit) - return RPC._convert_dataframe_to_dict(self._freqtrade.config['strategy'], - pair, timeframe, _data, last_analyzed) + return RPC._convert_dataframe_to_dict( + self._freqtrade.config['strategy'], pair, timeframe, _data, last_analyzed, + selected_cols + ) def __rpc_analysed_dataframe_raw( self, @@ -1322,7 +1333,7 @@ class RPC: @staticmethod def _rpc_analysed_history_full(config: Config, pair: str, timeframe: str, - exchange) -> Dict[str, Any]: + exchange, selected_cols: Optional[List[str]]) -> Dict[str, Any]: timerange_parsed = TimeRange.parse_timerange(config.get('timerange')) from freqtrade.data.converter import trim_dataframe @@ -1352,7 +1363,8 @@ class RPC: df_analyzed = trim_dataframe(df_analyzed, timerange_parsed, startup_candles=startup_candles) return RPC._convert_dataframe_to_dict(strategy.get_strategy_name(), pair, timeframe, - df_analyzed.copy(), dt_now()) + df_analyzed.copy(), dt_now(), + selected_cols) def _rpc_plot_config(self) -> Dict[str, Any]: if (self._freqtrade.strategy.plot_config and