Merge pull request #9011 from freqtrade/dependabot/pip/develop/pydantic-2.1.1

Bump pydantic from 1.10.11 to 2.1.1
This commit is contained in:
Matthias 2023-08-18 11:50:24 +02:00 committed by GitHub
commit 5a9dd79b45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 147 deletions

View File

@ -1,7 +1,7 @@
from datetime import date, datetime
from typing import Any, Dict, List, Optional, Union
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict, RootModel, SerializeAsAny
from freqtrade.constants import DATETIME_PRINT_FORMAT, IntOrInf
from freqtrade.enums import MarginMode, OrderTypeValues, SignalDirection, TradingMode
@ -9,9 +9,9 @@ from freqtrade.types import ValidExchangesType
class ExchangeModePayloadMixin(BaseModel):
trading_mode: Optional[TradingMode]
margin_mode: Optional[MarginMode]
exchange: Optional[str]
trading_mode: Optional[TradingMode] = None
margin_mode: Optional[MarginMode] = None
exchange: Optional[str] = None
class Ping(BaseModel):
@ -43,11 +43,11 @@ class BackgroundTaskStatus(BaseModel):
job_category: str
status: str
running: bool
progress: Optional[float]
progress: Optional[float] = None
class BackgroundTaskResult(BaseModel):
error: Optional[str]
error: Optional[str] = None
status: str
@ -60,9 +60,9 @@ class Balance(BaseModel):
free: float
balance: float
used: float
bot_owned: Optional[float]
bot_owned: Optional[float] = None
est_stake: float
est_stake_bot: Optional[float]
est_stake_bot: Optional[float] = None
stake: str
# Starting with 2.x
side: str
@ -141,7 +141,7 @@ class Profit(BaseModel):
expectancy_ratio: float
max_drawdown: float
max_drawdown_abs: float
trading_volume: Optional[float]
trading_volume: Optional[float] = None
bot_start_timestamp: int
bot_start_date: str
@ -173,50 +173,50 @@ class Daily(BaseModel):
class UnfilledTimeout(BaseModel):
entry: Optional[int]
exit: Optional[int]
unit: Optional[str]
exit_timeout_count: Optional[int]
entry: Optional[int] = None
exit: Optional[int] = None
unit: Optional[str] = None
exit_timeout_count: Optional[int] = None
class OrderTypes(BaseModel):
entry: OrderTypeValues
exit: OrderTypeValues
emergency_exit: Optional[OrderTypeValues]
force_exit: Optional[OrderTypeValues]
force_entry: Optional[OrderTypeValues]
emergency_exit: Optional[OrderTypeValues] = None
force_exit: Optional[OrderTypeValues] = None
force_entry: Optional[OrderTypeValues] = None
stoploss: OrderTypeValues
stoploss_on_exchange: bool
stoploss_on_exchange_interval: Optional[int]
stoploss_on_exchange_interval: Optional[int] = None
class ShowConfig(BaseModel):
version: str
strategy_version: Optional[str]
strategy_version: Optional[str] = None
api_version: float
dry_run: bool
trading_mode: str
short_allowed: bool
stake_currency: str
stake_amount: str
available_capital: Optional[float]
available_capital: Optional[float] = None
stake_currency_decimals: int
max_open_trades: IntOrInf
minimal_roi: Dict[str, Any]
stoploss: Optional[float]
stoploss: Optional[float] = None
stoploss_on_exchange: bool
trailing_stop: Optional[bool]
trailing_stop_positive: Optional[float]
trailing_stop_positive_offset: Optional[float]
trailing_only_offset_is_reached: Optional[bool]
unfilledtimeout: Optional[UnfilledTimeout] # Empty in webserver mode
order_types: Optional[OrderTypes]
use_custom_stoploss: Optional[bool]
timeframe: Optional[str]
trailing_stop: Optional[bool] = None
trailing_stop_positive: Optional[float] = None
trailing_stop_positive_offset: Optional[float] = None
trailing_only_offset_is_reached: Optional[bool] = None
unfilledtimeout: Optional[UnfilledTimeout] = None # Empty in webserver mode
order_types: Optional[OrderTypes] = None
use_custom_stoploss: Optional[bool] = None
timeframe: Optional[str] = None
timeframe_ms: int
timeframe_min: int
exchange: str
strategy: Optional[str]
strategy: Optional[str] = None
force_entry_enable: bool
exit_pricing: Dict[str, Any]
entry_pricing: Dict[str, Any]
@ -231,17 +231,17 @@ class OrderSchema(BaseModel):
pair: str
order_id: str
status: str
remaining: Optional[float]
remaining: Optional[float] = None
amount: float
safe_price: float
cost: float
filled: Optional[float]
filled: Optional[float] = None
ft_order_side: str
order_type: str
is_open: bool
order_timestamp: Optional[int]
order_filled_timestamp: Optional[int]
ft_fee_base: Optional[float]
order_timestamp: Optional[int] = None
order_filled_timestamp: Optional[int] = None
ft_fee_base: Optional[float] = None
class TradeSchema(BaseModel):
@ -255,81 +255,81 @@ class TradeSchema(BaseModel):
amount: float
amount_requested: float
stake_amount: float
max_stake_amount: Optional[float]
max_stake_amount: Optional[float] = None
strategy: str
enter_tag: Optional[str]
enter_tag: Optional[str] = None
timeframe: int
fee_open: Optional[float]
fee_open_cost: Optional[float]
fee_open_currency: Optional[str]
fee_close: Optional[float]
fee_close_cost: Optional[float]
fee_close_currency: Optional[str]
fee_open: Optional[float] = None
fee_open_cost: Optional[float] = None
fee_open_currency: Optional[str] = None
fee_close: Optional[float] = None
fee_close_cost: Optional[float] = None
fee_close_currency: Optional[str] = None
open_date: str
open_timestamp: int
open_rate: float
open_rate_requested: Optional[float]
open_rate_requested: Optional[float] = None
open_trade_value: float
close_date: Optional[str]
close_timestamp: Optional[int]
close_rate: Optional[float]
close_rate_requested: Optional[float]
close_date: Optional[str] = None
close_timestamp: Optional[int] = None
close_rate: Optional[float] = None
close_rate_requested: Optional[float] = None
close_profit: Optional[float]
close_profit_pct: Optional[float]
close_profit_abs: Optional[float]
close_profit: Optional[float] = None
close_profit_pct: Optional[float] = None
close_profit_abs: Optional[float] = None
profit_ratio: Optional[float]
profit_pct: Optional[float]
profit_abs: Optional[float]
profit_fiat: Optional[float]
profit_ratio: Optional[float] = None
profit_pct: Optional[float] = None
profit_abs: Optional[float] = None
profit_fiat: Optional[float] = None
realized_profit: float
realized_profit_ratio: Optional[float]
realized_profit_ratio: Optional[float] = None
exit_reason: Optional[str]
exit_order_status: Optional[str]
exit_reason: Optional[str] = None
exit_order_status: Optional[str] = None
stop_loss_abs: Optional[float]
stop_loss_ratio: Optional[float]
stop_loss_pct: Optional[float]
stoploss_order_id: Optional[str]
stoploss_last_update: Optional[str]
stoploss_last_update_timestamp: Optional[int]
initial_stop_loss_abs: Optional[float]
initial_stop_loss_ratio: Optional[float]
initial_stop_loss_pct: Optional[float]
stop_loss_abs: Optional[float] = None
stop_loss_ratio: Optional[float] = None
stop_loss_pct: Optional[float] = None
stoploss_order_id: Optional[str] = None
stoploss_last_update: Optional[str] = None
stoploss_last_update_timestamp: Optional[int] = None
initial_stop_loss_abs: Optional[float] = None
initial_stop_loss_ratio: Optional[float] = None
initial_stop_loss_pct: Optional[float] = None
min_rate: Optional[float]
max_rate: Optional[float]
open_order_id: Optional[str]
min_rate: Optional[float] = None
max_rate: Optional[float] = None
open_order_id: Optional[str] = None
orders: List[OrderSchema]
leverage: Optional[float]
interest_rate: Optional[float]
liquidation_price: Optional[float]
funding_fees: Optional[float]
trading_mode: Optional[TradingMode]
leverage: Optional[float] = None
interest_rate: Optional[float] = None
liquidation_price: Optional[float] = None
funding_fees: Optional[float] = None
trading_mode: Optional[TradingMode] = None
amount_precision: Optional[float]
price_precision: Optional[float]
precision_mode: Optional[int]
amount_precision: Optional[float] = None
price_precision: Optional[float] = None
precision_mode: Optional[int] = None
class OpenTradeSchema(TradeSchema):
stoploss_current_dist: Optional[float]
stoploss_current_dist_pct: Optional[float]
stoploss_current_dist_ratio: Optional[float]
stoploss_entry_dist: Optional[float]
stoploss_entry_dist_ratio: Optional[float]
stoploss_current_dist: Optional[float] = None
stoploss_current_dist_pct: Optional[float] = None
stoploss_current_dist_ratio: Optional[float] = None
stoploss_entry_dist: Optional[float] = None
stoploss_entry_dist_ratio: Optional[float] = None
current_rate: float
total_profit_abs: float
total_profit_fiat: Optional[float]
total_profit_ratio: Optional[float]
total_profit_fiat: Optional[float] = None
total_profit_ratio: Optional[float] = None
open_order: Optional[str]
open_order: Optional[str] = None
class TradeResponse(BaseModel):
@ -339,8 +339,7 @@ class TradeResponse(BaseModel):
total_trades: int
class ForceEnterResponse(BaseModel):
__root__: Union[TradeSchema, StatusMsg]
ForceEnterResponse = RootModel[Union[TradeSchema, StatusMsg]]
class LockModel(BaseModel):
@ -352,7 +351,7 @@ class LockModel(BaseModel):
lock_timestamp: int
pair: str
side: str
reason: Optional[str]
reason: Optional[str] = None
class Locks(BaseModel):
@ -361,8 +360,8 @@ class Locks(BaseModel):
class DeleteLockRequest(BaseModel):
pair: Optional[str]
lockid: Optional[int]
pair: Optional[str] = None
lockid: Optional[int] = None
class Logs(BaseModel):
@ -373,17 +372,17 @@ class Logs(BaseModel):
class ForceEnterPayload(BaseModel):
pair: str
side: SignalDirection = SignalDirection.LONG
price: Optional[float]
ordertype: Optional[OrderTypeValues]
stakeamount: Optional[float]
entry_tag: Optional[str]
leverage: Optional[float]
price: Optional[float] = None
ordertype: Optional[OrderTypeValues] = None
stakeamount: Optional[float] = None
entry_tag: Optional[str] = None
leverage: Optional[float] = None
class ForceExitPayload(BaseModel):
tradeid: str
ordertype: Optional[OrderTypeValues]
amount: Optional[float]
ordertype: Optional[OrderTypeValues] = None
amount: Optional[float] = None
class BlacklistPayload(BaseModel):
@ -405,7 +404,7 @@ class WhitelistResponse(BaseModel):
class WhitelistEvaluateResponse(BackgroundTaskResult):
result: Optional[WhitelistResponse]
result: Optional[WhitelistResponse] = None
class DeleteTrade(BaseModel):
@ -420,8 +419,7 @@ class PlotConfig_(BaseModel):
subplots: Dict[str, Any]
class PlotConfig(BaseModel):
__root__: Union[PlotConfig_, Dict]
PlotConfig = RootModel[Union[PlotConfig_, Dict]]
class StrategyListResponse(BaseModel):
@ -470,7 +468,7 @@ class PairHistory(BaseModel):
timeframe: str
timeframe_ms: int
columns: List[str]
data: List[Any]
data: SerializeAsAny[List[Any]]
length: int
buy_signals: int
sell_signals: int
@ -484,11 +482,11 @@ class PairHistory(BaseModel):
data_start: str
data_stop: str
data_stop_ts: int
class Config:
json_encoders = {
datetime: lambda v: v.strftime(DATETIME_PRINT_FORMAT),
}
# TODO[pydantic]: The following keys were removed: `json_encoders`.
# Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information.
model_config = ConfigDict(json_encoders={
datetime: lambda v: v.strftime(DATETIME_PRINT_FORMAT),
})
class BacktestFreqAIInputs(BaseModel):
@ -497,16 +495,16 @@ class BacktestFreqAIInputs(BaseModel):
class BacktestRequest(BaseModel):
strategy: str
timeframe: Optional[str]
timeframe_detail: Optional[str]
timerange: Optional[str]
max_open_trades: Optional[IntOrInf]
stake_amount: Optional[str]
timeframe: Optional[str] = None
timeframe_detail: Optional[str] = None
timerange: Optional[str] = None
max_open_trades: Optional[IntOrInf] = None
stake_amount: Optional[Union[str, float]] = None
enable_protections: bool
dry_run_wallet: Optional[float]
backtest_cache: Optional[str]
freqaimodel: Optional[str]
freqai: Optional[BacktestFreqAIInputs]
dry_run_wallet: Optional[float] = None
backtest_cache: Optional[str] = None
freqaimodel: Optional[str] = None
freqai: Optional[BacktestFreqAIInputs] = None
class BacktestResponse(BaseModel):
@ -515,9 +513,9 @@ class BacktestResponse(BaseModel):
status_msg: str
step: str
progress: float
trade_count: Optional[float]
trade_count: Optional[float] = None
# TODO: Properly type backtestresult...
backtest_result: Optional[Dict[str, Any]]
backtest_result: Optional[Dict[str, Any]] = None
# TODO: This is a copy of BacktestHistoryEntryType
@ -540,5 +538,5 @@ class SysInfo(BaseModel):
class Health(BaseModel):
last_process: Optional[datetime]
last_process_ts: Optional[int]
last_process: Optional[datetime] = None
last_process_ts: Optional[int] = None

