xync-client 0.0.25.dev40__tar.gz → 0.0.25.dev41__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 (83) hide show
  1. {xync_client-0.0.25.dev40/xync_client.egg-info → xync_client-0.0.25.dev41}/PKG-INFO +1 -1
  2. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/TestAgent.py +12 -1
  3. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/TestEx.py +21 -22
  4. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Abc/Ex.py +24 -31
  5. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Htx/agent.py +3 -2
  6. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Htx/etype/ad.py +4 -3
  7. xync_client-0.0.25.dev41/xync_client/Htx/ex.py +157 -0
  8. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/ex.py +17 -16
  9. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41/xync_client.egg-info}/PKG-INFO +1 -1
  10. xync_client-0.0.25.dev40/xync_client/Htx/ex.py +0 -90
  11. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/.env.sample +0 -0
  12. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/.gitignore +0 -0
  13. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/.pre-commit-config.yaml +0 -0
  14. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/README.md +0 -0
  15. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/makefile +0 -0
  16. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/pyproject.toml +0 -0
  17. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/setup.cfg +0 -0
  18. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/TestAsset.py +0 -0
  19. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/TestOrder.py +0 -0
  20. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/Binance/test_binance.py +0 -0
  21. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
  22. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
  23. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/Gate/test_gate.py +0 -0
  24. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/Htx/test_htx_p2p.py +0 -0
  25. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/Wallet/test_agent.py +0 -0
  26. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/Wallet/test_ex.py +0 -0
  27. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/__init__.py +0 -0
  28. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/tests/_todo_refact/_test_ex.py +0 -0
  29. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Abc/Agent.py +0 -0
  30. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Abc/Asset.py +0 -0
  31. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Abc/AuthTrait.py +0 -0
  32. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Abc/Base.py +0 -0
  33. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Abc/BaseTest.py +0 -0
  34. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Abc/InAgent.py +0 -0
  35. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Abc/Order.py +0 -0
  36. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Binance/__init__.py +0 -0
  37. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Binance/binance_async.py +0 -0
  38. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Binance/earn_api.py +0 -0
  39. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Binance/ex.py +0 -0
  40. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Binance/exceptions.py +0 -0
  41. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Binance/sapi.py +0 -0
  42. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Binance/web_c2c.py +0 -0
  43. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BingX/__init__.py +0 -0
  44. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BingX/agent.py +0 -0
  45. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BingX/base.py +0 -0
  46. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BingX/ex.py +0 -0
  47. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BingX/pyd.py +0 -0
  48. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BingX/req.mjs +0 -0
  49. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BingX/sign.js +0 -0
  50. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BingX/test/main.py +0 -0
  51. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BitGet/__init__.py +0 -0
  52. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BitGet/agent.py +0 -0
  53. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BitGet/ex.py +0 -0
  54. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/BitGet/req.mjs +0 -0
  55. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Bybit/agent.py +0 -0
  56. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Bybit/ex.py +0 -0
  57. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Bybit/web_earn.py +0 -0
  58. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Bybit/web_p2p.py +0 -0
  59. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Gate/ex.py +0 -0
  60. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Gate/premarket.py +0 -0
  61. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Htx/earn.py +0 -0
  62. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Htx/etype/__init__.py +0 -0
  63. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Htx/etype/cred.py +0 -0
  64. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Htx/etype/pm.py +0 -0
  65. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/KuCoin/pub.py +0 -0
  66. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/KuCoin/web.py +0 -0
  67. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Okx/ex.py +0 -0
  68. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/Okx/pyd.py +0 -0
  69. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/agent.py +0 -0
  70. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/asset.py +0 -0
  71. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/auth.py +0 -0
  72. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/inAgent.py +0 -0
  73. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/order.py +0 -0
  74. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/pyd.py +0 -0
  75. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/pyro.py +0 -0
  76. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/TgWallet/web.py +0 -0
  77. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/__init__.py +0 -0
  78. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/loader.py +0 -0
  79. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client/pyro.py +0 -0
  80. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client.egg-info/SOURCES.txt +0 -0
  81. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client.egg-info/dependency_links.txt +0 -0
  82. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client.egg-info/requires.txt +0 -0
  83. {xync_client-0.0.25.dev40 → xync_client-0.0.25.dev41}/xync_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: xync-client
3
- Version: 0.0.25.dev40
3
+ Version: 0.0.25.dev41
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
@@ -3,7 +3,7 @@ import logging
3
3
  import pytest
4
4
  from xync_schema.enums import ExStatus, ExAction
5
5
  from xync_schema.models import Ex, TestEx, Fiat, Ad, Coin, Cur
6
- from xync_schema.pydantic import FiatNew
6
+ from xync_schema.types import FiatNew, BaseAd
7
7
 
