xync-client 0.0.233__tar.gz → 0.0.235.dev3__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 (135) hide show
  1. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/PKG-INFO +1 -1
  2. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/README.md +38 -1
  3. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/Agent.py +169 -69
  4. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/Ex.py +77 -17
  5. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/xtype.py +88 -17
  6. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/agent.py +34 -23
  7. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/etype/ad.py +79 -33
  8. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/ex.py +33 -15
  9. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/agent.py +3 -3
  10. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Mexc/agent.py +1 -1
  11. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/details.py +3 -0
  12. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client.egg-info/PKG-INFO +1 -1
  13. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/.env.sample +0 -0
  14. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/.gitignore +0 -0
  15. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/.pre-commit-config.yaml +0 -0
  16. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/CLAUDE.md +0 -0
  17. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/__init__.py +0 -0
  18. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/makefile +0 -0
  19. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/pyproject.toml +0 -0
  20. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/setup.cfg +0 -0
  21. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/TestAgent.py +0 -0
  22. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/TestAsset.py +0 -0
  23. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/TestEx.py +0 -0
  24. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/TestOrder.py +0 -0
  25. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/_todo_refact/Binance/test_binance.py +0 -0
  26. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
  27. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
  28. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/_todo_refact/Gate/test_gate.py +0 -0
  29. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/_todo_refact/Wallet/test_agent.py +0 -0
  30. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/_todo_refact/Wallet/test_ex.py +0 -0
  31. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/_todo_refact/__init__.py +0 -0
  32. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/tests/_todo_refact/_test_ex.py +0 -0
  33. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/AdLoader.py +0 -0
  34. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/Asset.py +0 -0
  35. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/Auth.py +0 -0
  36. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/BaseTest.py +0 -0
  37. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/Exception.py +0 -0
  38. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/HasAbotUid.py +0 -0
  39. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/InAgent.py +0 -0
  40. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/Order.py +0 -0
  41. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Abc/PmAgent.py +0 -0
  42. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/__init__.py +0 -0
  43. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/binance_async.py +0 -0
  44. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/earn_api.py +0 -0
  45. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/etype/ad.py +0 -0
  46. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/etype/pm.py +0 -0
  47. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/ex.py +0 -0
  48. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/exceptions.py +0 -0
  49. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/sapi.py +0 -0
  50. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Binance/web_c2c.py +0 -0
  51. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BingX/__init__.py +0 -0
  52. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BingX/agent.py +0 -0
  53. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BingX/base.py +0 -0
  54. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BingX/etype/ad.py +0 -0
  55. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BingX/etype/pm.py +0 -0
  56. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BingX/ex.py +0 -0
  57. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BingX/req.mjs +0 -0
  58. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BingX/sign.js +0 -0
  59. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BitGet/__init__.py +0 -0
  60. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BitGet/agent.py +0 -0
  61. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BitGet/etype/ad.py +0 -0
  62. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BitGet/ex.py +0 -0
  63. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/BitPapa/ex.py +0 -0
  64. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/etype/__init__.py +0 -0
  65. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/etype/cred.py +0 -0
  66. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/etype/order.py +0 -0
  67. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/etype/pm.py +0 -0
  68. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/inAgent.py +0 -0
  69. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/order.py +0 -0
  70. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/web_earn.py +0 -0
  71. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/web_p2p.py +0 -0
  72. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Bybit/ws.py +0 -0
  73. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Gate/etype/ad.py +0 -0
  74. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Gate/ex.py +0 -0
  75. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Gate/premarket.py +0 -0
  76. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Gmail/__init__.py +0 -0
  77. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/earn.py +0 -0
  78. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/etype/__init__.py +0 -0
  79. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/etype/ad.py +0 -0
  80. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/etype/cred.py +0 -0
  81. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/etype/order.py +0 -0
  82. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/etype/pm.py +0 -0
  83. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/etype/test.py +0 -0
  84. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Htx/ex.py +0 -0
  85. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/KuCoin/etype/ad.py +0 -0
  86. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/KuCoin/etype/pm.py +0 -0
  87. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/KuCoin/ex.py +0 -0
  88. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/KuCoin/web.py +0 -0
  89. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Mexc/api.py +0 -0
  90. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Mexc/etype/ad.py +0 -0
  91. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Mexc/etype/order.py +0 -0
  92. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Mexc/etype/pm.py +0 -0
  93. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Mexc/ex.py +0 -0
  94. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Okx/1.py +0 -0
  95. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Okx/agent.py +0 -0
  96. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Okx/etype/ad.py +0 -0
  97. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Okx/etype/pm.py +0 -0
  98. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Okx/ex.py +0 -0
  99. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/.gitignore +0 -0
  100. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Alfa/__init__.py +0 -0
  101. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Alfa/state.json +0 -0
  102. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/MTS/__init__.py +0 -0
  103. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Ozon/__init__.py +0 -0
  104. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Payeer/.gitignore +0 -0
  105. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Payeer/agent.py +0 -0
  106. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Payeer/login.py +0 -0
  107. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Payeer/trade.py +0 -0
  108. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Sber/__init__.py +0 -0
  109. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Sber/utils.py +0 -0
  110. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Tinkoff/__init__.py +0 -0
  111. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Volet/_todo_req/req.mjs +0 -0
  112. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Volet/_todo_req/req.py +0 -0
  113. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Volet/agent.py +0 -0
  114. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Volet/api.py +0 -0
  115. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Volet/pl.py +0 -0
  116. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Xync/__main__.py +0 -0
  117. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Xync/ed.py +0 -0
  118. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/Yandex/__init__.py +0 -0
  119. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/Pms/airtm/__init__.py +0 -0
  120. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/agent.py +0 -0
  121. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/asset.py +0 -0
  122. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/auth.py +0 -0
  123. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/ex.py +0 -0
  124. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/inAgent.py +0 -0
  125. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/order.py +0 -0
  126. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/pyd.py +0 -0
  127. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/pyro.py +0 -0
  128. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/TgWallet/web.py +0 -0
  129. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/__init__.py +0 -0
  130. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/loader.py +0 -0
  131. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client/pm_unifier.py +0 -0
  132. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client.egg-info/SOURCES.txt +0 -0
  133. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client.egg-info/dependency_links.txt +0 -0
  134. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client.egg-info/requires.txt +0 -0
  135. {xync_client-0.0.233 → xync_client-0.0.235.dev3}/xync_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-client