View File

@ -175,9 +175,9 @@ def force_entry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)):
leverage=payload.leverage)
if trade:
return ForceEnterResponse.parse_obj(trade.to_json())
return ForceEnterResponse.model_validate(trade.to_json())
else:
return ForceEnterResponse.parse_obj(
return ForceEnterResponse.model_validate(
{"status": f"Error entering {payload.side} trade for pair {payload.pair}."})
@ -282,14 +282,14 @@ def plot_config(strategy: Optional[str] = None, config=Depends(get_config),
if not strategy:
if not rpc:
raise RPCException("Strategy is mandatory in webserver mode.")
return PlotConfig.parse_obj(rpc._rpc_plot_config())
return PlotConfig.model_validate(rpc._rpc_plot_config())
else:
config1 = deepcopy(config)
config1.update({
'strategy': strategy
})
try:
return PlotConfig.parse_obj(RPC._rpc_plot_config_with_strategy(config1))
return PlotConfig.model_validate(RPC._rpc_plot_config_with_strategy(config1))
except Exception as e:
raise HTTPException(status_code=502, detail=str(e))

View File

@ -65,7 +65,7 @@ async def _process_consumer_request(
"""
# Validate the request, makes sure it matches the schema
try:
websocket_request = WSRequestSchema.parse_obj(request)
websocket_request = WSRequestSchema.model_validate(request)
except ValidationError as e:
logger.error(f"Invalid request from {channel}: {e}")
return
@ -94,7 +94,7 @@ async def _process_consumer_request(
# Format response
response = WSWhitelistMessage(data=whitelist)
await channel.send(response.dict(exclude_none=True))
await channel.send(response.model_dump(exclude_none=True))
elif type_ == RPCRequestType.ANALYZED_DF:
# Limit the amount of candles per dataframe to 'limit' or 1500
@ -105,7 +105,7 @@ async def _process_consumer_request(
for message in rpc._ws_request_analyzed_df(limit, pair):
# Format response
response = WSAnalyzedDFMessage(data=message)
await channel.send(response.dict(exclude_none=True))
await channel.send(response.model_dump(exclude_none=True))
@router.websocket("/message/ws")

View File

@ -2,15 +2,14 @@ from datetime import datetime
from typing import Any, Dict, List, Optional, TypedDict
from pandas import DataFrame
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
from freqtrade.constants import PairWithTimeframe
from freqtrade.enums.rpcmessagetype import RPCMessageType, RPCRequestType
class BaseArbitraryModel(BaseModel):
class Config:
arbitrary_types_allowed = True
model_config = ConfigDict(arbitrary_types_allowed=True)
class WSRequestSchema(BaseArbitraryModel):
@ -27,9 +26,7 @@ class WSMessageSchemaType(TypedDict):
class WSMessageSchema(BaseArbitraryModel):
type: RPCMessageType
data: Optional[Any] = None
class Config:
extra = 'allow'
model_config = ConfigDict(extra='allow')
# ------------------------------ REQUEST SCHEMAS ----------------------------

View File

@ -41,7 +41,7 @@ logger = logging.getLogger(__name__)
def schema_to_dict(schema: Union[WSMessageSchema, WSRequestSchema]):
return schema.dict(exclude_none=True)
return schema.model_dump(exclude_none=True)
class ExternalMessageConsumer:
@ -322,7 +322,7 @@ class ExternalMessageConsumer:
producer_name = producer.get('name', 'default')
try:
producer_message = WSMessageSchema.parse_obj(message)
producer_message = WSMessageSchema.model_validate(message)
except ValidationError as e:
logger.error(f"Invalid message from `{producer_name}`: {e}")
return
@ -344,7 +344,7 @@ class ExternalMessageConsumer:
def _consume_whitelist_message(self, producer_name: str, message: WSMessageSchema):
try:
# Validate the message
whitelist_message = WSWhitelistMessage.parse_obj(message)
whitelist_message = WSWhitelistMessage.model_validate(message.model_dump())
except ValidationError as e:
logger.error(f"Invalid message from `{producer_name}`: {e}")
return
@ -356,7 +356,7 @@ class ExternalMessageConsumer:
def _consume_analyzed_df_message(self, producer_name: str, message: WSMessageSchema):
try:
df_message = WSAnalyzedDFMessage.parse_obj(message)
df_message = WSAnalyzedDFMessage.model_validate(message.model_dump())
except ValidationError as e:
logger.error(f"Invalid message from `{producer_name}`: {e}")
return

View File

@ -40,7 +40,7 @@ sdnotify==0.3.2
# API Server
fastapi==0.101.0
pydantic==1.10.11
pydantic==2.2.0
uvicorn==0.23.2
pyjwt==2.8.0
aiofiles==23.2.1

View File

@ -97,7 +97,7 @@ setup(
'rich',
'pyarrow; platform_machine != "armv7l"',
'fastapi',
'pydantic>=1.8.0,<2.0',
'pydantic>=2.2.0',
'uvicorn',
'psutil',
'pyjwt',

View File

@ -1429,12 +1429,12 @@ def test_api_pair_candles(botclient, ohlcv_history):
assert len(rc.json()['data']) == amount
assert (rc.json()['data'] ==
[['2017-11-26 08:50:00', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05, 0.0877869,
[['2017-11-26T08:50:00Z', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05, 0.0877869,
None, 0, 0, 0, 0, 1511686200000, None, None, None, None],
['2017-11-26 08:55:00', 8.88e-05, 8.942e-05, 8.88e-05,
['2017-11-26T08:55:00Z', 8.88e-05, 8.942e-05, 8.88e-05,
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0, 0, 0, 1511686500000, 8.893e-05,
None, None, None],
['2017-11-26 09:00:00', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
['2017-11-26T09:00:00Z', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
0.7039405, 8.885e-05, 0, 0, 0, 0, 1511686800000, None, None, None, None]
])
@ -1448,13 +1448,13 @@ def test_api_pair_candles(botclient, ohlcv_history):
f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}")
assert_response(rc)
assert (rc.json()['data'] ==
[['2017-11-26 08:50:00', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05, 0.0877869,
[['2017-11-26T08:50:00Z', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05, 0.0877869,
None, 0, None, 0, 0, None, 1511686200000, None, None, None, None],
['2017-11-26 08:55:00', 8.88e-05, 8.942e-05, 8.88e-05,
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0.0, 0, 0, '2017-11-26 08:55:00',
['2017-11-26T08:55:00Z', 8.88e-05, 8.942e-05, 8.88e-05,
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0.0, 0, 0, '2017-11-26T08:55:00Z',
1511686500000, 8.893e-05, None, None, None],
['2017-11-26 09:00:00', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
0.7039405, 8.885e-05, 0, 0.0, 0, 0, '2017-11-26 09:00:00', 1511686800000,
['2017-11-26T09:00:00Z', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
0.7039405, 8.885e-05, 0, 0.0, 0, 0, '2017-11-26T09:00:00Z', 1511686800000,
None, None, None, None]
])
@ -1511,7 +1511,7 @@ def test_api_pair_history(botclient, mocker):
date_col_idx = [idx for idx, c in enumerate(result['columns']) if c == 'date'][0]
rsi_col_idx = [idx for idx, c in enumerate(result['columns']) if c == 'rsi'][0]
assert data[0][date_col_idx] == '2018-01-11 00:00:00'
assert data[0][date_col_idx] == '2018-01-11T00:00:00Z'
assert data[0][rsi_col_idx] is not None
assert data[0][rsi_col_idx] > 0
assert lfm.call_count == 1