8
8
  from xync_client.Abc.Base import DictOfDicts, ListOfDicts
9
9
  from xync_client.Abc.BaseTest import BaseTest
@@ -25,6 +25,17 @@ class TestAgent(BaseTest):
25
25
  yield clients
26
26
  [(await taker.close(), await maker.close()) for taker, maker in clients]
27
27
 
28
+ # 42
29
+ async def test_ad(self, clients: list[BaseExClient]):
30
+ for client in clients:
31
+ if not self.ad.get(client.ex.id):
32
+ await self.test_ads(clients)
33
+ ad: BaseAd = await client.ad(self.ad[client.ex.id].id)
34
+ ok = isinstance(ad, BaseAd)
35
+ t, _ = await TestEx.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.ad)
36
+ assert t.ok, "No ad"
37
+ logging.info(f"{client.ex.name}: {ExAction.ad.name} - ok")
38
+
28
39
  # 0
29
40
  async def test_get_orders(self, clients: list[BaseAgentClient]):
30
41
  for taker, maker in clients:
@@ -1,19 +1,21 @@
1
1
  import logging
2
2
 
3
3
  import pytest
4
- from xync_schema.pydantic import PmPyd, BaseAd, CurEpyd, CoinEpyd
4
+ from xync_schema.types import Pm, BaseAd, CurE, CoinE
5
5
 
6
6
  from xync_client.Abc.BaseTest import BaseTest
7
7
  from xync_schema.enums import ExStatus, ExType, ExAction
8
8
  from xync_schema.models import Ex, TestEx as ExTest
9
9
 
10
- from xync_client.Abc.Base import BaseClient, FlatDict, MapOfIdsList
10
+ from xync_client.Abc.Base import BaseClient, MapOfIdsList
11
11
  from xync_client.Abc.Ex import BaseExClient
12
12
 
13
13
 
14
14
  @pytest.mark.asyncio(loop_scope="session")
15
15
  class TestEx(BaseTest):
16
- ad: dict[str, BaseAd] = {}
16
+ coins: dict[int, dict[str, CoinE]] = {}
17
+ curs: dict[int, dict[str, CurE]] = {}
18
+ ad: dict[int, BaseAd] = {}
17
19
 
18
20
  @pytest.fixture
19
21
  async def clients(self) -> list[BaseClient]:
@@ -26,17 +28,18 @@ class TestEx(BaseTest):
26
28
  # 19
27
29
  async def test_curs(self, clients: list[BaseExClient]):
28
30
  for client in clients:
29
- curs: list[CurEpyd] = await client.curs()
30
- ok = self.is_list_of_objects(curs, CurEpyd)
31
+ curs: dict[str, CurE] = await client.curs()
32
+ ok = self.is_dict_of_objects(curs, CurE)
31
33
  t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.curs)
32
34
  assert t.ok, "No curs"
35
+ self.curs[client.ex.id] = curs
33
36
  logging.info(f"{client.ex.name}: {ExAction.curs.name} - ok")
34
37
 
35
38
  # 20
36
39
  async def test_pms(self, clients: list[BaseExClient]):
37
40
  for client in clients:
38
- pms: dict[int | str, PmPyd] = await client.pms()
39
- ok = self.is_dict_of_objects(pms, PmPyd)
41
+ pms: dict[int | str, Pm] = await client.pms()
42
+ ok = self.is_dict_of_objects(pms, Pm)
40
43
  t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.pms)
41
44
  assert t.ok, "No pms"
42
45
  logging.info(f"{client.ex.name}: {ExAction.pms.name} - ok")
@@ -53,17 +56,18 @@ class TestEx(BaseTest):
53
56
  # 22
54
57
  async def test_coins(self, clients: list[BaseExClient]):
55
58
  for client in clients:
56
- coins: FlatDict = await client.coins()
57
- ok = self.is_list_of_objects(coins, CoinEpyd)
59
+ coins: dict[str, CoinE] = await client.coins()
60
+ ok = self.is_dict_of_objects(coins, CoinE)
58
61
  t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.coins)
59
62
  assert t.ok, "No coins"
63
+ self.coins[client.ex.id] = coins
60
64
  logging.info(f"{client.ex.name}: {ExAction.coins.name} - ok")
61
65
 
62
66
  # 23
63
67
  async def test_pairs(self, clients: list[BaseExClient]):
64
68
  for client in clients:
65
- pairs: MapOfIdsList = await client.pairs()
66
- ok = self.is_map_of_ids(pairs)
69
+ pairs_buy, pairs_sell = await client.pairs()
70
+ ok = self.is_map_of_ids(pairs_buy) and self.is_map_of_ids(pairs_sell)
67
71
  t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.pairs)
68
72
  assert t.ok, "No coins"