3
- Version: 0.0.233
3
+ Version: 0.0.235.dev3
4
4
  Author-email: Mike Artemiev <mixartemev@gmail.com>
5
5
  Project-URL: Homepage, https://gitlab.com/XyncNet/client
6
6
  Project-URL: Repository, https://gitlab.com/XyncNet/client
@@ -188,4 +188,41 @@ classDef red stroke:#f00
188
188
  - 40: Получить реквизиты для депозита монеты `deposit(amount: int) => bool`
189
189
  - 40N: Получена монета `deposited => amount`
190
190
  - 41: Вывести монету `withdraw(amount: int) => bool`
191
- - 41N: Монета выведена `withdrew => amount`
191
+ - 41N: Монета выведена `withdrew => amount`
192
+
193
+
194
+ ## Общий флоу для всех сущностей
195
+
196
+ ```mermaid
197
+ block-beta
198
+ columns 5
199
+ eOut["eOut"] space xIn["xIn"] space fOut["fOut"]
200
+ space:5
201
+ Exs((("Exs"))) space db[("DataBase")] space Front(("Front"))
202
+ space:5
203
+ eIn["eIn"] space xOut["xOut"] space fIn["fIn"]
204
+ Exs --"A"--> eOut
205
+ eOut --"B"--> xIn
206
+ xIn --"C"--> db
207
+ db --"D"--> xOut
208
+ xOut --"E"--> fIn
209
+ fIn --"F"--> Front
210
+ Front --"G"--> fOut
211
+ fOut --"H"--> xIn
212
+ xOut --"I"--> eIn
213
+ eIn --"J"--> Exs
214
+ ```
215
+
216
+ ```mermaid
217
+ mindmap
218
+ root(transfer)
219
+ order
220
+ ad
221
+ pair_side
222
+ pair
223
+ coin
224
+ cur
225
+ maker
226
+ person
227
+ user
228
+ ```
@@ -8,14 +8,15 @@ from pydantic import BaseModel
8
8
  from tortoise.exceptions import IntegrityError
9
9
  from x_client import df_hdrs
10
10
  from x_client.aiohttp import Client as HttpClient
11
+ from xync_client.details import hot_ad_cond
12
+ from xync_schema.enums import AdStatus, OrderStatus
13
+
11
14
  from xync_client.Abc.PmAgent import PmAgentClient
12
15
 
13
16
  from xync_client.Abc.InAgent import BaseInAgentClient
14
17
 
15
18
  from xync_client.Bybit.etype.order import TakeAdReq
16
19
  from xync_schema import models
17
- from xync_schema.models import OrderStatus, Coin, Cur, Ad, Actor, Agent, MyAd
18
- from xync_schema import xtype
19
20
 
20
21
  from xync_client.Abc.Ex import BaseExClient
