Add Post endpoint for to filter dataframe by columns
This commit is contained in:
@@ -489,12 +489,20 @@ class AvailablePairs(BaseModel):
|
|||||||
pair_interval: List[List[str]]
|
pair_interval: List[List[str]]
|
||||||
|
|
||||||
|
|
||||||
|
class PairHistoryRequest(BaseModel):
|
||||||
|
pair: str
|
||||||
|
timeframe: str
|
||||||
|
limit: Optional[int] = None
|
||||||
|
columns: Optional[List[str]] = None
|
||||||
|
|
||||||
|
|
||||||
class PairHistory(BaseModel):
|
class PairHistory(BaseModel):
|
||||||
strategy: str
|
strategy: str
|
||||||
pair: str
|
pair: str
|
||||||
timeframe: str
|
timeframe: str
|
||||||
timeframe_ms: int
|
timeframe_ms: int
|
||||||
columns: List[str]
|
columns: List[str]
|
||||||
|
selected_columns: List[str] = []
|
||||||
data: SerializeAsAny[List[Any]]
|
data: SerializeAsAny[List[Any]]
|
||||||
length: int
|
length: int
|
||||||
buy_signals: int
|
buy_signals: int
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, Blac
|
|||||||
ForceEnterResponse, ForceExitPayload,
|
ForceEnterResponse, ForceExitPayload,
|
||||||
FreqAIModelListResponse, Health, Locks,
|
FreqAIModelListResponse, Health, Locks,
|
||||||
LocksPayload, Logs, MixTag, OpenTradeSchema,
|
LocksPayload, Logs, MixTag, OpenTradeSchema,
|
||||||
PairHistory, PerformanceEntry, Ping, PlotConfig,
|
PairHistory, PairHistoryRequest, PerformanceEntry,
|
||||||
Profit, ResultMsg, ShowConfig, Stats, StatusMsg,
|
Ping, PlotConfig, Profit, ResultMsg, ShowConfig,
|
||||||
StrategyListResponse, StrategyResponse, SysInfo,
|
Stats, StatusMsg, StrategyListResponse,
|
||||||
Version, WhitelistResponse)
|
StrategyResponse, SysInfo, Version,
|
||||||
|
WhitelistResponse)
|
||||||
from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional
|
from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional
|
||||||
from freqtrade.rpc.rpc import RPCException
|
from freqtrade.rpc.rpc import RPCException
|
||||||
|
|
||||||
@@ -53,7 +54,8 @@ logger = logging.getLogger(__name__)
|
|||||||
# 2.32: new /backtest/history/ patch endpoint
|
# 2.32: new /backtest/history/ patch endpoint
|
||||||
# 2.33: Additional weekly/monthly metrics
|
# 2.33: Additional weekly/monthly metrics
|
||||||
# 2.34: new entries/exits/mix_tags endpoints
|
# 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.
|
# Public API, requires no auth.
|
||||||
router_public = APIRouter()
|
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'])
|
@router.get('/pair_candles', response_model=PairHistory, tags=['candle data'])
|
||||||
def pair_candles(
|
def pair_candles(
|
||||||
pair: str, timeframe: str, limit: Optional[int] = None, rpc: RPC = Depends(get_rpc)):
|
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'])
|
@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'),
|
'freqaimodel': freqaimodel if freqaimodel else config.get('freqaimodel'),
|
||||||
})
|
})
|
||||||
try:
|
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:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=502, detail=str(e))
|
raise HTTPException(status_code=502, detail=str(e))
|
||||||
|
|
||||||
|
|||||||
+22
-10
@@ -16,7 +16,7 @@ from sqlalchemy import func, select
|
|||||||
|
|
||||||
from freqtrade import __version__
|
from freqtrade import __version__
|
||||||
from freqtrade.configuration.timerange import TimeRange
|
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.history import load_data
|
||||||
from freqtrade.data.metrics import calculate_expectancy, calculate_max_drawdown
|
from freqtrade.data.metrics import calculate_expectancy, calculate_max_drawdown
|
||||||
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, MarketDirection, SignalDirection,
|
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, MarketDirection, SignalDirection,
|
||||||
@@ -1190,9 +1190,11 @@ class RPC:
|
|||||||
return self._freqtrade.edge.accepted_pairs()
|
return self._freqtrade.edge.accepted_pairs()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _convert_dataframe_to_dict(strategy: str, pair: str, timeframe: str, dataframe: DataFrame,
|
def _convert_dataframe_to_dict(
|
||||||
last_analyzed: datetime) -> Dict[str, Any]:
|
strategy: str, pair: str, timeframe: str, dataframe: DataFrame,
|
||||||
|
last_analyzed: datetime, selected_cols: Optional[List[str]]) -> Dict[str, Any]:
|
||||||
has_content = len(dataframe) != 0
|
has_content = len(dataframe) != 0
|
||||||
|
dataframe_columns = list(dataframe.columns)
|
||||||
signals = {
|
signals = {
|
||||||
'enter_long': 0,
|
'enter_long': 0,
|
||||||
'exit_long': 0,
|
'exit_long': 0,
|
||||||
@@ -1200,6 +1202,11 @@ class RPC:
|
|||||||
'exit_short': 0,
|
'exit_short': 0,
|
||||||
}
|
}
|
||||||
if has_content:
|
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
|
dataframe.loc[:, '__date_ts'] = dataframe.loc[:, 'date'].astype(int64) // 1000 // 1000
|
||||||
# Move signal close to separate column when signal for easy plotting
|
# Move signal close to separate column when signal for easy plotting
|
||||||
@@ -1224,7 +1231,8 @@ class RPC:
|
|||||||
'timeframe': timeframe,
|
'timeframe': timeframe,
|
||||||
'timeframe_ms': timeframe_to_msecs(timeframe),
|
'timeframe_ms': timeframe_to_msecs(timeframe),
|
||||||
'strategy': strategy,
|
'strategy': strategy,
|
||||||
'columns': list(dataframe.columns),
|
'columns': dataframe_columns,
|
||||||
|
'selected_columns': list(dataframe.columns),
|
||||||
'data': dataframe.values.tolist(),
|
'data': dataframe.values.tolist(),
|
||||||
'length': len(dataframe),
|
'length': len(dataframe),
|
||||||
'buy_signals': signals['enter_long'], # Deprecated
|
'buy_signals': signals['enter_long'], # Deprecated
|
||||||
@@ -1249,13 +1257,16 @@ class RPC:
|
|||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _rpc_analysed_dataframe(self, pair: str, timeframe: str,
|
def _rpc_analysed_dataframe(
|
||||||
limit: Optional[int]) -> Dict[str, Any]:
|
self, pair: str, timeframe: str, limit: Optional[int],
|
||||||
|
selected_cols: Optional[List[str]]) -> Dict[str, Any]:
|
||||||
""" Analyzed dataframe in Dict form """
|
""" Analyzed dataframe in Dict form """
|
||||||
|
|
||||||
_data, last_analyzed = self.__rpc_analysed_dataframe_raw(pair, timeframe, limit)
|
_data, last_analyzed = self.__rpc_analysed_dataframe_raw(pair, timeframe, limit)
|
||||||
return RPC._convert_dataframe_to_dict(self._freqtrade.config['strategy'],
|
return RPC._convert_dataframe_to_dict(
|
||||||
pair, timeframe, _data, last_analyzed)
|
self._freqtrade.config['strategy'], pair, timeframe, _data, last_analyzed,
|
||||||
|
selected_cols
|
||||||
|
)
|
||||||
|
|
||||||
def __rpc_analysed_dataframe_raw(
|
def __rpc_analysed_dataframe_raw(
|
||||||
self,
|
self,
|
||||||
@@ -1322,7 +1333,7 @@ class RPC:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _rpc_analysed_history_full(config: Config, pair: str, timeframe: str,
|
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'))
|
timerange_parsed = TimeRange.parse_timerange(config.get('timerange'))
|
||||||
|
|
||||||
from freqtrade.data.converter import trim_dataframe
|
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)
|
df_analyzed = trim_dataframe(df_analyzed, timerange_parsed, startup_candles=startup_candles)
|
||||||
|
|
||||||
return RPC._convert_dataframe_to_dict(strategy.get_strategy_name(), pair, timeframe,
|
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]:
|
def _rpc_plot_config(self) -> Dict[str, Any]:
|
||||||
if (self._freqtrade.strategy.plot_config and
|
if (self._freqtrade.strategy.plot_config and
|
||||||
|
|||||||
Reference in New Issue
Block a user