69
73
  logging.info(f"{client.ex.name}: {ExAction.pairs.name} - ok")
@@ -71,18 +75,13 @@ class TestEx(BaseTest):
71
75
  # 24
72
76
  async def test_ads(self, clients: list[BaseExClient]):
73
77
  for client in clients:
74
- ads: list[BaseAd] = await client.ads("USDT", "USD", False)
78
+ self.coins[client.ex.id] = self.coins.get(client.ex.id) or await client.coins()
79
+ self.curs[client.ex.id] = self.curs.get(client.ex.id) or await client.curs()
80
+ coin: int | str = self.coins[client.ex.id]["USDT"].exid
81
+ cur: int | str = self.curs[client.ex.id]["EUR"].exid
82
+ ads: list[BaseAd] = await client.ads(coin, cur, False)
75
83
  ok = self.is_list_of_objects(ads, BaseAd)
76
84
  t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.ads)
77
85
  assert t.ok, "No ads"
78
- self.ad[client.ex.name] = ads[0] # for further use in test_ad
86
+ self.ad[client.ex.id] = ads[0] # for further use in test_ad
79
87
  logging.info(f"{client.ex.name}: {ExAction.ads.name} - ok")
80
-
81
- # 42
82
- async def test_ad(self, clients: list[BaseExClient]):
83
- for client in clients:
84
- ad: BaseAd = await client.ad(self.ad.get(client.ex.name, (await client.ads("USDT", "RUB", False))[0]).id)
85
- ok = isinstance(ad, BaseAd)
86
- t, _ = await ExTest.update_or_create({"ok": ok}, ex=client.ex, action=ExAction.ad)
87
- assert t.ok, "No ad"
88
- logging.info(f"{client.ex.name}: {ExAction.ad.name} - ok")
@@ -38,7 +38,7 @@ class PmUnifier:
38
38
  # "GTB Bank (Guarantee Trust Bank)": "GTBank",
39
39
  }
40
40
  re_bank = [
41
- r"^bank (?!of )| bank$",
41
+ r"^bank (?!of )|bank$",
42
42
  r" banka$",
43
43
  r" bankas$",
44
44
  r" bankası$",
@@ -81,14 +81,11 @@ class PmUnifier:
81
81
  self.cts = countries
82
82
 
83
83
  def countries(self, name: str):
84
- cmap = {
85
- "kazakstan": "kazakhstan",
86
- }
87
84
  for ct in self.cts:
88
85
  # Если имя кончается на "Название_страны" в скобках
89
86
  if (
90
87
  ct
91
- and self.pms[name].norm.endswith((ct + ")", cmap.get(ct, ct)))
88
+ and self.pms[name].norm.endswith((ct + ")", ct))
92
89
  and not self.pms[name].norm.endswith((" of " + ct, " of the " + ct, " and " + ct, " de " + ct))
93
90
  ):
94
91
  self.pms[name].norm = self.pms[name].norm.replace(ct, "")
@@ -130,7 +127,7 @@ class PmUnifier:
130
127
  ).upper()
131
128
  if len(acr) >= 2 and (
132
129
  f"({acr})" in self.pms[name].norm
133
- or self.pms[name].norm.startswith(acr + " ")
130
+ or self.pms[name].norm.startswith((acr + " ", acr + ":"))
134
131
  or self.pms[name].norm.endswith(" " + acr)
135
132
  ):
136
133
  self.pms[name].norm = self.pms[name].norm.replace(acr, "", 1).replace("()", "", 1).strip()
@@ -146,7 +143,7 @@ class PmUnifier:
146
143
  self.pms[name].norm = self.pms[name].norm.replace(src, trgt)
147
144
 
148
145
  def clear(self, name: str):
149
- self.pms[name].norm = self.pms[name].norm.replace("()", "").replace(" ", " ").strip(" -")
146
+ self.pms[name].norm = self.pms[name].norm.replace("()", "").replace(" ", " ").strip(" -:")
150
147
 
151
148
  def __call__(self, s: str) -> PmUni:
152
149
  # если в словаре замен есть текущее назвние - меняем, иначе берем строку до запятой
@@ -179,7 +176,7 @@ class BaseExClient(BaseClient):
179
176
 
180
177
  # 19: Список поддерживаемых валют тейкера
181
178
  @abstractmethod
182
- async def curs(self) -> list[types.CurE]: # {cur.exid: cur.ticker}
179
+ async def curs(self) -> dict[str, types.CurE]: # {cur.ticker: cur}
183
180
  ...
184
181
 
185
182
  # 20: Список платежных методов
@@ -194,12 +191,12 @@ class BaseExClient(BaseClient):
194
191
 
195
192
  # 22: Список торгуемых монет (с ограничениям по валютам, если есть)
196
193
  @abstractmethod
