pybinbot 2.0.0__tar.gz → 2.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pybinbot-2.0.0/pybinbot.egg-info → pybinbot-2.1.1}/PKG-INFO +7 -7
- {pybinbot-2.0.0 → pybinbot-2.1.1}/README.md +4 -4
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/__init__.py +2 -24
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/rest.py +1 -1
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/bot_base.py +31 -1
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/candles.py +2 -2
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/enums.py +5 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/maths.py +1 -1
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/streaming/binance/socket_manager.py +3 -3
- {pybinbot-2.0.0 → pybinbot-2.1.1/pybinbot.egg-info}/PKG-INFO +7 -7
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot.egg-info/SOURCES.txt +1 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot.egg-info/requires.txt +1 -1
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pyproject.toml +3 -3
- pybinbot-2.1.1/tests/test_conditional_entry.py +45 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/LICENSE +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/MANIFEST.in +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/binance/base.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/binance/exceptions.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/binbot/base.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/binbot/exceptions.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/coingecko.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/base.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/exceptions.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/futures.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/market.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/orders.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/__init__.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/autotrade_settings.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/bot.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/deal.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/derivatives.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/grid_ladder.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/order.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/routes.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/signals.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/models/symbol.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/py.typed +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/__init__.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/beta.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/breadth.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/cache.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/handlers.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/heikin_ashi.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/indicators.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/logging_config.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/price_crossings.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/telegram_delivery.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/timestamps.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/types.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/shared/utils.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/streaming/binance/async_socket_client.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/streaming/binance/socket_client.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot/streaming/kucoin/kucoin_async_client.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot.egg-info/dependency_links.txt +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/pybinbot.egg-info/top_level.txt +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/setup.cfg +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_beta.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_binbot_api.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_bot_futures_leverage.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_breadth.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_candles.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_coingecko.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_conditional_futures_orders.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_deal_model.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_derivatives_api.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_futures_place_order.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_grid_ladder.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_handlers.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_heikin_ashi.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_indicators.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_kucoin_api.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_logging_config.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_matching_engine.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_maths.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_price_crossings.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_symbol_model.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_telegram_delivery.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_timestamps.py +0 -0
- {pybinbot-2.0.0 → pybinbot-2.1.1}/tests/test_utils.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pybinbot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Utility functions for the binbot project.
|
|
5
5
|
Author-email: Carlos Wu <carkodw@gmail.com>
|
|
6
|
-
Requires-Python: >=3.
|
|
6
|
+
Requires-Python: >=3.15
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Dist: pydantic[email]>=2.0.0
|
|
@@ -11,7 +11,7 @@ Requires-Dist: numpy==2.5.4
|
|
|
11
11
|
Requires-Dist: pandas==3.1.1
|
|
12
12
|
Requires-Dist: pandas-stubs==3.1.0.261029
|
|
13
13
|
Requires-Dist: requests>=2.32.5
|
|
14
|
-
Requires-Dist: kucoin-universal-sdk>=1.3.
|
|
14
|
+
Requires-Dist: kucoin-universal-sdk>=1.3.2
|
|
15
15
|
Requires-Dist: aiohttp<3.15,>=3.14.1
|
|
16
16
|
Requires-Dist: websocket-client>=1.9.0
|
|
17
17
|
Requires-Dist: python-dotenv>=1.2.1
|
|
@@ -37,12 +37,12 @@ This module re-exports the internal ``shared`` and ``models`` packages and the m
|
|
|
37
37
|
|
|
38
38
|
from pybinbot import round_numbers, ExchangeId
|
|
39
39
|
|
|
40
|
-
Python 3.
|
|
41
|
-
pybinbot
|
|
42
|
-
dependencies. Consumers can import the modules or commonly used types and functions:
|
|
40
|
+
Python 3.15 or newer is required. Pandas and NumPy use matching version pins in
|
|
41
|
+
pybinbot, binbot, and binquant. Import these libraries directly:
|
|
43
42
|
|
|
44
43
|
```python
|
|
45
|
-
|
|
44
|
+
import numpy as np
|
|
45
|
+
import pandas as pd
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
|
|
@@ -8,12 +8,12 @@ This module re-exports the internal ``shared`` and ``models`` packages and the m
|
|
|
8
8
|
|
|
9
9
|
from pybinbot import round_numbers, ExchangeId
|
|
10
10
|
|
|
11
|
-
Python 3.
|
|
12
|
-
pybinbot
|
|
13
|
-
dependencies. Consumers can import the modules or commonly used types and functions:
|
|
11
|
+
Python 3.15 or newer is required. Pandas and NumPy use matching version pins in
|
|
12
|
+
pybinbot, binbot, and binquant. Import these libraries directly:
|
|
14
13
|
|
|
15
14
|
```python
|
|
16
|
-
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pandas as pd
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
|
|
@@ -5,18 +5,6 @@ models, while still allowing access to the structured subpackages via
|
|
|
5
5
|
``pybinbot.shared`` and ``pybinbot.models``.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import numpy as numpy
|
|
9
|
-
import pandas as pandas
|
|
10
|
-
from numpy import isnan as isnan, log as log, nan as nan
|
|
11
|
-
from pandas import (
|
|
12
|
-
DataFrame as DataFrame,
|
|
13
|
-
Series as Series,
|
|
14
|
-
concat as concat,
|
|
15
|
-
to_datetime as to_datetime,
|
|
16
|
-
to_numeric as to_numeric,
|
|
17
|
-
)
|
|
18
|
-
from pandas.api.types import is_numeric_dtype as is_numeric_dtype
|
|
19
|
-
|
|
20
8
|
from pybinbot.shared.maths import (
|
|
21
9
|
STOP_LIMIT_OFFSET_RATIO,
|
|
22
10
|
ceil_price_to_tick,
|
|
@@ -50,6 +38,7 @@ from pybinbot.shared.enums import (
|
|
|
50
38
|
BinanceOrderModel,
|
|
51
39
|
CloseConditions,
|
|
52
40
|
DealType,
|
|
41
|
+
EntryMode,
|
|
53
42
|
ExchangeId,
|
|
54
43
|
KucoinKlineIntervals,
|
|
55
44
|
MarketDominance,
|
|
@@ -185,18 +174,6 @@ from . import models, shared, apis
|
|
|
185
174
|
configure_logging()
|
|
186
175
|
|
|
187
176
|
__all__ = [
|
|
188
|
-
# Numerical dependencies shared by binbot and binquant.
|
|
189
|
-
"numpy",
|
|
190
|
-
"pandas",
|
|
191
|
-
"DataFrame",
|
|
192
|
-
"Series",
|
|
193
|
-
"concat",
|
|
194
|
-
"to_datetime",
|
|
195
|
-
"to_numeric",
|
|
196
|
-
"is_numeric_dtype",
|
|
197
|
-
"isnan",
|
|
198
|
-
"log",
|
|
199
|
-
"nan",
|
|
200
177
|
"BinanceFundingRate",
|
|
201
178
|
"BinanceFundingRatesResponse",
|
|
202
179
|
"FundingRateHistoryPoint",
|
|
@@ -306,6 +283,7 @@ __all__ = [
|
|
|
306
283
|
# enums
|
|
307
284
|
"CloseConditions",
|
|
308
285
|
"DealType",
|
|
286
|
+
"EntryMode",
|
|
309
287
|
"BinanceOrderModel",
|
|
310
288
|
"Status",
|
|
311
289
|
"Position",
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from typing import Literal
|
|
2
2
|
from uuid import UUID
|
|
3
3
|
|
|
4
|
-
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
4
|
+
from pydantic import BaseModel, Field, ValidationInfo, field_validator, model_validator
|
|
5
5
|
|
|
6
6
|
from pybinbot.shared.enums import (
|
|
7
7
|
BinanceKlineIntervals,
|
|
8
8
|
CloseConditions,
|
|
9
|
+
EntryMode,
|
|
9
10
|
MarketType,
|
|
10
11
|
Position,
|
|
11
12
|
QuoteAssets,
|
|
@@ -46,6 +47,14 @@ class BotBase(BaseModel):
|
|
|
46
47
|
)
|
|
47
48
|
created_at: float = Field(default_factory=timestamp)
|
|
48
49
|
updated_at: float = Field(default_factory=timestamp)
|
|
50
|
+
entry_mode: EntryMode = Field(default=EntryMode.immediate, validate_default=True)
|
|
51
|
+
entry_price: float | None = Field(default=None, ge=0, allow_inf_nan=False)
|
|
52
|
+
entry_expires_at: float = Field(
|
|
53
|
+
default_factory=lambda data: data["created_at"] + 6 * 60 * 60 * 1000,
|
|
54
|
+
gt=0,
|
|
55
|
+
allow_inf_nan=False,
|
|
56
|
+
description="Absolute entry deadline in Unix milliseconds; defaults to creation + 6 hours",
|
|
57
|
+
)
|
|
49
58
|
dynamic_trailing: bool = Field(default=False)
|
|
50
59
|
dynamic_position_sizing: bool = Field(default=False)
|
|
51
60
|
logs: list = Field(default=[])
|
|
@@ -104,6 +113,27 @@ class BotBase(BaseModel):
|
|
|
104
113
|
},
|
|
105
114
|
}
|
|
106
115
|
|
|
116
|
+
@field_validator("entry_mode", mode="before")
|
|
117
|
+
@classmethod
|
|
118
|
+
def default_entry_mode(cls, value):
|
|
119
|
+
return EntryMode.immediate if value is None else value
|
|
120
|
+
|
|
121
|
+
@field_validator("entry_expires_at", mode="before")
|
|
122
|
+
@classmethod
|
|
123
|
+
def default_entry_expiry(cls, value, info: ValidationInfo):
|
|
124
|
+
return info.data["created_at"] + 6 * 60 * 60 * 1000 if value is None else value
|
|
125
|
+
|
|
126
|
+
@model_validator(mode="after")
|
|
127
|
+
def validate_conditional_entry(self):
|
|
128
|
+
if self.entry_mode == EntryMode.conditional_limit:
|
|
129
|
+
if self.entry_price is None or self.entry_price <= 0:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
"Conditional limit entry requires a positive entry_price"
|
|
132
|
+
)
|
|
133
|
+
if self.market_type != MarketType.FUTURES:
|
|
134
|
+
raise ValueError("Conditional limit entry currently requires futures")
|
|
135
|
+
return self
|
|
136
|
+
|
|
107
137
|
@model_validator(mode="after")
|
|
108
138
|
def validate_dynamic_position_sizing(self):
|
|
109
139
|
if self.dynamic_position_sizing:
|
|
@@ -126,7 +126,7 @@ class Candles:
|
|
|
126
126
|
continue
|
|
127
127
|
try:
|
|
128
128
|
open_time = float(open_time_value)
|
|
129
|
-
except
|
|
129
|
+
except TypeError, ValueError:
|
|
130
130
|
continue
|
|
131
131
|
if not isfinite(open_time):
|
|
132
132
|
continue
|
|
@@ -140,7 +140,7 @@ class Candles:
|
|
|
140
140
|
continue
|
|
141
141
|
try:
|
|
142
142
|
close_time = float(close_time_value)
|
|
143
|
-
except
|
|
143
|
+
except TypeError, ValueError:
|
|
144
144
|
continue
|
|
145
145
|
if not isfinite(close_time):
|
|
146
146
|
continue
|
|
@@ -14,7 +14,7 @@ STOP_LIMIT_OFFSET_RATIO = 0.005 # 0.5% of the stop price
|
|
|
14
14
|
def coerce_number(value: Any) -> float | None:
|
|
15
15
|
try:
|
|
16
16
|
parsed = float(value)
|
|
17
|
-
except
|
|
17
|
+
except TypeError, ValueError:
|
|
18
18
|
return None
|
|
19
19
|
if not math.isfinite(parsed):
|
|
20
20
|
return None
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
-
import threading
|
|
4
3
|
import time
|
|
4
|
+
from threading import Thread
|
|
5
5
|
|
|
6
6
|
from websocket import (
|
|
7
7
|
ABNF,
|
|
@@ -11,7 +11,7 @@ from websocket import (
|
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class BinanceSocketManager(
|
|
14
|
+
class BinanceSocketManager(Thread):
|
|
15
15
|
def __init__(
|
|
16
16
|
self,
|
|
17
17
|
stream_url,
|
|
@@ -23,7 +23,7 @@ class BinanceSocketManager(threading.Thread):
|
|
|
23
23
|
on_pong=None,
|
|
24
24
|
logger=None,
|
|
25
25
|
):
|
|
26
|
-
|
|
26
|
+
Thread.__init__(self)
|
|
27
27
|
if not logger:
|
|
28
28
|
logger = logging.getLogger(__name__)
|
|
29
29
|
self.logger = logger
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pybinbot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Utility functions for the binbot project.
|
|
5
5
|
Author-email: Carlos Wu <carkodw@gmail.com>
|
|
6
|
-
Requires-Python: >=3.
|
|
6
|
+
Requires-Python: >=3.15
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Dist: pydantic[email]>=2.0.0
|
|
@@ -11,7 +11,7 @@ Requires-Dist: numpy==2.5.4
|
|
|
11
11
|
Requires-Dist: pandas==3.1.1
|
|
12
12
|
Requires-Dist: pandas-stubs==3.1.0.261029
|
|
13
13
|
Requires-Dist: requests>=2.32.5
|
|
14
|
-
Requires-Dist: kucoin-universal-sdk>=1.3.
|
|
14
|
+
Requires-Dist: kucoin-universal-sdk>=1.3.2
|
|
15
15
|
Requires-Dist: aiohttp<3.15,>=3.14.1
|
|
16
16
|
Requires-Dist: websocket-client>=1.9.0
|
|
17
17
|
Requires-Dist: python-dotenv>=1.2.1
|
|
@@ -37,12 +37,12 @@ This module re-exports the internal ``shared`` and ``models`` packages and the m
|
|
|
37
37
|
|
|
38
38
|
from pybinbot import round_numbers, ExchangeId
|
|
39
39
|
|
|
40
|
-
Python 3.
|
|
41
|
-
pybinbot
|
|
42
|
-
dependencies. Consumers can import the modules or commonly used types and functions:
|
|
40
|
+
Python 3.15 or newer is required. Pandas and NumPy use matching version pins in
|
|
41
|
+
pybinbot, binbot, and binquant. Import these libraries directly:
|
|
43
42
|
|
|
44
43
|
```python
|
|
45
|
-
|
|
44
|
+
import numpy as np
|
|
45
|
+
import pandas as pd
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
|
|
@@ -22,20 +22,20 @@ build-backend = "setuptools.build_meta"
|
|
|
22
22
|
|
|
23
23
|
[project]
|
|
24
24
|
name = "pybinbot"
|
|
25
|
-
version = "2.
|
|
25
|
+
version = "2.1.1"
|
|
26
26
|
description = "Utility functions for the binbot project."
|
|
27
27
|
authors = [
|
|
28
28
|
{ name="Carlos Wu", email="carkodw@gmail.com" }
|
|
29
29
|
]
|
|
30
30
|
readme = "README.md"
|
|
31
|
-
requires-python = ">=3.
|
|
31
|
+
requires-python = ">=3.15"
|
|
32
32
|
dependencies = [
|
|
33
33
|
"pydantic[email]>=2.0.0",
|
|
34
34
|
"numpy==2.5.4",
|
|
35
35
|
"pandas==3.1.1",
|
|
36
36
|
"pandas-stubs==3.1.0.261029",
|
|
37
37
|
"requests>=2.32.5",
|
|
38
|
-
"kucoin-universal-sdk>=1.3.
|
|
38
|
+
"kucoin-universal-sdk>=1.3.2",
|
|
39
39
|
"aiohttp>=3.14.1,<3.15",
|
|
40
40
|
"websocket-client>=1.9.0",
|
|
41
41
|
"python-dotenv>=1.2.1",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pydantic import ValidationError
|
|
3
|
+
|
|
4
|
+
from pybinbot import BotBase, EntryMode, MarketType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.parametrize(
|
|
8
|
+
"overrides",
|
|
9
|
+
[{}, {"entry_mode": None, "entry_expires_at": None, "entry_price": None}],
|
|
10
|
+
)
|
|
11
|
+
def test_omitted_or_null_entry_fields_default_to_immediate_and_six_hours(overrides):
|
|
12
|
+
bot = BotBase(pair="XBTUSDTM", created_at=1000, **overrides)
|
|
13
|
+
assert bot.entry_mode == EntryMode.immediate
|
|
14
|
+
assert bot.entry_expires_at == 21_601_000
|
|
15
|
+
assert bot.entry_price is None
|
|
16
|
+
assert bot.model_dump(mode="json")["entry_price"] is None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_conditional_entry_serializes_strings_and_preserves_deadline():
|
|
20
|
+
bot = BotBase(
|
|
21
|
+
pair="XBTUSDTM",
|
|
22
|
+
market_type=MarketType.FUTURES,
|
|
23
|
+
entry_mode="conditional_limit",
|
|
24
|
+
entry_price=100,
|
|
25
|
+
entry_expires_at=9000,
|
|
26
|
+
)
|
|
27
|
+
assert bot.model_dump(mode="json")["entry_mode"] == "conditional_limit"
|
|
28
|
+
assert bot.entry_expires_at == 9000
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize(
|
|
32
|
+
"values",
|
|
33
|
+
[
|
|
34
|
+
{"entry_mode": "unknown"},
|
|
35
|
+
{"entry_mode": "conditional_limit"},
|
|
36
|
+
{"entry_mode": "conditional_limit", "entry_price": None},
|
|
37
|
+
{"entry_mode": "conditional_limit", "entry_price": 0},
|
|
38
|
+
{"entry_mode": "conditional_limit", "entry_price": -1},
|
|
39
|
+
{"entry_mode": "conditional_limit", "entry_price": float("inf")},
|
|
40
|
+
{"entry_expires_at": float("nan")},
|
|
41
|
+
],
|
|
42
|
+
)
|
|
43
|
+
def test_invalid_entry_configuration_is_rejected(values):
|
|
44
|
+
with pytest.raises(ValidationError):
|
|
45
|
+
BotBase(pair="XBTUSDTM", market_type=MarketType.FUTURES, **values)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|