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
8 changed files with 142 additions and 147 deletions
+117 -119
View File
@@ -1,7 +1,7 @@
from datetime import date, datetime from datetime import date, datetime
from typing import Any, Dict, List, Optional, Union 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.constants import DATETIME_PRINT_FORMAT, IntOrInf
from freqtrade.enums import MarginMode, OrderTypeValues, SignalDirection, TradingMode from freqtrade.enums import MarginMode, OrderTypeValues, SignalDirection, TradingMode
@@ -9,9 +9,9 @@ from freqtrade.types import ValidExchangesType
class ExchangeModePayloadMixin(BaseModel): class ExchangeModePayloadMixin(BaseModel):
trading_mode: Optional[TradingMode] trading_mode: Optional[TradingMode] = None
margin_mode: Optional[MarginMode] margin_mode: Optional[MarginMode] = None
exchange: Optional[str] exchange: Optional[str] = None
class Ping(BaseModel): class Ping(BaseModel):
@@ -43,11 +43,11 @@ class BackgroundTaskStatus(BaseModel):
job_category: str job_category: str
status: str status: str
running: bool running: bool
progress: Optional[float] progress: Optional[float] = None
class BackgroundTaskResult(BaseModel): class BackgroundTaskResult(BaseModel):
error: Optional[str] error: Optional[str] = None
status: str status: str
@@ -60,9 +60,9 @@ class Balance(BaseModel):
free: float free: float
balance: float balance: float
used: float used: float
bot_owned: Optional[float] bot_owned: Optional[float] = None
est_stake: float est_stake: float
est_stake_bot: Optional[float] est_stake_bot: Optional[float] = None
stake: str stake: str
# Starting with 2.x # Starting with 2.x
side: str side: str
@@ -141,7 +141,7 @@ class Profit(BaseModel):
expectancy_ratio: float expectancy_ratio: float
max_drawdown: float max_drawdown: float
max_drawdown_abs: float max_drawdown_abs: float
trading_volume: Optional[float] trading_volume: Optional[float] = None
bot_start_timestamp: int bot_start_timestamp: int
bot_start_date: str bot_start_date: str
@@ -173,50 +173,50 @@ class Daily(BaseModel):
class UnfilledTimeout(BaseModel): class UnfilledTimeout(BaseModel):
entry: Optional[int] entry: Optional[int] = None
exit: Optional[int] exit: Optional[int] = None
unit: Optional[str] unit: Optional[str] = None
exit_timeout_count: Optional[int] exit_timeout_count: Optional[int] = None
class OrderTypes(BaseModel): class OrderTypes(BaseModel):
entry: OrderTypeValues entry: OrderTypeValues
exit: OrderTypeValues exit: OrderTypeValues
emergency_exit: Optional[OrderTypeValues] emergency_exit: Optional[OrderTypeValues] = None
force_exit: Optional[OrderTypeValues] force_exit: Optional[OrderTypeValues] = None
force_entry: Optional[OrderTypeValues] force_entry: Optional[OrderTypeValues] = None
stoploss: OrderTypeValues stoploss: OrderTypeValues
stoploss_on_exchange: bool stoploss_on_exchange: bool
stoploss_on_exchange_interval: Optional[int] stoploss_on_exchange_interval: Optional[int] = None
class ShowConfig(BaseModel): class ShowConfig(BaseModel):
version: str version: str
strategy_version: Optional[str] strategy_version: Optional[str] = None
api_version: float api_version: float
dry_run: bool dry_run: bool
trading_mode: str trading_mode: str
short_allowed: bool short_allowed: bool
stake_currency: str stake_currency: str
stake_amount: str stake_amount: str
available_capital: Optional[float] available_capital: Optional[float] = None
stake_currency_decimals: int stake_currency_decimals: int
max_open_trades: IntOrInf max_open_trades: IntOrInf
minimal_roi: Dict[str, Any] minimal_roi: Dict[str, Any]
stoploss: Optional[float] stoploss: Optional[float] = None
stoploss_on_exchange: bool stoploss_on_exchange: bool
trailing_stop: Optional[bool] trailing_stop: Optional[bool] = None
trailing_stop_positive: Optional[float] trailing_stop_positive: Optional[float] = None
trailing_stop_positive_offset: Optional[float] trailing_stop_positive_offset: Optional[float] = None
trailing_only_offset_is_reached: Optional[bool] trailing_only_offset_is_reached: Optional[bool] = None
unfilledtimeout: Optional[UnfilledTimeout] # Empty in webserver mode unfilledtimeout: Optional[UnfilledTimeout] = None # Empty in webserver mode
order_types: Optional[OrderTypes] order_types: Optional[OrderTypes] = None
use_custom_stoploss: Optional[bool] use_custom_stoploss: Optional[bool] = None
timeframe: Optional[str] timeframe: Optional[str] = None
timeframe_ms: int timeframe_ms: int
timeframe_min: int timeframe_min: int
exchange: str exchange: str
strategy: Optional[str] strategy: Optional[str] = None
force_entry_enable: bool force_entry_enable: bool
exit_pricing: Dict[str, Any] exit_pricing: Dict[str, Any]
entry_pricing: Dict[str, Any] entry_pricing: Dict[str, Any]
@@ -231,17 +231,17 @@ class OrderSchema(BaseModel):
pair: str pair: str
order_id: str order_id: str
status: str status: str
remaining: Optional[float] remaining: Optional[float] = None
amount: float amount: float
safe_price: float safe_price: float
cost: float cost: float
filled: Optional[float] filled: Optional[float] = None
ft_order_side: str ft_order_side: str
order_type: str order_type: str
is_open: bool is_open: bool
order_timestamp: Optional[int] order_timestamp: Optional[int] = None
order_filled_timestamp: Optional[int] order_filled_timestamp: Optional[int] = None
ft_fee_base: Optional[float] ft_fee_base: Optional[float] = None
class TradeSchema(BaseModel): class TradeSchema(BaseModel):
@@ -255,81 +255,81 @@ class TradeSchema(BaseModel):
amount: float amount: float
amount_requested: float amount_requested: float
stake_amount: float stake_amount: float
max_stake_amount: Optional[float] max_stake_amount: Optional[float] = None
strategy: str strategy: str
enter_tag: Optional[str] enter_tag: Optional[str] = None
timeframe: int timeframe: int
fee_open: Optional[float] fee_open: Optional[float] = None
fee_open_cost: Optional[float] fee_open_cost: Optional[float] = None
fee_open_currency: Optional[str] fee_open_currency: Optional[str] = None
fee_close: Optional[float] fee_close: Optional[float] = None
fee_close_cost: Optional[float] fee_close_cost: Optional[float] = None
fee_close_currency: Optional[str] fee_close_currency: Optional[str] = None
open_date: str open_date: str
open_timestamp: int open_timestamp: int
open_rate: float open_rate: float
open_rate_requested: Optional[float] open_rate_requested: Optional[float] = None
open_trade_value: float open_trade_value: float
close_date: Optional[str] close_date: Optional[str] = None
close_timestamp: Optional[int] close_timestamp: Optional[int] = None
close_rate: Optional[float] close_rate: Optional[float] = None
close_rate_requested: Optional[float] close_rate_requested: Optional[float] = None
close_profit: Optional[float] close_profit: Optional[float] = None
close_profit_pct: Optional[float] close_profit_pct: Optional[float] = None
close_profit_abs: Optional[float] close_profit_abs: Optional[float] = None
profit_ratio: Optional[float] profit_ratio: Optional[float] = None
profit_pct: Optional[float] profit_pct: Optional[float] = None
profit_abs: Optional[float] profit_abs: Optional[float] = None
profit_fiat: Optional[float] profit_fiat: Optional[float] = None
realized_profit: float realized_profit: float
realized_profit_ratio: Optional[float] realized_profit_ratio: Optional[float] = None
exit_reason: Optional[str] exit_reason: Optional[str] = None
exit_order_status: Optional[str] exit_order_status: Optional[str] = None
stop_loss_abs: Optional[float] stop_loss_abs: Optional[float] = None
stop_loss_ratio: Optional[float] stop_loss_ratio: Optional[float] = None
stop_loss_pct: Optional[float] stop_loss_pct: Optional[float] = None
stoploss_order_id: Optional[str] stoploss_order_id: Optional[str] = None
stoploss_last_update: Optional[str] stoploss_last_update: Optional[str] = None
stoploss_last_update_timestamp: Optional[int] stoploss_last_update_timestamp: Optional[int] = None
initial_stop_loss_abs: Optional[float] initial_stop_loss_abs: Optional[float] = None
initial_stop_loss_ratio: Optional[float] initial_stop_loss_ratio: Optional[float] = None
initial_stop_loss_pct: Optional[float] initial_stop_loss_pct: Optional[float] = None
min_rate: Optional[float] min_rate: Optional[float] = None
max_rate: Optional[float] max_rate: Optional[float] = None
open_order_id: Optional[str] open_order_id: Optional[str] = None
orders: List[OrderSchema] orders: List[OrderSchema]
leverage: Optional[float] leverage: Optional[float] = None
interest_rate: Optional[float] interest_rate: Optional[float] = None
liquidation_price: Optional[float] liquidation_price: Optional[float] = None
funding_fees: Optional[float] funding_fees: Optional[float] = None
trading_mode: Optional[TradingMode] trading_mode: Optional[TradingMode] = None
amount_precision: Optional[float] amount_precision: Optional[float] = None
price_precision: Optional[float] price_precision: Optional[float] = None
precision_mode: Optional[int] precision_mode: Optional[int] = None
class OpenTradeSchema(TradeSchema): class OpenTradeSchema(TradeSchema):
stoploss_current_dist: Optional[float] stoploss_current_dist: Optional[float] = None
stoploss_current_dist_pct: Optional[float] stoploss_current_dist_pct: Optional[float] = None
stoploss_current_dist_ratio: Optional[float] stoploss_current_dist_ratio: Optional[float] = None
stoploss_entry_dist: Optional[float] stoploss_entry_dist: Optional[float] = None
stoploss_entry_dist_ratio: Optional[float] stoploss_entry_dist_ratio: Optional[float] = None
current_rate: float current_rate: float
total_profit_abs: float total_profit_abs: float
total_profit_fiat: Optional[float] total_profit_fiat: Optional[float] = None
total_profit_ratio: Optional[float] total_profit_ratio: Optional[float] = None
open_order: Optional[str] open_order: Optional[str] = None
class TradeResponse(BaseModel): class TradeResponse(BaseModel):
@@ -339,8 +339,7 @@ class TradeResponse(BaseModel):
total_trades: int total_trades: int
class ForceEnterResponse(BaseModel): ForceEnterResponse = RootModel[Union[TradeSchema, StatusMsg]]
__root__: Union[TradeSchema, StatusMsg]
class LockModel(BaseModel): class LockModel(BaseModel):
@@ -352,7 +351,7 @@ class LockModel(BaseModel):
lock_timestamp: int lock_timestamp: int
pair: str pair: str
side: str side: str
reason: Optional[str] reason: Optional[str] = None
class Locks(BaseModel): class Locks(BaseModel):
@@ -361,8 +360,8 @@ class Locks(BaseModel):
class DeleteLockRequest(BaseModel): class DeleteLockRequest(BaseModel):
pair: Optional[str] pair: Optional[str] = None
lockid: Optional[int] lockid: Optional[int] = None
class Logs(BaseModel): class Logs(BaseModel):
@@ -373,17 +372,17 @@ class Logs(BaseModel):
class ForceEnterPayload(BaseModel): class ForceEnterPayload(BaseModel):
pair: str pair: str
side: SignalDirection = SignalDirection.LONG side: SignalDirection = SignalDirection.LONG
price: Optional[float] price: Optional[float] = None
ordertype: Optional[OrderTypeValues] ordertype: Optional[OrderTypeValues] = None
stakeamount: Optional[float] stakeamount: Optional[float] = None
entry_tag: Optional[str] entry_tag: Optional[str] = None
leverage: Optional[float] leverage: Optional[float] = None
class ForceExitPayload(BaseModel): class ForceExitPayload(BaseModel):
tradeid: str tradeid: str
ordertype: Optional[OrderTypeValues] ordertype: Optional[OrderTypeValues] = None
amount: Optional[float] amount: Optional[float] = None
class BlacklistPayload(BaseModel): class BlacklistPayload(BaseModel):
@@ -405,7 +404,7 @@ class WhitelistResponse(BaseModel):
class WhitelistEvaluateResponse(BackgroundTaskResult): class WhitelistEvaluateResponse(BackgroundTaskResult):
result: Optional[WhitelistResponse] result: Optional[WhitelistResponse] = None
class DeleteTrade(BaseModel): class DeleteTrade(BaseModel):
@@ -420,8 +419,7 @@ class PlotConfig_(BaseModel):
subplots: Dict[str, Any] subplots: Dict[str, Any]
class PlotConfig(BaseModel): PlotConfig = RootModel[Union[PlotConfig_, Dict]]
__root__: Union[PlotConfig_, Dict]
class StrategyListResponse(BaseModel): class StrategyListResponse(BaseModel):
@@ -470,7 +468,7 @@ class PairHistory(BaseModel):
timeframe: str timeframe: str
timeframe_ms: int timeframe_ms: int
columns: List[str] columns: List[str]
data: List[Any] data: SerializeAsAny[List[Any]]
length: int length: int
buy_signals: int buy_signals: int
sell_signals: int sell_signals: int
@@ -484,11 +482,11 @@ class PairHistory(BaseModel):
data_start: str data_start: str
data_stop: str data_stop: str
data_stop_ts: int data_stop_ts: int
# TODO[pydantic]: The following keys were removed: `json_encoders`.
class Config: # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information.
json_encoders = { model_config = ConfigDict(json_encoders={
datetime: lambda v: v.strftime(DATETIME_PRINT_FORMAT), datetime: lambda v: v.strftime(DATETIME_PRINT_FORMAT),
} })
class BacktestFreqAIInputs(BaseModel): class BacktestFreqAIInputs(BaseModel):
@@ -497,16 +495,16 @@ class BacktestFreqAIInputs(BaseModel):
class BacktestRequest(BaseModel): class BacktestRequest(BaseModel):
strategy: str strategy: str
timeframe: Optional[str] timeframe: Optional[str] = None
timeframe_detail: Optional[str] timeframe_detail: Optional[str] = None
timerange: Optional[str] timerange: Optional[str] = None
max_open_trades: Optional[IntOrInf] max_open_trades: Optional[IntOrInf] = None
stake_amount: Optional[str] stake_amount: Optional[Union[str, float]] = None
enable_protections: bool enable_protections: bool
dry_run_wallet: Optional[float] dry_run_wallet: Optional[float] = None
backtest_cache: Optional[str] backtest_cache: Optional[str] = None
freqaimodel: Optional[str] freqaimodel: Optional[str] = None
freqai: Optional[BacktestFreqAIInputs] freqai: Optional[BacktestFreqAIInputs] = None
class BacktestResponse(BaseModel): class BacktestResponse(BaseModel):
@@ -515,9 +513,9 @@ class BacktestResponse(BaseModel):
status_msg: str status_msg: str
step: str step: str
progress: float progress: float
trade_count: Optional[float] trade_count: Optional[float] = None
# TODO: Properly type backtestresult... # TODO: Properly type backtestresult...
backtest_result: Optional[Dict[str, Any]] backtest_result: Optional[Dict[str, Any]] = None
# TODO: This is a copy of BacktestHistoryEntryType # TODO: This is a copy of BacktestHistoryEntryType
@@ -540,5 +538,5 @@ class SysInfo(BaseModel):
class Health(BaseModel): class Health(BaseModel):
last_process: Optional[datetime] last_process: Optional[datetime] = None
last_process_ts: Optional[int] last_process_ts: Optional[int] = None
+4 -4
View File
@@ -175,9 +175,9 @@ def force_entry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)):
leverage=payload.leverage) leverage=payload.leverage)
if trade: if trade:
return ForceEnterResponse.parse_obj(trade.to_json()) return ForceEnterResponse.model_validate(trade.to_json())
else: else:
return ForceEnterResponse.parse_obj( return ForceEnterResponse.model_validate(
{"status": f"Error entering {payload.side} trade for pair {payload.pair}."}) {"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 strategy:
if not rpc: if not rpc:
raise RPCException("Strategy is mandatory in webserver mode.") 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: else:
config1 = deepcopy(config) config1 = deepcopy(config)
config1.update({ config1.update({
'strategy': strategy 'strategy': strategy
}) })
try: 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: except Exception as e:
raise HTTPException(status_code=502, detail=str(e)) raise HTTPException(status_code=502, detail=str(e))
+3 -3
View File
@@ -65,7 +65,7 @@ async def _process_consumer_request(
""" """
# Validate the request, makes sure it matches the schema # Validate the request, makes sure it matches the schema
try: try:
websocket_request = WSRequestSchema.parse_obj(request) websocket_request = WSRequestSchema.model_validate(request)
except ValidationError as e: except ValidationError as e:
logger.error(f"Invalid request from {channel}: {e}") logger.error(f"Invalid request from {channel}: {e}")
return return
@@ -94,7 +94,7 @@ async def _process_consumer_request(
# Format response # Format response
response = WSWhitelistMessage(data=whitelist) 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: elif type_ == RPCRequestType.ANALYZED_DF:
# Limit the amount of candles per dataframe to 'limit' or 1500 # 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): for message in rpc._ws_request_analyzed_df(limit, pair):
# Format response # Format response
response = WSAnalyzedDFMessage(data=message) 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") @router.websocket("/message/ws")
+3 -6
View File
@@ -2,15 +2,14 @@ from datetime import datetime
from typing import Any, Dict, List, Optional, TypedDict from typing import Any, Dict, List, Optional, TypedDict
from pandas import DataFrame from pandas import DataFrame
from pydantic import BaseModel from pydantic import BaseModel, ConfigDict
from freqtrade.constants import PairWithTimeframe from freqtrade.constants import PairWithTimeframe
from freqtrade.enums.rpcmessagetype import RPCMessageType, RPCRequestType from freqtrade.enums.rpcmessagetype import RPCMessageType, RPCRequestType
class BaseArbitraryModel(BaseModel): class BaseArbitraryModel(BaseModel):
class Config: model_config = ConfigDict(arbitrary_types_allowed=True)
arbitrary_types_allowed = True
class WSRequestSchema(BaseArbitraryModel): class WSRequestSchema(BaseArbitraryModel):
@@ -27,9 +26,7 @@ class WSMessageSchemaType(TypedDict):
class WSMessageSchema(BaseArbitraryModel): class WSMessageSchema(BaseArbitraryModel):
type: RPCMessageType type: RPCMessageType
data: Optional[Any] = None data: Optional[Any] = None
model_config = ConfigDict(extra='allow')
class Config:
extra = 'allow'
# ------------------------------ REQUEST SCHEMAS ---------------------------- # ------------------------------ REQUEST SCHEMAS ----------------------------
+4 -4
View File
@@ -41,7 +41,7 @@ logger = logging.getLogger(__name__)
def schema_to_dict(schema: Union[WSMessageSchema, WSRequestSchema]): def schema_to_dict(schema: Union[WSMessageSchema, WSRequestSchema]):
return schema.dict(exclude_none=True) return schema.model_dump(exclude_none=True)
class ExternalMessageConsumer: class ExternalMessageConsumer:
@@ -322,7 +322,7 @@ class ExternalMessageConsumer:
producer_name = producer.get('name', 'default') producer_name = producer.get('name', 'default')
try: try:
producer_message = WSMessageSchema.parse_obj(message) producer_message = WSMessageSchema.model_validate(message)
except ValidationError as e: except ValidationError as e:
logger.error(f"Invalid message from `{producer_name}`: {e}") logger.error(f"Invalid message from `{producer_name}`: {e}")
return return
@@ -344,7 +344,7 @@ class ExternalMessageConsumer:
def _consume_whitelist_message(self, producer_name: str, message: WSMessageSchema): def _consume_whitelist_message(self, producer_name: str, message: WSMessageSchema):
try: try:
# Validate the message # Validate the message
whitelist_message = WSWhitelistMessage.parse_obj(message) whitelist_message = WSWhitelistMessage.model_validate(message.model_dump())
except ValidationError as e: except ValidationError as e:
logger.error(f"Invalid message from `{producer_name}`: {e}") logger.error(f"Invalid message from `{producer_name}`: {e}")
return return
@@ -356,7 +356,7 @@ class ExternalMessageConsumer:
def _consume_analyzed_df_message(self, producer_name: str, message: WSMessageSchema): def _consume_analyzed_df_message(self, producer_name: str, message: WSMessageSchema):
try: try:
df_message = WSAnalyzedDFMessage.parse_obj(message) df_message = WSAnalyzedDFMessage.model_validate(message.model_dump())
except ValidationError as e: except ValidationError as e:
logger.error(f"Invalid message from `{producer_name}`: {e}") logger.error(f"Invalid message from `{producer_name}`: {e}")
return return
+1 -1
View File
@@ -40,7 +40,7 @@ sdnotify==0.3.2
# API Server # API Server
fastapi==0.101.0 fastapi==0.101.0
pydantic==1.10.11 pydantic==2.2.0
uvicorn==0.23.2 uvicorn==0.23.2
pyjwt==2.8.0 pyjwt==2.8.0
aiofiles==23.2.1 aiofiles==23.2.1
+1 -1
View File
@@ -97,7 +97,7 @@ setup(
'rich', 'rich',
'pyarrow; platform_machine != "armv7l"', 'pyarrow; platform_machine != "armv7l"',
'fastapi', 'fastapi',
'pydantic>=1.8.0,<2.0', 'pydantic>=2.2.0',
'uvicorn', 'uvicorn',
'psutil', 'psutil',
'pyjwt', 'pyjwt',
+9 -9
View File
@@ -1429,12 +1429,12 @@ def test_api_pair_candles(botclient, ohlcv_history):
assert len(rc.json()['data']) == amount assert len(rc.json()['data']) == amount
assert (rc.json()['data'] == 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], 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, 8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0, 0, 0, 1511686500000, 8.893e-05,
None, None, None], 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] 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}") f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}")
assert_response(rc) assert_response(rc)
assert (rc.json()['data'] == 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], 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, ['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-26 08:55:00', 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], 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, 0, '2017-11-26 09:00:00', 1511686800000, 0.7039405, 8.885e-05, 0, 0.0, 0, 0, '2017-11-26T09:00:00Z', 1511686800000,
None, None, None, None] 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] 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] 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] is not None
assert data[0][rsi_col_idx] > 0 assert data[0][rsi_col_idx] > 0
assert lfm.call_count == 1 assert lfm.call_count == 1