197
- async def coins(self) -> list[types.CoinE]: # {coin.exid: coin.ticker}
194
+ async def coins(self) -> dict[str, types.CoinE]: # {coin.ticker: coin}
198
195
  ...
199
196
 
200
197
  # 23: Список пар валюта/монет
201
198
  @abstractmethod
202
- async def pairs(self) -> MapOfIdsList: ...
199
+ async def pairs(self) -> tuple[MapOfIdsList, MapOfIdsList]: ...
203
200
 
204
201
  # 24: Список объяв по (buy/sell, cur, coin, pm)
205
202
  @abstractmethod
@@ -217,20 +214,18 @@ class BaseExClient(BaseClient):
217
214
  async def ad_epyd2pydin(self, ad: types.BaseAd) -> types.BaseAdIn: ... # my_uid: for MyAd
218
215
 
219
216
  # 99: Страны
220
- @abstractmethod
221
- async def countries(self) -> list[Struct]: ...
217
+ async def countries(self) -> list[Struct]:
218
+ return []
222
219
 
223
220
  # Импорт Pm-ов (с Pmcur-, Pmex- и Pmcurex-ами) и валют (с Curex-ами) с биржи в бд
224
221
  async def set_pmcurexs(self):
225
222
  # Curs
226
- cur_pyds: list[types.CurE] = await self.curs()
223
+ cur_pyds: dict[str, types.CurE] = await self.curs()
227
224
  curs: dict[int | str, models.Cur] = {
228
- cur_pyd.exid: (
229
- await models.Cur.update_or_create({"rate": cur_pyd.rate}, ticker=cur_pyd.ticker, id=cur_pyd.exid)
230
- )[0]
231
- for cur_pyd in cur_pyds
225
+ cur_pyd.exid: (await models.Cur.update_or_create({"rate": cur_pyd.rate}, ticker=tkr))[0]
226
+ for tkr, cur_pyd in cur_pyds.items()
232
227
  }
233
- curexs: list[models.Curex] = [models.Curex(**c.model_dump(), cur=curs[c.exid], ex=self.ex) for c in cur_pyds]
228
+ curexs = [models.Curex(**c.model_dump(), cur=curs[c.exid], ex=self.ex) for c in cur_pyds.values()]
234
229
  # Curex
