xync-client 0.0.164__py3-none-any.whl → 0.0.172__py3-none-any.whl
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.
- xync_client/Abc/Agent.py +154 -22
- xync_client/Abc/Ex.py +107 -127
- xync_client/Abc/Order.py +16 -17
- xync_client/Abc/xtype.py +139 -65
- xync_client/BingX/agent.py +1 -1
- xync_client/BitGet/agent.py +1 -3
- xync_client/Bybit/agent.py +72 -321
- xync_client/Bybit/etype/ad.py +79 -58
- xync_client/Bybit/etype/cred.py +25 -3
- xync_client/Bybit/etype/order.py +150 -95
- xync_client/Bybit/ex.py +24 -12
- xync_client/Bybit/{InAgent.py → inAgent.py} +5 -10
- xync_client/Bybit/order.py +33 -16
- xync_client/Htx/agent.py +9 -9
- xync_client/Htx/etype/ad.py +2 -4
- xync_client/Htx/etype/test.py +4 -4
- xync_client/Htx/ex.py +35 -3
- xync_client/Mexc/agent.py +6 -6
- xync_client/Mexc/ex.py +2 -2
- xync_client/TgWallet/agent.py +21 -21
- xync_client/TgWallet/ex.py +11 -11
- xync_client/TgWallet/pyd.py +5 -5
- xync_client/pm_unifier.py +3 -2
- {xync_client-0.0.164.dist-info → xync_client-0.0.172.dist-info}/METADATA +1 -1
- {xync_client-0.0.164.dist-info → xync_client-0.0.172.dist-info}/RECORD +27 -27
- {xync_client-0.0.164.dist-info → xync_client-0.0.172.dist-info}/WHEEL +0 -0
- {xync_client-0.0.164.dist-info → xync_client-0.0.172.dist-info}/top_level.txt +0 -0
xync_client/Abc/Agent.py
CHANGED
|
@@ -6,6 +6,7 @@ from typing import Literal
|
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
from pyro_client.client.file import FileClient
|
|
9
|
+
from tortoise.exceptions import IntegrityError
|
|
9
10
|
from x_client import df_hdrs
|
|
10
11
|
from x_client.aiohttp import Client as HttpClient
|
|
11
12
|
from xync_bot import XyncBot
|
|
@@ -15,25 +16,36 @@ from xync_client.Abc.InAgent import BaseInAgentClient
|
|
|
15
16
|
|
|
16
17
|
from xync_client.Bybit.etype.order import TakeAdReq
|
|
17
18
|
from xync_schema import models
|
|
18
|
-
from xync_schema.models import OrderStatus, Coin, Cur, Ad,
|
|
19
|
-
from xync_schema
|
|
19
|
+
from xync_schema.models import OrderStatus, Coin, Cur, Ad, Actor, Agent
|
|
20
|
+
from xync_schema import xtype
|
|
20
21
|
|
|
21
22
|
from xync_client.Abc.Ex import BaseExClient
|
|
22
|
-
from xync_client.Abc.xtype import
|
|
23
|
+
from xync_client.Abc.xtype import (
|
|
24
|
+
BaseCredEx,
|
|
25
|
+
BaseOrderReq,
|
|
26
|
+
BaseAd,
|
|
27
|
+
AdUpdReq,
|
|
28
|
+
GetAdsReq,
|
|
29
|
+
BaseCredexsExidsTrait,
|
|
30
|
+
BaseOrderFull,
|
|
31
|
+
)
|
|
23
32
|
from xync_client.Gmail import GmClient
|
|
24
33
|
|
|
25
34
|
|
|
26
|
-
class BaseAgentClient(HttpClient, BaseInAgentClient):
|
|
35
|
+
class BaseAgentClient(HttpClient, BaseInAgentClient): # , metaclass=ABCMeta
|
|
27
36
|
actor: Actor
|
|
28
37
|
agent: Agent
|
|
29
38
|
bbot: XyncBot
|
|
30
39
|
fbot: FileClient
|
|
31
40
|
ex_client: BaseExClient
|
|
32
|
-
orders: dict[int, tuple[models.Order,
|
|
41
|
+
orders: dict[int, tuple[models.Order, xtype.BaseOrder]] = {} # pending
|
|
33
42
|
pm_clients: dict[int, PmAgentClient] # {pm_id: PmAgentClient}
|
|
34
43
|
api: HttpClient
|
|
35
44
|
cred_x2e: dict[int, int] = {}
|
|
36
|
-
cred_e2x: dict[int,
|
|
45
|
+
cred_e2x: dict[int, models.CredEx] = {}
|
|
46
|
+
order_x2e: dict[int, int] = {}
|
|
47
|
+
order_e2x: dict[int, int] = {}
|
|
48
|
+
cdx_cls: type[BaseCredEx]
|
|
37
49
|
|
|
38
50
|
def __init__(
|
|
39
51
|
self,
|
|
@@ -59,15 +71,30 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
|
|
|
59
71
|
|
|
60
72
|
async def x2e_cred(self, cred_id: int) -> int: # cred.exid
|
|
61
73
|
if not self.cred_x2e.get(cred_id):
|
|
62
|
-
|
|
63
|
-
self.
|
|
74
|
+
credex = await models.CredEx.get(cred_id=cred_id)
|
|
75
|
+
self.cred_x2e[cred_id] = credex.exid
|
|
76
|
+
self.cred_e2x[credex.exid] = credex
|
|
64
77
|
return self.cred_x2e[cred_id]
|
|
65
78
|
|
|
66
|
-
async def e2x_cred(self,
|
|
67
|
-
if not self.cred_e2x.get(exid):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
async def e2x_cred(self, base_credex: BaseCredEx) -> models.CredEx: # cred.id
|
|
80
|
+
if not self.cred_e2x.get(base_credex.exid):
|
|
81
|
+
if not (credex := await models.CredEx.get_or_none(exid=base_credex.exid, ex=self.ex_client.ex)):
|
|
82
|
+
credex = await self.credex_save(base_credex)
|
|
83
|
+
self.cred_e2x[base_credex.exid] = credex
|
|
84
|
+
self.cred_x2e[credex.cred_id] = base_credex.exid
|
|
85
|
+
return self.cred_e2x[base_credex.exid]
|
|
86
|
+
|
|
87
|
+
async def x2e_order(self, order_id: int) -> int: # order.exid
|
|
88
|
+
if not self.order_x2e.get(order_id):
|
|
89
|
+
self.order_x2e[order_id] = (await models.Order[order_id]).exid
|
|
90
|
+
self.order_e2x[self.order_x2e[order_id]] = order_id
|
|
91
|
+
return self.order_x2e[order_id]
|
|
92
|
+
|
|
93
|
+
async def e2x_order(self, exid: int) -> int: # order.id
|
|
94
|
+
if not self.order_e2x.get(exid):
|
|
95
|
+
self.order_e2x[exid] = (await models.Order.get(exid=exid, taker__ex=self.ex_client.ex)).id
|
|
96
|
+
self.order_x2e[self.order_e2x[exid]] = exid
|
|
97
|
+
return self.order_e2x[exid]
|
|
71
98
|
|
|
72
99
|
async def start(self):
|
|
73
100
|
if self.agent.status & 1: # race
|
|
@@ -78,6 +105,115 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
|
|
|
78
105
|
if self.agent.status & 2: # listen
|
|
79
106
|
await self.start_listen()
|
|
80
107
|
|
|
108
|
+
@abstractmethod
|
|
109
|
+
async def _get_creds(self) -> list[BaseModel]: ...
|
|
110
|
+
|
|
111
|
+
async def get_creds(self) -> list[BaseCredEx]:
|
|
112
|
+
creds: list[BaseModel] = await self._get_creds()
|
|
113
|
+
return [self.cdx_cls.model_validate(cred, from_attributes=True) for cred in creds]
|
|
114
|
+
|
|
115
|
+
async def credex_save(self, cdx: BaseCredEx, pers_id: int = None, cur_id: int = None) -> models.CredEx | None:
|
|
116
|
+
pmex = None
|
|
117
|
+
if cred_old := await models.Cred.get_or_none(
|
|
118
|
+
credexs__exid=cdx.exid, credexs__ex=self.actor.ex
|
|
119
|
+
).prefetch_related("pmcur"): # is old Cred
|
|
120
|
+
cur_id = cur_id or cred_old.pmcur.cur_id
|
|
121
|
+
elif not cur_id: # is new Cred
|
|
122
|
+
if cdx.curex_exid:
|
|
123
|
+
cur_id = (await models.CurEx.get(exid=cdx.curex_exid, ex=self.actor.ex)).cur_id
|
|
124
|
+
else:
|
|
125
|
+
pmex = await models.PmEx.get_or_none(exid=cdx.pmex_exid, ex=self.ex_client.ex).prefetch_related(
|
|
126
|
+
"pm__curs"
|
|
127
|
+
)
|
|
128
|
+
cur_id = (
|
|
129
|
+
pmex.pm.df_cur_id
|
|
130
|
+
or (await cdx.guess_cur(pmex.pm.curs) if len(pmex.pm.curs) != 1 else pmex.pm.curs[0].cur_id)
|
|
131
|
+
or (pmex.pm.country_id and (await pmex.pm.country).cur_id)
|
|
132
|
+
# or (ecdx.currencyBalance and await models.Cur.get_or_none(ticker=ecdx.currencyBalance[0])) # это че еще за хуйня?
|
|
133
|
+
)
|
|
134
|
+
if not cur_id:
|
|
135
|
+
raise ValueError(f"Set default cur for {pmex.name}")
|
|
136
|
+
pm_id = pmex and pmex.pm_id or await self.ex_client.e2x_pm(cdx.pmex_exid)
|
|
137
|
+
if not (pmcur := await models.PmCur.get_or_none(cur_id=cur_id, pm_id=pm_id)):
|
|
138
|
+
raise ValueError(f"No PmCur with cur#{cur_id} and pm#{cdx.pmex_exid}", 404)
|
|
139
|
+
try:
|
|
140
|
+
pers_id = pers_id or cdx.seller.exid and (await self.ex_client.e2x_actor(cdx.seller)).person_id
|
|
141
|
+
cred_db, _ = await models.Cred.update_or_create(
|
|
142
|
+
{"name": cdx.name, "extra": cdx.extra},
|
|
143
|
+
pmcur=pmcur,
|
|
144
|
+
person_id=pers_id,
|
|
145
|
+
detail=cdx.detail,
|
|
146
|
+
)
|
|
147
|
+
if not cred_db.ovr_pm_id and ("XyncPay" in cred_db.detail or "XyncPay" in cred_db.extra):
|
|
148
|
+
cred_db.ovr_pm_id = 0
|
|
149
|
+
await cred_db.save()
|
|
150
|
+
credex_db, _ = await models.CredEx.update_or_create(exid=cdx.exid, cred=cred_db, ex=self.actor.ex)
|
|
151
|
+
except IntegrityError as e:
|
|
152
|
+
raise e
|
|
153
|
+
return credex_db
|
|
154
|
+
|
|
155
|
+
# 25: Список реквизитов моих платежных методов
|
|
156
|
+
async def load_creds(self) -> list[models.CredEx]:
|
|
157
|
+
credexs_epyd: list[BaseCredEx] = await self.get_creds()
|
|
158
|
+
credexs: list[models.CredEx] = [await self.credex_save(f) for f in credexs_epyd]
|
|
159
|
+
return credexs
|
|
160
|
+
|
|
161
|
+
async def my_ad_save(
|
|
162
|
+
self,
|
|
163
|
+
bmad: BaseAd | BaseCredexsExidsTrait,
|
|
164
|
+
rname: str = None,
|
|
165
|
+
) -> models.MyAd:
|
|
166
|
+
ad_db = await self.ex_client.ad_save(bmad)
|
|
167
|
+
mad_db, _ = await models.MyAd.update_or_create(ad=ad_db)
|
|
168
|
+
credexs = await models.CredEx.filter(ex_id=self.actor.ex_id, exid__in=bmad.credex_exids)
|
|
169
|
+
await mad_db.credexs.clear()
|
|
170
|
+
await mad_db.credexs.add(*credexs)
|
|
171
|
+
return mad_db
|
|
172
|
+
|
|
173
|
+
async def load_my_ads(self, only_active: bool = None) -> list[models.MyAd]: # upserted)
|
|
174
|
+
ads = await self.get_my_ads(True)
|
|
175
|
+
if not only_active:
|
|
176
|
+
ads += await self.get_my_ads(False)
|
|
177
|
+
return [await self.my_ad_save(ad) for ad in ads]
|
|
178
|
+
|
|
179
|
+
@abstractmethod
|
|
180
|
+
async def _get_order_full(self, order_exid: int) -> BaseOrderFull: ...
|
|
181
|
+
|
|
182
|
+
async def get_order_full(self, order_exid: int) -> xtype.BaseOrder:
|
|
183
|
+
eorder: BaseOrderFull = await self._get_order_full(order_exid)
|
|
184
|
+
_, cur_scale, __ = await self.ex_client.x2e_cur(await self.ex_client.e2x_cur(eorder.curex_exid))
|
|
185
|
+
_, coin_scale = await self.ex_client.x2e_coin(await self.ex_client.e2x_coin(eorder.coinex_exid))
|
|
186
|
+
ad = await self.ex_client.e2x_ad(eorder.ad_id)
|
|
187
|
+
credex = await self.e2x_cred(eorder.credex)
|
|
188
|
+
taker = await self.ex_client.e2x_actor(eorder.taker)
|
|
189
|
+
border = eorder.model_dump()
|
|
190
|
+
border.update(
|
|
191
|
+
ad_id=ad.id,
|
|
192
|
+
cred_id=credex.cred_id,
|
|
193
|
+
taker_id=taker.id,
|
|
194
|
+
amount=int(eorder.amount * 10**cur_scale),
|
|
195
|
+
quantity=int(eorder.quantity * 10**coin_scale),
|
|
196
|
+
)
|
|
197
|
+
return xtype.BaseOrder.model_validate(border)
|
|
198
|
+
|
|
199
|
+
async def load_order(self, order_exid: int, force_refresh: bool = False) -> tuple[models.Order, xtype.BaseOrder]:
|
|
200
|
+
if not self.orders.get(order_exid) or force_refresh:
|
|
201
|
+
order: xtype.BaseOrder = await self.get_order_full(order_exid)
|
|
202
|
+
if not (
|
|
203
|
+
order_db := await models.Order.get_or_none(
|
|
204
|
+
exid=order_exid, ad__maker__ex=self.actor.ex
|
|
205
|
+
).prefetch_related("ad__pair_side__pair", "cred__pmcur__cur")
|
|
206
|
+
):
|
|
207
|
+
order_db = await self.order_save(order)
|
|
208
|
+
self.orders[order_exid] = order_db, order
|
|
209
|
+
return self.orders[order_exid]
|
|
210
|
+
|
|
211
|
+
async def order_save(self, order: xtype.BaseOrder) -> models.Order:
|
|
212
|
+
order_in = models.Order.validate(order.model_dump())
|
|
213
|
+
odb, _ = await models.Order.update_or_create(**order_in.df_unq())
|
|
214
|
+
# await odb.fetch_related("ad") # todo: for what?
|
|
215
|
+
return odb
|
|
216
|
+
|
|
81
217
|
async def racing(self, race: models.Race):
|
|
82
218
|
pair = race.road.ad.pair_side.pair
|
|
83
219
|
taker_side: int = not race.road.ad.pair_side.is_sell
|
|
@@ -116,7 +252,7 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
|
|
|
116
252
|
asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
|
|
117
253
|
volume = asset.free * 10**-coinex.scale
|
|
118
254
|
volume = str(round(volume, coinex.scale))
|
|
119
|
-
get_ads_req =
|
|
255
|
+
get_ads_req = GetAdsReq(
|
|
120
256
|
coin_id=pair.coin_id, cur_id=pair.cur_id, is_sell=bool(taker_side), pm_ids=pm_ids, amount=amt, limit=50
|
|
121
257
|
)
|
|
122
258
|
try:
|
|
@@ -363,10 +499,6 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
|
|
|
363
499
|
self, exid: int | str, cur: str, detail: str, name: str, fid: int, typ: str, extra=None
|
|
364
500
|
) -> fiat_pyd: ...
|
|
365
501
|
|
|
366
|
-
# 25: Список реквизитов моих платежных методов
|
|
367
|
-
@abstractmethod
|
|
368
|
-
async def creds(self) -> list[CredExOut]: ... # {credex.exid: {cred}}
|
|
369
|
-
|
|
370
502
|
# Создание реквизита на бирже
|
|
371
503
|
async def cred_new(self, cred: models.Cred) -> models.CredEx: ...
|
|
372
504
|
|
|
@@ -387,16 +519,16 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
|
|
|
387
519
|
# # # Ad
|
|
388
520
|
# 29: Список моих объявлений
|
|
389
521
|
@abstractmethod
|
|
390
|
-
async def
|
|
522
|
+
async def get_my_ads(self, status: bool = None) -> list[BaseAd | BaseCredexsExidsTrait]: ...
|
|
391
523
|
|
|
392
524
|
@abstractmethod
|
|
393
|
-
async def x2e_req_ad_upd(self, xreq:
|
|
525
|
+
async def x2e_req_ad_upd(self, xreq: AdUpdReq) -> BaseAd: ...
|
|
394
526
|
|
|
395
527
|
# 30: Создание объявления
|
|
396
528
|
@abstractmethod
|
|
397
529
|
async def ad_new(self, ad: BaseAd) -> Ad: ...
|
|
398
530
|
|
|
399
|
-
async def ad_upd(self, xreq:
|
|
531
|
+
async def ad_upd(self, xreq: AdUpdReq) -> Ad:
|
|
400
532
|
xreq.credexs = await models.CredEx.filter(
|
|
401
533
|
ex_id=self.actor.ex_id,
|
|
402
534
|
cred__pmcur__pm_id__in=xreq.pm_ids,
|
|
@@ -409,7 +541,7 @@ class BaseAgentClient(HttpClient, BaseInAgentClient):
|
|
|
409
541
|
|
|
410
542
|
# 31: Редактирование объявления
|
|
411
543
|
@abstractmethod
|
|
412
|
-
async def _ad_upd(self, ad:
|
|
544
|
+
async def _ad_upd(self, ad: BaseAd) -> Ad: ...
|
|
413
545
|
|
|
414
546
|
# 32: Удаление
|
|
415
547
|
@abstractmethod
|
xync_client/Abc/Ex.py
CHANGED
|
@@ -6,7 +6,6 @@ from collections import defaultdict
|
|
|
6
6
|
from difflib import SequenceMatcher
|
|
7
7
|
|
|
8
8
|
from aiohttp import ClientSession, ClientResponse
|
|
9
|
-
from google.protobuf.internal.wire_format import INT32_MAX
|
|
10
9
|
from msgspec import Struct
|
|
11
10
|
from pydantic import BaseModel
|
|
12
11
|
from pyro_client.client.file import FileClient
|
|
@@ -15,10 +14,10 @@ from x_client.aiohttp import Client as HttpClient
|
|
|
15
14
|
from xync_client.Bybit.etype.ad import AdsReq
|
|
16
15
|
from xync_schema import models
|
|
17
16
|
from xync_schema.enums import FileType
|
|
18
|
-
from xync_schema
|
|
17
|
+
from xync_schema import xtype
|
|
19
18
|
|
|
20
19
|
from xync_client.Abc.AdLoader import AdLoader
|
|
21
|
-
from xync_client.Abc.xtype import PmEx, MapOfIdsList,
|
|
20
|
+
from xync_client.Abc.xtype import PmEx, MapOfIdsList, GetAdsReq, BaseActor, BaseAd, BaseCounteragent
|
|
22
21
|
from xync_client.pm_unifier import PmUnifier, PmUni
|
|
23
22
|
|
|
24
23
|
|
|
@@ -34,14 +33,14 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
34
33
|
coin_e2x: dict[str, int] = {}
|
|
35
34
|
cur_x2e: dict[int, tuple[str, int, int]] = {}
|
|
36
35
|
cur_e2x: dict[str, int] = {}
|
|
37
|
-
pm_x2e: dict[int, str] = {}
|
|
38
|
-
pm_e2x: dict[str, int] = {}
|
|
36
|
+
pm_x2e: dict[int, str | int] = {}
|
|
37
|
+
pm_e2x: dict[str | int, int] = {}
|
|
39
38
|
pairs_e2x: dict[int, dict[int, tuple[int, int]]] = defaultdict(defaultdict)
|
|
40
39
|
pairs_x2e: dict[int, tuple[int, int, int]] = defaultdict(defaultdict)
|
|
41
40
|
actor_x2e: dict[int, int] = {}
|
|
42
|
-
actor_e2x: dict[int,
|
|
41
|
+
actor_e2x: dict[int, models.Actor] = {}
|
|
43
42
|
ad_x2e: dict[int, int] = {}
|
|
44
|
-
ad_e2x: dict[int,
|
|
43
|
+
ad_e2x: dict[int, models.Ad] = {}
|
|
45
44
|
|
|
46
45
|
def __init__(
|
|
47
46
|
self,
|
|
@@ -54,14 +53,14 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
54
53
|
):
|
|
55
54
|
self.ex = ex
|
|
56
55
|
self.bot = bot
|
|
57
|
-
super().__init__(self.host or getattr(ex, attr), headers, cookies, proxy and
|
|
56
|
+
super().__init__(self.host or getattr(ex, attr), headers, cookies, proxy and str(proxy))
|
|
58
57
|
|
|
59
58
|
@abstractmethod
|
|
60
59
|
def pm_type_map(self, typ: models.PmEx) -> str: ...
|
|
61
60
|
|
|
62
61
|
# 19: Список поддерживаемых валют тейкера
|
|
63
62
|
@abstractmethod
|
|
64
|
-
async def curs(self) -> dict[str, CurEx]: # {cur.ticker: cur}
|
|
63
|
+
async def curs(self) -> dict[str, xtype.CurEx]: # {cur.ticker: cur}
|
|
65
64
|
...
|
|
66
65
|
|
|
67
66
|
# 20: Список платежных методов
|
|
@@ -74,9 +73,9 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
74
73
|
async def cur_pms_map(self) -> MapOfIdsList: # {cur.exid: [pm.exid]}
|
|
75
74
|
...
|
|
76
75
|
|
|
77
|
-
# 22: Список торгуемых монет (с
|
|
76
|
+
# 22: Список торгуемых монет (с ограничением по валютам, если есть)
|
|
78
77
|
@abstractmethod
|
|
79
|
-
async def coins(self) -> dict[str, CoinEx]: # {coin.ticker: coin}
|
|
78
|
+
async def coins(self) -> dict[str, xtype.CoinEx]: # {coin.ticker: coin}
|
|
80
79
|
...
|
|
81
80
|
|
|
82
81
|
# 23: Список пар валюта/монет
|
|
@@ -118,7 +117,7 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
118
117
|
self.pm_e2x[self.pm_x2e[pm_id]] = pm_id
|
|
119
118
|
return self.pm_x2e[pm_id]
|
|
120
119
|
|
|
121
|
-
async def e2x_pm(self, exid: str) -> int: # pm.id
|
|
120
|
+
async def e2x_pm(self, exid: str | int) -> int: # pm.id
|
|
122
121
|
if not self.pm_e2x.get(exid):
|
|
123
122
|
self.pm_e2x[exid] = (await models.PmEx.get(exid=exid, ex=self.ex)).pm_id
|
|
124
123
|
self.pm_x2e[self.pm_e2x[exid]] = exid
|
|
@@ -153,15 +152,17 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
153
152
|
|
|
154
153
|
async def x2e_actor(self, actor_id: int) -> int: # actor.exid
|
|
155
154
|
if not self.actor_x2e.get(actor_id):
|
|
156
|
-
|
|
157
|
-
self.
|
|
155
|
+
actor = await models.Actor[actor_id]
|
|
156
|
+
self.actor_x2e[actor_id] = actor.exid
|
|
157
|
+
self.actor_e2x[self.actor_x2e[actor_id]] = actor
|
|
158
158
|
return self.actor_x2e[actor_id]
|
|
159
159
|
|
|
160
|
-
async def e2x_actor(self,
|
|
161
|
-
if not self.actor_e2x.get(exid):
|
|
162
|
-
|
|
163
|
-
self.
|
|
164
|
-
|
|
160
|
+
async def e2x_actor(self, base_actor: BaseActor) -> models.Actor:
|
|
161
|
+
if not self.actor_e2x.get(base_actor.exid):
|
|
162
|
+
actor = await self.actor_save(base_actor)
|
|
163
|
+
self.actor_e2x[base_actor.exid] = actor
|
|
164
|
+
self.actor_x2e[actor.id] = base_actor.exid
|
|
165
|
+
return self.actor_e2x[base_actor.exid]
|
|
165
166
|
|
|
166
167
|
async def x2e_ad(self, ad_id: int) -> int: # ad.exid
|
|
167
168
|
if not self.ad_x2e.get(ad_id):
|
|
@@ -169,15 +170,15 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
169
170
|
self.ad_e2x[self.ad_x2e[ad_id]] = ad_id
|
|
170
171
|
return self.ad_x2e[ad_id]
|
|
171
172
|
|
|
172
|
-
async def e2x_ad(self,
|
|
173
|
-
if not self.ad_e2x.get(
|
|
174
|
-
|
|
175
|
-
self.
|
|
176
|
-
|
|
173
|
+
async def e2x_ad(self, ad_exid: int) -> models.Ad: # todo: пока только для мейкерных, не создаются если нет в бд
|
|
174
|
+
if not self.ad_e2x.get(ad_exid):
|
|
175
|
+
ad = await models.Ad.get_or_none(exid=ad_exid, maker__ex=self.ex)
|
|
176
|
+
self.ad_e2x[ad_exid] = ad
|
|
177
|
+
self.ad_x2e[ad.id] = ad_exid
|
|
178
|
+
return self.ad_e2x[ad_exid]
|
|
177
179
|
|
|
178
180
|
# 24: Список объяв по (buy/sell, cur, coin, pm)
|
|
179
|
-
async def ads(self, xreq:
|
|
180
|
-
self.ex.etype().ad.AdsReq
|
|
181
|
+
async def ads(self, xreq: GetAdsReq, **kwargs) -> list[xtype.BaseAd]:
|
|
181
182
|
ereq = AdsReq(
|
|
182
183
|
coin_id=(await self.x2e_coin(xreq.coin_id))[0],
|
|
183
184
|
cur_id=(await self.x2e_cur(xreq.cur_id))[0],
|
|
@@ -195,11 +196,11 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
195
196
|
|
|
196
197
|
# 42: Чужая объява по id
|
|
197
198
|
@abstractmethod
|
|
198
|
-
async def ad(self, ad_id: int) -> BaseAd: ...
|
|
199
|
+
async def ad(self, ad_id: int) -> xtype.BaseAd: ...
|
|
199
200
|
|
|
200
201
|
# Преобразрование объекта объявления из формата биржи в формат xync
|
|
201
202
|
@abstractmethod
|
|
202
|
-
async def ad_epyd2pydin(self, ad: BaseAd) ->
|
|
203
|
+
async def ad_epyd2pydin(self, ad: BaseAd) -> xtype.BaseAd: ... # my_uid: for MyAd
|
|
203
204
|
|
|
204
205
|
# 99: Страны
|
|
205
206
|
async def countries(self) -> list[Struct]:
|
|
@@ -208,7 +209,7 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
208
209
|
# Импорт валют Cur-ов (с CurEx-ами)
|
|
209
210
|
async def set_curs(self, cookies: dict = None) -> bool:
|
|
210
211
|
# Curs
|
|
211
|
-
cur_pyds: dict[str, CurEx] = await self.curs()
|
|
212
|
+
cur_pyds: dict[str, xtype.CurEx] = await self.curs()
|
|
212
213
|
old_curs = {c.ticker: c.id for c in await models.Cur.all()}
|
|
213
214
|
curs: dict[int | str, models.Cur] = {
|
|
214
215
|
exid: (
|
|
@@ -330,7 +331,7 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
330
331
|
|
|
331
332
|
# Импорт монет (с CoinEx-ами) с биржи в бд
|
|
332
333
|
async def set_coins(self):
|
|
333
|
-
coinexs: dict[str, CoinEx] = await self.coins()
|
|
334
|
+
coinexs: dict[str, xtype.CoinEx] = await self.coins()
|
|
334
335
|
coins_db: dict[int, models.Coin] = {
|
|
335
336
|
c.exid: (
|
|
336
337
|
await models.Coin.update_or_create({"scale": c.scale or self.coin_scales[c.ticker]}, ticker=c.ticker)
|
|
@@ -352,10 +353,10 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
352
353
|
|
|
353
354
|
# Импорт пар биржи в бд
|
|
354
355
|
async def set_pairs(self):
|
|
355
|
-
curs: dict[str,
|
|
356
|
+
curs: dict[str, models.Cur] = {
|
|
356
357
|
k: (await models.Cur.get_or_create(ticker=c.ticker))[0] for k, c in (await self.curs()).items()
|
|
357
358
|
}
|
|
358
|
-
coins: dict[str, CoinEx] = {
|
|
359
|
+
coins: dict[str, xtype.CoinEx] = {
|
|
359
360
|
k: (await models.Coin.get_or_create(ticker=c.ticker))[0] for k, c in (await self.coins()).items()
|
|
360
361
|
}
|
|
361
362
|
prs: tuple[dict, dict] = await self.pairs()
|
|
@@ -394,7 +395,10 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
394
395
|
proxy = await models.Proxy.filter(valid=True, country__short__not="US").order_by("-updated_at").first()
|
|
395
396
|
cookies = self.session.cookie_jar.filter_cookies(self.session._base_url)
|
|
396
397
|
self.session = ClientSession(
|
|
397
|
-
self.session._base_url,
|
|
398
|
+
self.session._base_url,
|
|
399
|
+
headers=self.session.headers,
|
|
400
|
+
cookies=cookies or None,
|
|
401
|
+
proxy=proxy and str(proxy),
|
|
398
402
|
)
|
|
399
403
|
return await self.METHS[resp.method](self, resp.url.path, bp)
|
|
400
404
|
return await super()._proc(resp, bp)
|
|
@@ -429,106 +433,82 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
429
433
|
if ct := set(self.tree.keys()) & a:
|
|
430
434
|
logging.exception(f"cycle cids: {ct}")
|
|
431
435
|
|
|
432
|
-
async def
|
|
433
|
-
if
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
actor.person.name = name
|
|
441
|
-
await actor.person.save()
|
|
442
|
-
return actor.person
|
|
443
|
-
# tmp dirty fix
|
|
444
|
-
note = f"{self.ex.id}:{exid}"
|
|
445
|
-
if person := await models.Person.get_or_none(note__startswith=note, name__not=name):
|
|
446
|
-
person.name = name
|
|
447
|
-
await person.save()
|
|
448
|
-
return person
|
|
449
|
-
try:
|
|
450
|
-
return (await models.Person.update_or_create(name=name, note=note))[0]
|
|
451
|
-
except OperationalError as e:
|
|
452
|
-
raise e
|
|
453
|
-
await models.Actor.create(person=person, exid=exid, ex=self.ex)
|
|
436
|
+
async def person_save(self, base_person: BaseActor) -> models.Person:
|
|
437
|
+
if not (person := await models.Person.get_or_none(note=(note := f"{self.ex.id}:{base_person.exid}"))):
|
|
438
|
+
name = base_person.name if isinstance(base_person, BaseCounteragent) else base_person.nick
|
|
439
|
+
person = await models.Person.create(name=name, note=note)
|
|
440
|
+
elif isinstance(base_person, BaseCounteragent) and not person.name:
|
|
441
|
+
# если персона не новая, но имени не было, а щас передано - обновим
|
|
442
|
+
person.name = base_person.name
|
|
443
|
+
await person.save(update_fields=["name"])
|
|
454
444
|
return person
|
|
455
|
-
# person = await models.Person.create(note=f'{actor.ex_id}:{actor.exid}:{name}') # no person for just ads with no orders
|
|
456
|
-
# raise ValueError(f"Agent #{exid} not found")
|
|
457
445
|
|
|
458
|
-
async def
|
|
459
|
-
self
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
coin_scale = 10 ** (coinex or await models.CoinEx.get(coin_id=ps.pair.coin_id, ex=self.ex)).scale
|
|
485
|
-
amt = int(float(pad.quantity) * float(pad.price) * cur_scale)
|
|
486
|
-
mxf = pad.maxAmount and int(float(pad.maxAmount) * cur_scale)
|
|
487
|
-
df_unq = ad_upd.df_unq(
|
|
488
|
-
maker_id=maker.id,
|
|
489
|
-
pair_side_id=ps.id,
|
|
490
|
-
amount=min(amt, INT32_MAX),
|
|
491
|
-
quantity=int(float(pad.quantity) * coin_scale),
|
|
492
|
-
min_fiat=int(float(pad.minAmount) * cur_scale),
|
|
493
|
-
max_fiat=min(mxf, INT32_MAX),
|
|
494
|
-
price=int(float(pad.price) * cur_scale),
|
|
495
|
-
premium=int(float(pad.premium) * 100),
|
|
496
|
-
cond_id=cid,
|
|
497
|
-
status=self.ad_status(ad_upd.status),
|
|
446
|
+
async def actor_save(self, base_actor: BaseActor) -> models.Actor:
|
|
447
|
+
if not (actor := await models.Actor.get_or_none(exid=base_actor.exid, ex=self.ex)):
|
|
448
|
+
# если это новый актор - делаем создание персоны со стороны биржи
|
|
449
|
+
person = await self.person_save(base_actor)
|
|
450
|
+
actor = await models.Actor.create(exid=base_actor.exid, name=base_actor.nick, person=person, ex=self.ex)
|
|
451
|
+
return actor
|
|
452
|
+
|
|
453
|
+
async def ad_save(self, base_ad: BaseAd) -> models.Ad:
|
|
454
|
+
if ad_db := await models.Ad.get_or_none(exid=base_ad.exid, maker__ex=self.ex):
|
|
455
|
+
... # load new data
|
|
456
|
+
return ad_db
|
|
457
|
+
cond = await self.load_cond(base_ad.cond_txt, base_ad.maker_exid)
|
|
458
|
+
_, cur_scale, _cur_min = await self.x2e_cur(await self.e2x_cur(base_ad.curex_exid))
|
|
459
|
+
_, coin_scale = await self.x2e_coin(await self.e2x_coin(base_ad.coinex_exid))
|
|
460
|
+
badd = base_ad.model_dump()
|
|
461
|
+
badd.update(
|
|
462
|
+
amount=int((base_ad.amount or base_ad.quantity * base_ad.price) * 10**cur_scale),
|
|
463
|
+
max_fiat=int(base_ad.max_fiat * 10**cur_scale),
|
|
464
|
+
min_fiat=int(base_ad.min_fiat * 10**cur_scale),
|
|
465
|
+
premium=int(base_ad.premium * 100_00),
|
|
466
|
+
price=int(base_ad.price * 10**cur_scale),
|
|
467
|
+
quantity=int((base_ad.quantity or base_ad.amount / base_ad.price) * 10**coin_scale),
|
|
468
|
+
maker_id=(await self.e2x_actor(base_ad.maker)).id,
|
|
469
|
+
cond_id=cond and cond.id,
|
|
470
|
+
pair_side_id=await self.e2x_pair(base_ad.coinex_exid, base_ad.curex_exid, bool(base_ad.side.value)),
|
|
471
|
+
pms=await models.Pm.filter(pmexs__ex=self.ex, pmexs__exid__in=base_ad.pmex_exids),
|
|
498
472
|
)
|
|
473
|
+
ad = xtype.BaseAd.model_validate(badd)
|
|
474
|
+
ad_upd = models.Ad.validate(ad.model_dump(), with_pk=False)
|
|
499
475
|
try:
|
|
500
|
-
ad_db, _ = await models.Ad.update_or_create(**df_unq)
|
|
476
|
+
ad_db, _ = await models.Ad.update_or_create(**ad_upd.df_unq())
|
|
501
477
|
except OperationalError as e:
|
|
502
478
|
raise e
|
|
503
479
|
await ad_db.pms.clear()
|
|
504
|
-
await ad_db.pms.add(*
|
|
480
|
+
await ad_db.pms.add(*ad.pms)
|
|
505
481
|
return ad_db
|
|
506
482
|
|
|
507
|
-
async def
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
483
|
+
async def load_cond(self, txt: str, maker_exid: int) -> models.Cond | None:
|
|
484
|
+
# если текст пустой
|
|
485
|
+
if not (cleaned := clean(txt)):
|
|
486
|
+
return None
|
|
487
|
+
# если точно такое условие уже есть в бд
|
|
488
|
+
if cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned):
|
|
489
|
+
return await models.Cond[cid]
|
|
490
|
+
# создаем новое условие
|
|
491
|
+
return await self.cond_new(cleaned, {maker_exid})
|
|
492
|
+
|
|
493
|
+
async def old_load_cond(self, ad: BaseAd) -> models.Ad:
|
|
517
494
|
_sim, cid = None, None
|
|
518
|
-
ad_db = await models.Ad.get_or_none(exid=ad.
|
|
495
|
+
ad_db = await models.Ad.get_or_none(exid=ad.exid, maker__ex=self.ex).prefetch_related("cond")
|
|
519
496
|
# если точно такое условие уже есть в бд
|
|
520
|
-
if not (cleaned := clean(ad.
|
|
497
|
+
if not (cleaned := clean(ad.cond_txt)) or (
|
|
498
|
+
cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned)
|
|
499
|
+
):
|
|
521
500
|
# и объява с таким ид уже есть, но у нее другое условие
|
|
522
501
|
if ad_db and ad_db.cond_id != cid:
|
|
502
|
+
old_cid = ad_db.cond_id
|
|
523
503
|
# то обновляем ид ее условия
|
|
524
504
|
ad_db.cond_id = cid
|
|
525
|
-
await ad_db.save()
|
|
526
|
-
logging.info(f"{ad.
|
|
527
|
-
#
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
return
|
|
505
|
+
await ad_db.save(update_fields=["cond_id"])
|
|
506
|
+
logging.info(f"{ad.maker_name} upd cond#{ad_db.cond_id}->{cid}")
|
|
507
|
+
# если после переназначения объяве нового условия, со старым условием не осталось объяв, то удаляем его
|
|
508
|
+
if not len((old_cond := await models.Cond.get(id=old_cid).prefetch_related("ads")).ads):
|
|
509
|
+
await old_cond.delete()
|
|
510
|
+
logging.warning(f"Cond#{old_cid} deleted!")
|
|
511
|
+
return ad_db or await self.ad_save(ad)
|
|
532
512
|
# если эта объява в таким ид уже есть в бд, но с другим условием (или без), а текущего условия еще нет в бд
|
|
533
513
|
if ad_db:
|
|
534
514
|
await ad_db.fetch_related("cond__ads", "maker")
|
|
@@ -540,11 +520,11 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
540
520
|
{ra.maker_id for ra in rest_ads} - {ad_db.maker_id}
|
|
541
521
|
):
|
|
542
522
|
# создадим новое условие и присвоим его только текущей объяве
|
|
543
|
-
cond = await self.cond_new(cleaned, {int(ad.
|
|
523
|
+
cond = await self.cond_new(cleaned, {int(ad.maker_exid)})
|
|
544
524
|
ad_db.cond_id = cond.id
|
|
545
|
-
await ad_db.save()
|
|
546
|
-
ad_db.cond = cond
|
|
547
|
-
return ad_db
|
|
525
|
+
await ad_db.save(update_fields=["cond_id"])
|
|
526
|
+
ad_db.cond = cond # todo: а это зачем?
|
|
527
|
+
return ad_db
|
|
548
528
|
# а если других объяв со старым условием этой обявы нет, либо они все этого же юзера
|
|
549
529
|
# обновляем условие (в тч во всех ЕГО объявах)
|
|
550
530
|
ad_db.cond.last_ver = ad_db.cond.raw_txt
|
|
@@ -556,12 +536,12 @@ class BaseExClient(HttpClient, AdLoader):
|
|
|
556
536
|
await self.cond_upd(ad_db.cond, {ad_db.maker.exid})
|
|
557
537
|
# и подправим коэфициенты похожести нового текста
|
|
558
538
|
await self.fix_rel_sims(ad_db.cond_id, cleaned)
|
|
559
|
-
return ad_db
|
|
560
|
-
|
|
561
|
-
cond = await self.cond_new(cleaned, {int(ad.
|
|
562
|
-
ad_db = await self.
|
|
539
|
+
return ad_db
|
|
540
|
+
# нет ни объяв ни таких условий еще в бд, все новое
|
|
541
|
+
cond = await self.cond_new(cleaned, {int(ad.maker_exid)})
|
|
542
|
+
ad_db = await self.ad_save(ad, cond.id)
|
|
563
543
|
ad_db.cond = cond
|
|
564
|
-
return ad_db
|
|
544
|
+
return ad_db
|
|
565
545
|
|
|
566
546
|
async def cond_new(self, txt: str, uids: set[int]) -> models.Cond:
|
|
567
547
|
new_cond, _ = await models.Cond.update_or_create(raw_txt=txt)
|