21
22
  from xync_client.Abc.xtype import (
@@ -26,14 +27,16 @@ from xync_client.Abc.xtype import (
26
27
  GetAdsReq,
27
28
  BaseCredexsExidsTrait,
28
29
  BaseOrderFull,
30
+ BaseOrder,
31
+ MyAdXOut,
29
32
  )
30
33
 
31
34
 
32
35
  class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
33
- actor: Actor
34
- agent: Agent
36
+ actor: models.Actor
37
+ agent: models.Agent
35
38
  ex_client: BaseExClient
36
- orders: dict[int, tuple[models.Order, xtype.BaseOrder]] = {} # pending
39
+ orders: dict[int, tuple[models.Order, BaseOrder]] = {} # pending
37
40
  pm_clients: dict[int, PmAgentClient] # {pm_id: PmAgentClient}
38
41
  api: HttpClient
39
42
  cred_x2e: dict[int, int] = {}
@@ -44,15 +47,15 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
44
47
 
45
48
  def __init__(
46
49
  self,
47
- agent: Agent, # agent.actor.person.user
50
+ agent: models.Agent, # agent.actor.person.user
48
51
  ex_client: BaseExClient,
49
52
  pm_clients: dict[int, PmAgentClient] = None,
50
53
  headers: dict[str, str] = df_hdrs,
51
54
  cookies: dict[str, str] = None,
52
55
  proxy: models.Proxy = None,
53
56
  ):
54
- self.agent: Agent = agent
55
- self.actor: Actor = agent.actor
57
+ self.agent: models.Agent = agent
58
+ self.actor: models.Actor = agent.actor
56
59
  # self.gmail = agent.actor.person.user.gmail and GmClient(agent.actor.person.user)
57
60
  self.ex_client: BaseExClient = ex_client
58
61
  self.pm_clients: dict[int, PmAgentClient] = defaultdict()
@@ -111,6 +114,11 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
111
114
  await self.load_pending_orders()
112
115
  await self.ads_share()
113
116
 
117
+ async def load_assets(self, coin_ids: list[int] = None) -> dict:
118
+ assets = {cid: await self.get_asset(cid) for cid in coin_ids} if coin_ids else await self.my_assets()
119
+ for cid, amount in assets.items():
120
+ await self.asset_save(cid, amount)
121
+
114
122
  @abstractmethod
115
123
  async def _get_creds(self) -> list[BaseModel]: ...
116
124
 
@@ -180,7 +188,7 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
180
188
  @abstractmethod
181
189
  async def _get_order_full(self, order_exid: int) -> BaseOrderFull: ...
182
190
 
183
- async def get_order_full(self, order_exid: int) -> xtype.BaseOrder:
191
+ async def get_order_full(self, order_exid: int) -> BaseOrder:
184
192
  eorder: BaseOrderFull = await self._get_order_full(order_exid)
185
193
  _, cur_scale, __ = await self.ex_client.x2e_cur(await self.ex_client.e2x_cur(eorder.curex_exid))
186
194
  _, coin_scale = await self.ex_client.x2e_coin(await self.ex_client.e2x_coin(eorder.coinex_exid))
@@ -195,11 +203,11 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
195
203
  amount=int(eorder.amount * 10**cur_scale),
196
204
  quantity=int(eorder.quantity * 10**coin_scale),
197
205
  )
198
- return xtype.BaseOrder.model_validate(border)
206
+ return BaseOrder.model_validate(border)
199
207
 
200
- async def load_order(self, order_exid: int, force_refresh: bool = False) -> tuple[models.Order, xtype.BaseOrder]:
208
+ async def load_order(self, order_exid: int, force_refresh: bool = False) -> tuple[models.Order, BaseOrder]:
201
209
  if not self.orders.get(order_exid) or force_refresh:
