pybinbot 2.1.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.
Files changed (79) hide show
  1. {pybinbot-2.1.0/pybinbot.egg-info → pybinbot-2.1.1}/PKG-INFO +2 -2
  2. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/__init__.py +2 -0
  3. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/bot_base.py +31 -1
  4. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/enums.py +5 -0
  5. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/streaming/binance/socket_manager.py +3 -3
  6. {pybinbot-2.1.0 → pybinbot-2.1.1/pybinbot.egg-info}/PKG-INFO +2 -2
  7. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot.egg-info/SOURCES.txt +1 -0
  8. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot.egg-info/requires.txt +1 -1
  9. {pybinbot-2.1.0 → pybinbot-2.1.1}/pyproject.toml +2 -2
  10. pybinbot-2.1.1/tests/test_conditional_entry.py +45 -0
  11. {pybinbot-2.1.0 → pybinbot-2.1.1}/LICENSE +0 -0
  12. {pybinbot-2.1.0 → pybinbot-2.1.1}/MANIFEST.in +0 -0
  13. {pybinbot-2.1.0 → pybinbot-2.1.1}/README.md +0 -0
  14. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/binance/base.py +0 -0
  15. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/binance/exceptions.py +0 -0
  16. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/binbot/base.py +0 -0
  17. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/binbot/exceptions.py +0 -0
  18. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/coingecko.py +0 -0
  19. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/base.py +0 -0
  20. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/exceptions.py +0 -0
  21. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/futures.py +0 -0
  22. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/market.py +0 -0
  23. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/orders.py +0 -0
  24. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/apis/kucoin/rest.py +0 -0
  25. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/__init__.py +0 -0
  26. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/autotrade_settings.py +0 -0
  27. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/bot.py +0 -0
  28. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/deal.py +0 -0
  29. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/derivatives.py +0 -0
  30. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/grid_ladder.py +0 -0
  31. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/order.py +0 -0
  32. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/routes.py +0 -0
  33. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/signals.py +0 -0
  34. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/models/symbol.py +0 -0
  35. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/py.typed +0 -0
  36. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/__init__.py +0 -0
  37. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/beta.py +0 -0
  38. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/breadth.py +0 -0
  39. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/cache.py +0 -0
  40. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/candles.py +0 -0
  41. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/handlers.py +0 -0
  42. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/heikin_ashi.py +0 -0
  43. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/indicators.py +0 -0
  44. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/logging_config.py +0 -0
  45. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/maths.py +0 -0
  46. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/price_crossings.py +0 -0
  47. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/telegram_delivery.py +0 -0
  48. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/timestamps.py +0 -0
  49. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/types.py +0 -0
  50. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/shared/utils.py +0 -0
  51. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/streaming/binance/async_socket_client.py +0 -0
  52. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/streaming/binance/socket_client.py +0 -0
  53. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot/streaming/kucoin/kucoin_async_client.py +0 -0
  54. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot.egg-info/dependency_links.txt +0 -0
  55. {pybinbot-2.1.0 → pybinbot-2.1.1}/pybinbot.egg-info/top_level.txt +0 -0
  56. {pybinbot-2.1.0 → pybinbot-2.1.1}/setup.cfg +0 -0
  57. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_beta.py +0 -0
  58. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_binbot_api.py +0 -0
  59. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_bot_futures_leverage.py +0 -0
  60. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_breadth.py +0 -0
  61. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_candles.py +0 -0
  62. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_coingecko.py +0 -0
  63. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_conditional_futures_orders.py +0 -0
  64. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_deal_model.py +0 -0
  65. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_derivatives_api.py +0 -0
  66. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_futures_place_order.py +0 -0
  67. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_grid_ladder.py +0 -0
  68. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_handlers.py +0 -0
  69. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_heikin_ashi.py +0 -0
  70. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_indicators.py +0 -0
  71. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_kucoin_api.py +0 -0
  72. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_logging_config.py +0 -0
  73. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_matching_engine.py +0 -0
  74. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_maths.py +0 -0
  75. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_price_crossings.py +0 -0
  76. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_symbol_model.py +0 -0
  77. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_telegram_delivery.py +0 -0
  78. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_timestamps.py +0 -0
  79. {pybinbot-2.1.0 → pybinbot-2.1.1}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybinbot
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: Utility functions for the binbot project.
5
5
  Author-email: Carlos Wu <carkodw@gmail.com>
6
6
  Requires-Python: >=3.15
@@ -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.0
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
@@ -38,6 +38,7 @@ from pybinbot.shared.enums import (
38
38
  BinanceOrderModel,
39
39
  CloseConditions,
40
40
  DealType,
41
+ EntryMode,
41
42
  ExchangeId,
42
43
  KucoinKlineIntervals,
43
44
  MarketDominance,
@@ -282,6 +283,7 @@ __all__ = [
282
283
  # enums
283
284
  "CloseConditions",
284
285
  "DealType",
286
+ "EntryMode",
285
287
  "BinanceOrderModel",
286
288
  "Status",
287
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:
@@ -60,6 +60,11 @@ class Status(str, Enum):
60
60
  error = "error"
61
61
 
62
62
 
63
+ class EntryMode(str, Enum):
64
+ immediate = "immediate"
65
+ conditional_limit = "conditional_limit"
66
+
67
+
63
68
  class Position(str, Enum):
64
69
  long = "long"
65
70
  short = "short"
@@ -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(threading.Thread):
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
- threading.Thread.__init__(self)
26
+ Thread.__init__(self)
27
27
  if not logger:
28
28
  logger = logging.getLogger(__name__)
29
29
  self.logger = logger
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybinbot
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: Utility functions for the binbot project.
5
5
  Author-email: Carlos Wu <carkodw@gmail.com>
6
6
  Requires-Python: >=3.15
@@ -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.0
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
@@ -57,6 +57,7 @@ tests/test_bot_futures_leverage.py
57
57
  tests/test_breadth.py
58
58
  tests/test_candles.py
59
59
  tests/test_coingecko.py
60
+ tests/test_conditional_entry.py
60
61
  tests/test_conditional_futures_orders.py
61
62
  tests/test_deal_model.py
62
63
  tests/test_derivatives_api.py
@@ -3,7 +3,7 @@ numpy==2.5.4
3
3
  pandas==3.1.1
4
4
  pandas-stubs==3.1.0.261029
5
5
  requests>=2.32.5
6
- kucoin-universal-sdk>=1.3.0
6
+ kucoin-universal-sdk>=1.3.2
7
7
  aiohttp<3.15,>=3.14.1
8
8
  websocket-client>=1.9.0
9
9
  python-dotenv>=1.2.1
@@ -22,7 +22,7 @@ build-backend = "setuptools.build_meta"
22
22
 
23
23
  [project]
24
24
  name = "pybinbot"
25
- version = "2.1.0"
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" }
@@ -35,7 +35,7 @@ dependencies = [
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.0",
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