235
230
  await models.Curex.bulk_create(
236
231
  curexs, update_fields=["minimum", "rounding_scale"], on_conflict=["cur_id", "ex_id"]
@@ -288,10 +283,11 @@ class BaseExClient(BaseClient):
288
283
  uni = PmUnifier(cntrs)
289
284
  for k, pm in pms_epyds.items():
290
285
  pmu: PmUni = uni(pm.name)
286
+ country = await models.Country.get(name__iexact=cnt) if (cnt := pmu.country) else None
291
287
  if prev[2] == pm.name and pmu.country == prev[3]: # оригинальное имя не уникально на этой бирже
292
288
  logging.warning(f"Pm: '{pm.name}' duplicated with ids {prev[0]}: {k} on {self.ex.name}")
293
289
  # новый Pm не добавляем, а берем старый с этим названием
294
- pm_ = pms.get(prev[0], await models.Pm.get_or_none(norm=prev[1]))
290
+ pm_ = pms.get(prev[0], await models.Pm.get_or_none(norm=prev[1], country=country))
295
291
  # и добавляем Pmex для него
296
292
  await models.Pmex.update_or_create({"name": pm.name}, ex=self.ex, exid=k, pm=pm_)
297
293
  elif (
@@ -302,19 +298,16 @@ class BaseExClient(BaseClient):
302
298
  )
303
299
  # новый Pm не добавляем, только Pmex для него
304
300
  # новый Pm не добавляем, а берем старый с этим названием
305
- pm_ = pms.get(prev[0], await models.Pm.get_or_none(norm=prev[1]))
301
+ pm_ = pms.get(prev[0], await models.Pm.get_or_none(norm=prev[1], country=country))
306
302
  # и добавляем.обновляем Pmex для него
307
303
  await models.Pmex.update_or_create({"pm": pm_}, ex=self.ex, exid=k, name=pm.name)
308
304
  else:
309
305
  # todo: add logo and all other
310
- d = pmu.df_unq()
311
- d.update(
312
- {
313
- "country": await models.Country.get(name__iexact=cnt) if (cnt := d.get("country")) else None,
314
- "logo": pm.logo,
315
- "type_": pm.type_,
316
- }
317
- )
306
+ d = {**pmu.df_unq(), "country": country}
307
+ if pm.logo:
308
+ d["defaults"]["logo"] = pm.logo
309
+ if pm.type_:
310
+ d["defaults"]["type_"] = pm.type_
318
311
  try:
319
312
  pms[k], _ = await models.Pm.update_or_create(**d)
320
313
  except (MultipleObjectsReturned, IntegrityError) as e:
@@ -350,12 +343,12 @@ class BaseExClient(BaseClient):
350
343
 
351
344
  # Импорт монет (с Coinex-ами) с биржи в бд
352
345
  async def set_coinexs(self):
353
- coins: list[types.CoinE] = await self.coins()
346
+ coins: dict[str, types.CoinE] = await self.coins()
354
347
  coins_db: dict[int, models.Coin] = {
355
- c.exid: (await models.Coin.update_or_create(ticker=c.ticker))[0] for c in coins
348
+ c.exid: (await models.Coin.update_or_create(ticker=c.ticker))[0] for c in coins.values()
356
349
  }
357
350
  coinexs: list[models.Coinex] = [
358
- models.Coinex(coin=coins_db[c.exid], ex=self.ex, exid=c.exid, minimum=c.minimum) for c in coins
351
+ models.Coinex(coin=coins_db[c.exid], ex=self.ex, exid=c.exid, minimum=c.minimum) for c in coins.values()
359
352
  ]
360
353
  await models.Coinex.bulk_create(coinexs, update_fields=["minimum"], on_conflict=["coin_id", "ex_id"])
361
354
 
@@ -83,8 +83,9 @@ class Private(BaseAgentClient):
83
83
  async def ads_switch(self) -> bool:
84
84
  pass
85
85
 
86
- async def get_user(self, user_id) -> dict:
87
- pass
86
+ async def get_user(self, user_id: int) -> dict:
87
+ user = (await self._get(f"/-/x/otc/v1/user/{user_id}/info"))["data"]
88
+ return user
88
89
 
89
90
  async def send_user_msg(self, msg: str, file=None) -> bool:
90
91
  pass
@@ -1,6 +1,7 @@
1
1
  from typing import Literal
2
2
 
3
3
  from pydantic import BaseModel
4
+ from xync_schema.types import BaseAd
4
5
 
5
6
 
6
7
  class TradeRule(BaseModel):
@@ -56,7 +57,7 @@ class PayName(BaseModel):
56
57
  id: int
57
58
 
58
59
 
59
- class Resp(BaseModel):
60
+ class Resp(BaseAd):
60
61
  blockType: int
61
62
  chargeType: bool
62
63
  coinId: int
@@ -71,12 +72,12 @@ class Resp(BaseModel):
71
72
  labelName: str | None = None
72
73
  maxTradeLimit: str
73
74
  merchantLevel: int
74
- merchantTags: str | None = None
75
+ merchantTags: list[str] | None
75
76
  minTradeLimit: str
76
77
  orderCompleteRate: str
77
78
  payMethod: str
78
79
  payMethods: list[PayMethod]
79
- payName: list[PayName] # приходит массив объектов внутри строки
80
+ payName: str # list[PayName] # приходит массив объектов внутри строки
80
81
  payTerm: int
81
82
  price: str
82
83
  seaViewRoom: str | None = None
@@ -0,0 +1,157 @@
1
+ from asyncio import run
2
+
3
+ from msgspec import convert
4
+ from x_model import init_db
5
+ from xync_schema import models, types
6
+ from xync_schema.models import Ex, Cur
7
+ from xync_schema.enums import PmType
8
+
9
+ from xync_client.Abc.Base import MapOfIdsList
10
+ from xync_client.Abc.Ex import BaseExClient
11
+ from xync_client.Htx.etype import pm, Country, ad
12
+ from xync_client.loader import PG_DSN
13
+
14
+
15
+ class ExClient(BaseExClient):
16
+ def pm_type_map(self, type_: models.Pmex) -> str:
17
+ pass
18
+
19
+ async def pairs(self) -> tuple[MapOfIdsList, MapOfIdsList]:
20
+ self.session.headers["client-type"] = "web"
21
+ reb = (await self._get("/-/x/otc/v1/trade/fast/config/list?side=buy&tradeMode=c2c_simple"))["data"]
22
+ res = (await self._get("/-/x/otc/v1/trade/fast/config/list?side=sell&tradeMode=c2c_simple"))["data"]
23
+ coins = await self.coins()
24
+ curs = await self.curs()
25
+ b = {
26
+ cur.exid: cns
27
+ for tkr, cur in curs.items()
28
+ if (
29
+ cns := set(
30
+ coins[c["cryptoAsset"]["name"]].exid for c in reb if tkr in [q["name"] for q in c["quoteAsset"]]
31
+ )
32
+ )
33
+ }
34
+ s = {
35
+ cur.exid: cns
36
+ for tkr, cur in curs.items()
37
+ if (
38
+ cns := set(
39
+ coins[c["cryptoAsset"]["name"]].exid for c in res if tkr in [q["name"] for q in c["quoteAsset"]]
40
+ )
41
+ )
42
+ }
43
+ return b, s
44
+
45
+ async def ads(
46
+ self, coin_exid: int, cur_exid: int, is_sell: bool, pm_exids: list[str] = None, amount: int = None
47
+ ) -> list[types.BaseAd]:
48
+ params = {
49
+ "coinId": coin_exid,
50
+ "currency": cur_exid,
51
+ "tradeType": "sell" if is_sell else "buy",
52
+ "currPage": 1,
53
+ "payMethod": ",".join(pm_exids) if pm_exids else 0,
54
+ "acceptOrder": 0,
55
+ "blockType": "general",
56
+ "online": 1,
57
+ "range": 0,
58
+ "amount": amount or "",
59
+ "onlyTradable": "false",
60
+ "isFollowed": "false",
61
+ }
62
+ res = (await self._get("/-/x/otc/v1/data/trade-market", params))["data"]
63
+ ads = [ad.Resp(**a) for a in res]
64
+ return ads
65
+
66
+ async def ad(self, ad_id: int) -> types.BaseAd:
67
+ pass
68
+
69
+ # 20: Get all pms
70
+ async def pms(self, _cur: Cur = None) -> dict[int, types.Pm]:
71
+ dist = {
72
+ 0: PmType.card,
73
+ 1: PmType.bank,
74
+ 2: PmType.cash,
75
+ 3: PmType.emoney,
76
+ 4: PmType.emoney,
77
+ 5: PmType.IFSC,
78
+ }
79
+
80
+ pms: list[pm.Resp] = [convert(p, pm.Resp) for p in (await self._coin_curs_pms())["payMethod"]]
81
+
82
+ pmsd = {
83
+ p.payMethodId: types.Pm(
84
+ name=p.name,
85
+ type_=dist.get(p.template),
86
+ logo=p.bankImage or p.bankImageWeb,
87
+ )
88
+ for p in pms
89
+ }
90
+
91
+ return pmsd
92
+
93
+ # 21: Get all: currency,pay,allCountry,coin
94
+ async def curs(self) -> dict[str, types.CurE]:
95
+ self.session.headers["client-type"] = "web"
96
+ curs: list[dict] = (await self._coin_curs_pms())["currency"]
97
+ cmap: dict[str, int] = {c["nameShort"]: c["currencyId"] for c in curs}
98
+ res = (await self._get("/-/x/otc/v1/trade/fast/config/list?side=sell&tradeMode=c2c_simple"))["data"]
99
+ cursd: dict[str, float] = {}
100
+ for c in res:
101
+ for q in c["quoteAsset"]:
102
+ cursd[q["name"]] = max(cursd.get(q["name"], 0), float(q["minAmount"]))
103
+ return {tkr: types.CurE(exid=exid, ticker=tkr, minimum=cursd.get(tkr)) for tkr, exid in cmap.items()}
104
+
105
+ # 22: Список платежных методов по каждой валюте
106
+ async def cur_pms_map(self) -> dict[int, set[int]]:
107
+ res = await self._coin_curs_pms()
108
+ wrong_pms = {4, 34, 498, 548, 20009, 20010} # , 212, 239, 363 # these ids not exist in pms
109
+ return {c["currencyId"]: set(c["supportPayments"]) - wrong_pms for c in res["currency"] if c["supportPayments"]}
110
+
111
+ # 23: Список торгуемых монет
112
+ async def coins(self) -> dict[str, types.CoinE]:
113
+ self.session.headers["client-type"] = "web"
114
+ coins: list[dict] = (await self._coin_curs_pms())["coin"]
115
+ cmap: dict[str, int] = {c["coinCode"]: c["coinId"] for c in coins}
116
+ res = (await self._get("/-/x/otc/v1/trade/fast/config/list?side=buy&tradeMode=c2c_simple"))["data"]
117
+ coinsl: list[str] = [c["cryptoAsset"]["name"] for c in res]
118
+ return {tkr: types.CoinE(exid=cid, ticker=tkr, p2p=tkr in coinsl) for tkr, cid in cmap.items()}
119
+
120
+ # 99: Страны
121
+ async def countries(self) -> list[Country]:
122
+ cmap = {
123
+ "Kazakstan": "Kazakhstan",
124
+ }
125
+ res = await self._coin_curs_pms()
126
+ cts = [
127
+ Country(
128
+ id=c["countryId"],
129
+ code=c["code"],
130
+ name=cmap.get(ct := name[:-1] if (name := c["name"].split(",")[0]).endswith(".") else name, ct),
131
+ short=c["appShort"],
132
+ cur_id=c["currencyId"],
133
+ )
134
+ for c in res["country"]
135
+ ]
136
+ return cts
137
+
138
+ # Get all: currency,pay,allCountry,coin
139
+ async def _coin_curs_pms(self) -> (dict, dict, dict, dict):
140
+ res = (await self._get("/-/x/otc/v1/data/config-list?type=currency,pay,coin,allCountry"))["data"]
141
+ res["currency"][0]["currencyId"] = 1
142
+ [c.update({"currencyId": 1, "name": ""}) for c in res["country"] if c["currencyId"] == 172]
143
+ return res
144
+
145
+
146
+ async def main():
147
+ _ = await init_db(PG_DSN, models, True)
148
+ ex = await Ex.get(name="Htx")
149
+ cl = ExClient(ex)
150
+ await cl.pairs()
151
+ await cl.set_pmcurexs()
152
+ await cl.set_coinexs()
153
+ await cl.close()
154
+
155
+
156
+ if __name__ == "__main__":
157
+ run(main())
@@ -32,17 +32,17 @@ class ExClient(BaseExClient, AuthClient):
32
32
  return settings["data"]
33
33
 
34
34
  # 19: Список поддерживаемых валют тейкера
35
- async def curs(self) -> list[types.CurE]:
35
+ async def curs(self) -> dict[str, types.CurE]:
36
36
  coins_curs = await self._post("/p2p/public-api/v2/currency/all-supported")
37
37
  stg = await self._settings()
38
38
  roundings: dict[str, int] = stg["offerSettings"]["roundingScaleByFiatCurrency"]
39
39
  minimums: dict[str, str] = stg["offerSettings"]["minOrderAmountByCurrencyCode"]
40
- return [
41
- types.CurE(
40
+ return {
41
+ c["code"]: types.CurE(
42
42
  exid=c["code"], ticker=c["code"], rounding_scale=roundings.get(c["code"]), minimum=minimums[c["code"]]
43
43
  )
44
44
  for c in coins_curs["data"]["fiat"]
45
- ]
45
+ }
46
46
 
47
47
  async def _pms(self, cur: str) -> dict[str, PmEpydRoot]:
48
48
  pms = await self._post("/p2p/public-api/v3/payment-details/get-methods/by-currency-code", {"currencyCode": cur})
@@ -54,7 +54,7 @@ class ExClient(BaseExClient, AuthClient):
54
54
  if cur:
55
55
  pms = await self._pms(cur)
56
56
  else:
57
- for cur in await self.curs():
57
+ for cur in (await self.curs()).values():
58
58
  pms |= await self._pms(cur.exid)
59
59
  return {
60
60
  k: types.Pm(
@@ -67,25 +67,25 @@ class ExClient(BaseExClient, AuthClient):
67
67
 
68
68
  # 21: Список платежных методов по каждой валюте
69
69
  async def cur_pms_map(self) -> MapOfIdsList:
70
- return {cur.exid: list(await self._pms(cur.exid)) for cur in await self.curs()}
70
+ return {cur.exid: list(await self._pms(cur.exid)) for cur in (await self.curs()).values()}
71
71
 
72
72
  # 22: Список торгуемых монет (с ограничениям по валютам, если есть)
73
- async def coins(self) -> list[types.CoinE]:
73
+ async def coins(self) -> dict[str, types.CoinE]:
74
74
  coins_curs = await self._post("/p2p/public-api/v2/currency/all-supported")
75
75
  stg = await self._settings()
76
76
  lims = list(stg["offerSettings"]["offerVolumeLimitsPerMarket"].values())
77
77
  coins = {k: max(float(v[k]["minInclusive"]) for v in lims) for k, v in lims[0].items()}
78
- return [
79
- types.CoinE(exid=c["code"], ticker=c["code"], minimum=coins[c["code"]])
78
+ return {
79
+ c["code"]: types.CoinE(exid=c["code"], ticker=c["code"], minimum=coins[c["code"]])
80
80
  for c in coins_curs["data"]["crypto"]
81
- ]
81
+ }
82
82
 
83
83
  # 23: Список пар валюта/монет
84
- async def pairs(self) -> MapOfIdsList:
84
+ async def pairs(self) -> tuple[MapOfIdsList, MapOfIdsList]:
85
85
  coins = await self.coins()
86
86
  curs = await self.curs()
87
- pairs = {cur.exid: set(c.exid for c in coins) for cur in curs}
88
- return pairs
87
+ pairs = {cur.exid: set(c.exid for c in coins.values()) for cur in curs.values()}
88
+ return pairs, pairs
89
89
 
90
90
  # 42: Чужая объява по id
91
91
  async def ad(self, ad_id: int) -> _TakerOne:
@@ -126,9 +126,10 @@ class ExClient(BaseExClient, AuthClient):
126
126
 
127
127
  async def ad_taker_epyd2pydin(self, ad: _TakerOne) -> types.AdBuyIn:
128
128
  adx: types.BaseAdIn = await self.ad_common_epyd2pydin(ad)
129
- adx.maker = (await models.Actor.update_or_create({"name": ad.user.nickname}, ex=self.ex, exid=ad.user.userId))[
130
- 0
131
- ]
129
+ act_unq = dict(ex=self.ex, exid=ad.user.userId)
130
+ if not (actor := await models.Actor.get_or_none(**act_unq)):
131
+ actor = await models.Actor.create(**act_unq, name=ad.user.nickname, person=await models.Person.create())
132
+ adx.maker = actor
132
133
  pms = ad.paymentMethods if isinstance(ad, _PmsTrait) else [pd.paymentMethod for pd in ad.paymentDetails]
133
134
  return types.AdBuyIn(
134
135
  **adx.model_dump(), pms_=await models.Pm.filter(pmexs__ex=self.ex, pmexs__exid__in=[p.code for p in pms])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: xync-client
3
- Version: 0.0.25.dev40
3
+ Version: 0.0.25.dev41
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
@@ -1,90 +0,0 @@
1
- from asyncio import run
2
-
3
- from msgspec import convert
4
- from x_model import init_db
5
- from xync_schema import models, types
6
- from xync_schema.models import Ex, Cur
7
- from xync_schema.enums import PmType
8
-
9
- from xync_client.Abc.Ex import BaseExClient
10
- from xync_client.Htx.etype import pm, Country
11
- from xync_client.loader import PG_DSN
12
-
13
-
14
- class ExClient(BaseExClient):
15
- # 20: Get all pms
16
- async def pms(self, _cur: Cur = None) -> dict[int, types.Pm]:
17
- dist = {
18
- 0: PmType.card,
19
- 1: PmType.bank,
20
- 2: PmType.cash,
21
- 3: PmType.emoney,
22
- 4: PmType.emoney,
23
- 5: PmType.IFSC,
24
- }
25
-
26
- pms: list[pm.Resp] = [convert(p, pm.Resp) for p in (await self._coin_curs_pms())["payMethod"]]
27
-
28
- pmsd = {
29
- p.payMethodId: types.Pm(
30
- name=p.name,
31
- type_=dist.get(p.template),
32
- logo=p.bankImage or p.bankImageWeb,
33
- )
34
- for p in pms
35
- }
36
-
37
- return pmsd
38
-
39
- # 21: Get all: currency,pay,allCountry,coin
40
- async def curs(self) -> list[types.CurE]:
41
- res = (await self._coin_curs_pms())["currency"]
42
- return [
43
- types.CurE(exid=c["currencyId"], ticker=c["nameShort"]) for c in res
44
- ] # if c['showPtoP'] todo: wht "showPtoP" is means
45
-
46
- # 22: Список платежных методов по каждой валюте
47
- async def cur_pms_map(self) -> dict[int, set[int]]:
48
- res = await self._coin_curs_pms()
49
- wrong_pms = {4, 34, 498, 548, 20009, 20010} # , 212, 239, 363 # these ids not exist in pms
50
- return {c["currencyId"]: set(c["supportPayments"]) - wrong_pms for c in res["currency"] if c["supportPayments"]}
51
-
52
- # 23: Список торгуемых монет
53
- async def coins(self) -> list[types.CoinE]:
54
- coins: list[dict] = (await self._coin_curs_pms())["coin"]
55
- return [types.CoinE(exid=c["coinId"], ticker=c["coinCode"]) for c in coins if c["coinType"] == 2]
56
-
57
- # 99: Страны
58
- async def countries(self) -> list[Country]:
59
- res = await self._coin_curs_pms()
60
- cts = [
61
- Country(
62
- id=c["countryId"],
63
- code=c["code"],
64
- name=name[:-1] if (name := c["name"].split(",")[0]).endswith(".") else name,
65
- short=c["appShort"],
66
- cur_id=c["currencyId"],
67
- )
68
- for c in res["country"]
69
- ]
70
- return cts
71
-
72
- # Get all: currency,pay,allCountry,coin
73
- async def _coin_curs_pms(self) -> (dict, dict, dict, dict):
74
- res = (await self._get("/-/x/otc/v1/data/config-list?type=currency,pay,coin,allCountry"))["data"]
75
- res["currency"][0]["currencyId"] = 1
76
- [c.update({"currencyId": 1, "name": ""}) for c in res["country"] if c["currencyId"] == 172]
77
- return res
78
-
79
-
80
- async def main():
81
- _ = await init_db(PG_DSN, models, True)
82
- ex = await Ex.get(name="Htx")
83
- cl = ExClient(ex)
84
- await cl.set_pmcurexs()
85
- await cl.set_coinexs()
86
- await cl.close()
87
-
88
-
89
- if __name__ == "__main__":
90
- run(main())