202
- order: xtype.BaseOrder = await self.get_order_full(order_exid)
210
+ order: BaseOrder = await self.get_order_full(order_exid)
203
211
  if not (
204
212
  order_db := await models.Order.get_or_none(
205
213
  exid=order_exid, ad__maker__ex=self.actor.ex
@@ -214,7 +222,7 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
214
222
  self.orders[order_exid] = order_db, order
215
223
  return self.orders[order_exid]
216
224
 
217
- async def order_save(self, order: xtype.BaseOrder) -> models.Order:
225
+ async def order_save(self, order: BaseOrder) -> models.Order:
218
226
  order_in = models.Order.validate(order.model_dump())
219
227
  odb, _ = await models.Order.update_or_create(**order_in.df_unq())
220
228
 
@@ -265,10 +273,12 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
265
273
  coin_id=pair.coin_id, cur_id=pair.cur_id, is_sell=bool(taker_side), pm_ids=pm_ids, amount=amt, limit=50
266
274
  )
267
275
  try:
268
- ads: list[Ad] = await self.ex_client.ads(get_ads_req)
276
+ ads: list[BaseAd] = await self.ex_client.ads(get_ads_req)
269
277
  except Exception:
270
278
  await sleep(1)
271
- ads: list[Ad] = await self.ads(coinex, curex, taker_side, pmexs, amt, 50, race.vm_filter, post_pmexs)
279
+ ads: list[BaseAd] = await self.ads(
280
+ coinex, curex, taker_side, pmexs, amt, 50, race.vm_filter, post_pmexs
281
+ )
272
282
 
273
283
  self.overprice_filter(ads, race.ceil * 10**-curex.scale, k) # обрезаем сверху все ads дороже нашего потолка
274
284
 
@@ -289,7 +299,7 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
289
299
  # ]
290
300
  # for plc, ad in enumerate(rads)
291
301
  # ]
292
- mad: Ad = ads.pop(cur_plc)
302
+ mad: BaseAd = ads.pop(cur_plc)
293
303
  # if (
294
304
  # not (lstat := lstat or await race.stats.order_by("-created_at").first())
295
305
  # or lstat.place != cur_plc
@@ -386,50 +396,7 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
386
396
  # logging.warning("Connection failed. Restarting..")
387
397
  await sleep(6)
388
398
 
389
- async def get_books(
390
- self,
391
- coinex: models.CoinEx,
392
- curex: models.CurEx,
393
- pmexs: list[models.PmEx],
394
- amount: int,
395
- post_pmexs: list[models.PmEx] = None,
396
- ) -> tuple[list[Ad], list[Ad]]:
397
- buy: list[Ad] = await self.ads(coinex, curex, False, pmexs, amount, 40, False, post_pmexs)
398
- sell: list[Ad] = await self.ads(coinex, curex, True, pmexs, amount, 30, False, post_pmexs)
399
- return buy, sell
400
-
401
- async def get_spread(
402
- self, bb: list[Ad], sb: list[Ad], perc: float, place: int = 0
403
- ) -> tuple[tuple[float, float], float, int] | None:
404
- if len(bb) and len(sb):
405
- buy_price, sell_price = float(bb[place].price), float(sb[place].price)
406
- half_spread = (buy_price - sell_price) / (buy_price + sell_price)
407
- if half_spread * 2 < perc:
408
- return await self.get_spread(bb, sb, perc, place)
409
- return (buy_price, sell_price), half_spread, place
410
- return None
411
-
412
- async def get_ceils(
413
- self,
414
- coinex: models.CoinEx,
415
- curex: models.CurEx,
416
- pmexs: list[models.PmEx],
417
- min_prof=0.02,
418
- place: int = 0,
419
- amount: int = None,
420
- post_pmexs: set[models.PmEx] = None,
421
- ) -> tuple[float, float]: # todo: refact to Pairex
422
- for pmc_id in {pmx.pm_id for pmx in pmexs} | set(self.pm_clients.keys()):
423
- if ceils := self.pm_clients[pmc_id].get_ceils():
424
- return ceils
425
- bb, sb = await self.get_books(coinex, curex, pmexs, amount, post_pmexs)
426
- perc = list(post_pmexs or pmexs)[0].pm.fee * 0.0001 + min_prof
427
- (bf, sf), _hp, _zplace = await self.get_spread(bb, sb, perc, place)
428
- mdl = (bf + sf) / 2 # middle price
429
- bc, sc = mdl + mdl * (perc / 2), mdl - mdl * (perc / 2)
430
- return bc, sc
431
-
432
- async def mad_upd(self, mad: Ad, attrs: dict, cxids: list[str]):
399
+ async def mad_upd(self, mad: BaseAd, attrs: dict, cxids: list[str]):
433
400
  if not [setattr(mad, k, v) for k, v in attrs.items() if getattr(mad, k) != v]:
434
401
  print(end="v" if mad.side else "^", flush=True)
435
402
  return await sleep(5)
@@ -452,14 +419,14 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
452
419
  # print("-" if mad.side else "+", end=req.price, flush=True)
453
420
  await sleep(60)
454
421
 
455
- def overprice_filter(self, ads: list[Ad], ceil: float, k: Literal[-1, 1]):
422
+ def overprice_filter(self, ads: list[BaseAd], ceil: float, k: Literal[-1, 1]):
456
423
  # вырезаем ads с ценами выше потолка
457
424
  if ads and (ceil - float(ads[0].price)) * k > 0:
458
425
  if int(ads[0].userId) != self.actor.exid:
459
426
  ads.pop(0)
460
427
  self.overprice_filter(ads, ceil, k)
461
428
 
462
- def get_cad(self, ads: list[Ad], ceil: float, k: Literal[-1, 1], target_place: int, cur_plc: int) -> Ad:
429
+ def get_cad(self, ads: list[BaseAd], ceil: float, k: Literal[-1, 1], target_place: int, cur_plc: int) -> BaseAd:
463
430
  if not ads:
464
431
  return None
465
432
  # чью цену будем обгонять, предыдущей или слещующей объявы?
@@ -468,7 +435,7 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
468
435
  if len(ads) <= target_place:
469
436
  logging.error(f"target place {target_place} not found in ads {len(ads)}-lenght list")
470
437
  target_place = len(ads) - 1
471
- cad: Ad = ads[target_place]
438
+ cad: BaseAd = ads[target_place]
472
439
  # а цена обгоняемой объявы не выше нашего потолка?
473
440
  if (float(cad.price) - ceil) * k <= 0:
474
441
  # тогда берем следующую
@@ -480,7 +447,11 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
480
447
  # 0: Получшение ордеров в статусе status, по монете coin, в валюте coin, в направлении is_sell: bool
481
448
  @abstractmethod
482
449
  async def get_orders(
483
- self, status: OrderStatus = OrderStatus.created, coin: Coin = None, cur: Cur = None, is_sell: bool = None
450
+ self,
451
+ status: OrderStatus = OrderStatus.created,
452
+ coin: models.Coin = None,
453
+ cur: models.Cur = None,
454
+ is_sell: bool = None,
484
455
  ) -> list: ...
485
456
 
486
457
  # 1: [T] Запрос на старт сделки
@@ -535,9 +506,9 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
535
506
 
536
507
  # 30: Создание объявления
537
508
  @abstractmethod
538
- async def ad_new(self, ad: BaseAd) -> Ad: ...
509
+ async def ad_new(self, ad: BaseAd) -> int: ...
539
510
 
540
- async def ad_upd(self, xreq: AdUpdReq) -> Ad:
511
+ async def ad_upd(self, xreq) -> int:
541
512
  xreq.credexs = await models.CredEx.filter(
542
513
  ex_id=self.actor.ex_id,
543
514
  cred__pmcur__pm_id__in=xreq.pm_ids,
@@ -545,14 +516,143 @@ class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
545
516
  cred__person_id=self.actor.person_id,
546
517
  ).prefetch_related("cred__pmcur")
547
518
  # xreq.credexs = credexs
519
+
548
520
  ereq = await self.x2e_req_ad_upd(xreq)
549
- return await self._ad_upd(ereq)
521
+ return await self._ad_upd_api(ereq)
550
522
 
551
523
  # 31: Редактирование объявления
552
524
  @abstractmethod
553
- async def _ad_upd(self, ad: BaseAd) -> Ad: ...
525
+ async def _ad_upd_api(self, ad: MyAdXOut) -> int: ...
526
+
527
+ async def ads_fresh(self, cur_id: int = 1, coin_id: int = 1):
528
+ """
529
+ Обновляем/добавляем объявления агента, в зависимости от кол-ва объявлений которое он может размещать
530
+ одновременно `same_dir_ad` (от 1 до 4), и его текущего coin_balance.
531
+ """
532
+ curex = await models.CurEx.get(cur_id=cur_id, ex_id=self.actor.ex_id)
533
+ usdt_bal = (await self.agent.coins_balance())[coin_id]
534
+ xbals = await self.actor.person.user.balances()
535
+ xbal = xbals[cur_id]
536
+ # В идеале, если он может размещать сразу 4 объявления, и имеет баланс не менее 50 минимальных размеров ордера,
537
+ # то постим 4 объявления:
538
+ # 1) с минимальным нижним лимитом размера ордера, и без фильтрации контрагентов
539
+ # 2) с минимальным лимитом, но с фильтрацией контрагентов
540
+ # 3) с 10x мин лимитом, и с фильтрацией (если вообще есть баланс от 10x min)
541
+ # 4) 50x мин лимит, либо весь баланс если он меньше, с фильтрацией (если есть баланс от 20x min)
542
+ if usdt_bal < curex.minimum:
543
+ return
544
+ fltr = dict(
545
+ my_ad__credexs__cred__ovr_pm_id=0,
546
+ my_ad__credexs__cred__pmcur__cur_id=cur_id,
547
+ pair_side__pair__coin_id=coin_id,
548
+ maker_id=self.agent.actor_id,
549
+ )
550
+ prftch = "my_ad__credexs", "pair_side__pair__coin", "pair_side__pair__cur"
554
551
 
555
- async def ads_fresh(self) -> list[MyAd]: ...
552
+ bads = (
553
+ await models.Ad.filter(**fltr, status=AdStatus.active, pair_side__is_sell=False)
554
+ .order_by("min_fiat", "filtered")
555
+ .prefetch_related(*prftch)
556
+ .limit(self.agent.same_dir_ad)
557
+ )
558
+ if not bads:
559
+ return
560
+ bad = await bads.pop(0).to_float(ex_id=self.actor.ex_id)
561
+ if bad.min_fiat != curex.minimum and bad.filtered:
562
+ logging.error(bad.min_fiat) # need debug
563
+ if bad.amount != xbal:
564
+ bad.amount = xbal
565
+ bad.quantity = None
566
+ xma = MyAdXOut.model_validate(bad, from_attributes=True)
567
+ xma.cond_txt = hot_ad_cond
568
+ await self._ad_upd_api(xma)
569
+
570
+ if not bads:
571
+ return
572
+ bad = await bads.pop(0).to_float(ex_id=self.actor.ex_id)
573
+ if bad.min_fiat != curex.minimum and not bad.filtered:
574
+ logging.error(bad.min_fiat) # need debug
575
+ if bad.amount != xbal:
576
+ bad.amount = xbal
577
+ bad.quantity = None
578
+ xma = MyAdXOut.model_validate(bad, from_attributes=True)
579
+ xma.cond_txt = hot_ad_cond
580
+ await self._ad_upd_api(xma)
581
+
582
+ if not bads:
583
+ return
584
+ bad = await bads.pop(0).to_float(ex_id=self.actor.ex_id)
585
+ if curex.minimum * 2 <= bad.min_fiat < curex.minimum * 10 or bad.amount != xbal:
586
+ bad.min_fiat = min(xbal, curex.minimum * 10)
587
+ bad.amount = xbal
588
+ bad.quantity = None
589
+ xma = MyAdXOut.model_validate(bad, from_attributes=True)
590
+ xma.cond_txt = hot_ad_cond
591
+ await self._ad_upd_api(xma)
592
+
593
+ if not bads:
594
+ return
595
+ bad = await bads.pop(0).to_float(ex_id=self.actor.ex_id)
596
+ if curex.minimum * 20 <= bad.min_fiat < curex.minimum * 50 or bad.amount != xbal:
597
+ bad.min_fiat = min(xbal, curex.minimum * 10)
598
+ bad.amount = xbal
599
+ bad.quantity = None
600
+ xma = MyAdXOut.model_validate(bad, from_attributes=True)
601
+ xma.cond_txt = hot_ad_cond
602
+ await self._ad_upd_api(xma)
603
+
604
+ # sell
605
+ sads = (
606
+ await models.Ad.filter(**fltr, status=AdStatus.active, pair_side__is_sell=False)
607
+ .order_by("min_fiat", "filtered")
608
+ .prefetch_related(*prftch)
609
+ .limit(self.agent.same_dir_ad)
610
+ )
611
+ if not sads:
612
+ return
613
+ sad = await sads.pop(0).to_float(ex_id=self.actor.ex_id)
614
+ if sad.min_fiat != curex.minimum and sad.filtered:
615
+ logging.error(sad.min_fiat) # need debug
616
+ if sad.quantity != usdt_bal:
617
+ sad.quantity = usdt_bal
618
+ sad.amount = None
619
+ xma = MyAdXOut.model_validate(sad, from_attributes=True)
620
+ xma.cond_txt = hot_ad_cond
621
+ await self._ad_upd_api(xma)
622
+
623
+ if not sads:
624
+ return
625
+ sad = await sads.pop(0).to_float(ex_id=self.actor.ex_id)
626
+ if sad.min_fiat != curex.minimum and not sad.filtered:
627
+ logging.error(sad.min_fiat) # need debug
628
+ if sad.quantity != usdt_bal:
629
+ sad.quantity = usdt_bal
630
+ sad.amount = None
631
+ xma = MyAdXOut.model_validate(sad, from_attributes=True)
632
+ xma.cond_txt = hot_ad_cond
633
+ await self._ad_upd_api(xma)
634
+
635
+ if not sads:
636
+ return
637
+ sad = await sads.pop(0).to_float(ex_id=self.actor.ex_id)
638
+ if curex.minimum * 2 <= sad.min_fiat < curex.minimum * 10 or sad.quantity != usdt_bal:
639
+ sad.min_fiat = min(usdt_bal, curex.minimum * 10)
640
+ sad.quantity = usdt_bal
641
+ sad.amount = None
642
+ xma = MyAdXOut.model_validate(sad, from_attributes=True)
643
+ xma.cond_txt = hot_ad_cond
644
+ await self._ad_upd_api(xma)
645
+
646
+ if not sads:
647
+ return
648
+ sad = await sads.pop(0).to_float(ex_id=self.actor.ex_id)
649
+ if curex.minimum * 20 <= sad.min_fiat < curex.minimum * 50 or sad.quantity != usdt_bal:
650
+ sad.min_fiat = min(usdt_bal, curex.minimum * 10)
651
+ sad.quantity = usdt_bal
652
+ sad.amount = None
653
+ xma = MyAdXOut.model_validate(sad, from_attributes=True)
654
+ xma.cond_txt = hot_ad_cond
655
+ await self._ad_upd_api(xma)
556
656
 
557
657
  # 32: Удаление
558
658
  @abstractmethod
@@ -4,6 +4,7 @@ from abc import abstractmethod
4
4
  from asyncio import sleep
5
5
  from collections import defaultdict
6
6
  from difflib import SequenceMatcher
7
+ from math import floor
7
8
 
8
9
  from aiohttp import ClientSession, ClientResponse
9
10
  from msgspec import Struct
@@ -72,7 +73,7 @@ class BaseExClient(HttpClient, AdLoader):
72
73
 
73
74
  # 22: Список торгуемых монет (с ограничением по валютам, если есть)
74
75
  @abstractmethod
75
- async def coins(self) -> dict[str, xtype.CoinEx]: # {coin.ticker: coin}
76
+ async def coins(self, p2p_only: bool = False, cks: dict = None) -> dict[str, xtype.CoinEx]: # {coin.ticker: coin}
76
77
  ...
77
78
 
78
79
  # 23: Список пар валюта/монет
@@ -182,16 +183,15 @@ class BaseExClient(HttpClient, AdLoader):
182
183
 
183
184
  # 24: Список объяв по (buy/sell, cur, coin, pm)
184
185
  async def ads(self, xreq: GetAdsReq, **kwargs) -> list[xtype.BaseAd]:
185
- ereq = AdsReq(
186
- coin_id=(await self.x2e_coin(xreq.coin_id))[0],
187
- cur_id=(await self.x2e_cur(xreq.cur_id))[0],
188
- is_sell=str(int(xreq.is_sell)),
189
- pm_ids=[await self.x2e_pm(pid) for pid in xreq.pm_ids],
190
- # size=str(xreq.limit),
191
- # page=str(xreq.page),
186
+ src = xreq.model_copy(
187
+ update=dict(
188
+ coin_id=(await self.x2e_coin(xreq.coin_id))[0],
189
+ cur_id=(await self.x2e_cur(xreq.cur_id))[0],
190
+ is_sell=str(int(xreq.is_sell)),
191
+ pm_ids=xreq.pm_ids and [await self.x2e_pm(pid) for pid in xreq.pm_ids],
192
+ )
192
193
  )
193
- if xreq.amount:
194
- ereq.amount = str(xreq.amount)
194
+ ereq = AdsReq.model_validate(src.model_dump(exclude_none=True))
195
195
  return await self._ads(ereq, **kwargs)
196
196
 
197
197
  @abstractmethod
@@ -332,25 +332,34 @@ class BaseExClient(HttpClient, AdLoader):
332
332
  return file
333
333
 
334
334
  # Импорт монет (с CoinEx-ами) с биржи в бд
335
- async def set_coins(self):
336
- coinexs: dict[str, xtype.CoinEx] = await self.coins()
335
+ async def set_coins(self, p2p_only: bool = True, cks: dict = None):
336
+ coinexs: dict[str, xtype.CoinEx] = await self.coins(p2p_only, cks)
337
337
  coins_db: dict[int, models.Coin] = {
338
338
  c.exid: (
339
- await models.Coin.update_or_create({"scale": c.scale or self.coin_scales[c.ticker]}, ticker=c.ticker)
339
+ await models.Coin.update_or_create(
340
+ {
341
+ "scale": c.scale if c.scale is not None else self.coin_scales[c.ticker],
342
+ "typ": c.typ,
343
+ },
344
+ ticker=c.ticker,
345
+ )
340
346
  )[0]
341
347
  for c in coinexs.values()
342
348
  }
343
349
  coinexs_db: list[models.CoinEx] = [
344
350
  models.CoinEx(
345
- scale=(scl := c.scale or self.coin_scales[c.ticker]),
351
+ scale=(scl := c.scale if c.scale is not None else self.coin_scales[c.ticker]),
346
352
  coin=coins_db[c.exid],
347
353
  ex=self.ex,
348
354
  exid=c.exid,
349
355
  minimum=c.minimum and c.minimum * 10**scl,
356
+ p2p=c.p2p,
350
357
  )
351
358
  for c in coinexs.values()
352
359
  ]
353
- await models.CoinEx.bulk_create(coinexs_db, update_fields=["minimum"], on_conflict=["coin_id", "ex_id"])
360
+ await models.CoinEx.bulk_create(
361
+ coinexs_db, update_fields=["minimum", "scale", "p2p"], on_conflict=["coin_id", "ex_id"]
362
+ )
354
363
  return True
355
364
 
356
365
  # Импорт пар биржи в бд
@@ -463,8 +472,8 @@ class BaseExClient(HttpClient, AdLoader):
463
472
  pms = await models.Pm.filter(pmexs__ex=self.ex, pmexs__exid__in=base_ad.pmex_exids)
464
473
  badd.update(
465
474
  amount=int((base_ad.amount or base_ad.quantity * base_ad.price) * 10**cur_scale),
466
- max_fiat=int(base_ad.max_fiat * 10**cur_scale),
467
- min_fiat=int(base_ad.min_fiat * 10**cur_scale),
475
+ max_fiat=int(base_ad.max_fiat),
476
+ min_fiat=int(base_ad.min_fiat),
468
477
  premium=int(base_ad.premium * 100_00),
469
478
  price=int(base_ad.price * 10**cur_scale),
470
479
  quantity=int((base_ad.quantity or base_ad.amount / base_ad.price) * 10**coin_scale),
@@ -665,6 +674,57 @@ class BaseExClient(HttpClient, AdLoader):
665
674
 
666
675
  self.tree = tree
667
676
 
677
+ async def get_books(
678
+ self,
679
+ coin_id: int,
680
+ cur_id: int,
681
+ pm_ids: list[int] = None,
682
+ amount: int = None,
683
+ post_pms: list[models.Pm] = None,
684
+ ) -> tuple[list[BaseAd], list[BaseAd]]:
685
+ sell_req = GetAdsReq(
686
+ coin_id=coin_id, cur_id=cur_id, is_sell=True, pm_ids=pm_ids, amount=amount, vm_only=True, limit=10
687
+ )
688
+ buy_req = GetAdsReq(
689
+ coin_id=coin_id, cur_id=cur_id, is_sell=False, pm_ids=pm_ids, amount=amount, vm_only=True, limit=10
690
+ )
691
+ return await self.ads(sell_req, post_pms=post_pms), await self.ads(buy_req, post_pms=post_pms)
692
+
693
+ async def get_spread(
694
+ self, bb: list[models.Ad], sb: list[models.Ad], perc: float, place: int = 0
695
+ ) -> tuple[tuple[float, float], float, int] | None:
696
+ if len(bb) and len(sb):
697
+ buy_price, sell_price = float(bb[place].price), float(sb[place].price)
698
+ half_spread = (buy_price - sell_price) / (buy_price + sell_price)
699
+ if half_spread * 2 < perc:
700
+ return await self.get_spread(bb, sb, perc, place)
701
+ return (buy_price, sell_price), half_spread, place
702
+ return None
703
+
704
+ async def get_ceils(
705
+ self,
706
+ coinex: models.CoinEx,
707
+ curex: models.CurEx,
708
+ pmexs: list[models.PmEx],
709
+ min_prof=0.02,
710
+ place: int = 0,
711
+ amount: int = None,
712
+ post_pms: set[models.PmEx] = None,
713
+ ) -> tuple[float, float]: # todo: refact to Pairex
714
+ for pmc_id in {pmx.pm_id for pmx in pmexs} | set(self.pm_clients.keys()):
715
+ if ceils := self.pm_clients[pmc_id].get_ceils():
716
+ return ceils
717
+ bb, sb = await self.get_books(coinex, curex, pmexs, amount, post_pms)
718
+ perc = list(post_pms or pmexs)[0].pm.fee * 0.0001 + min_prof
719
+ (bf, sf), _hp, _zplace = await self.get_spread(bb, sb, perc, place)
720
+ mdl = (bf + sf) / 2 # middle price
721
+ bc, sc = mdl + mdl * (perc / 2), mdl - mdl * (perc / 2)
722
+ return bc, sc
723
+
724
+ async def rate(self, cur_id: int, coin_id: int = 1) -> int:
725
+ bp, sp = await self.get_books(cur_id, coin_id)
726
+ return floor((sum(b.price for b in bp[5:]) + sum(s.price for s in sp[5:])) * 0.1)
727
+
668
728
  async def init_seed(self, fbot: FileClient):
669
729
  await self.set_curs()
670
730
  await self.set_coins()