xync-client 0.0.145__py3-none-any.whl → 0.0.148__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.
Potentially problematic release.
This version of xync-client might be problematic. Click here for more details.
- xync_client/Abc/AdLoader.py +285 -0
- xync_client/Abc/Agent.py +2 -0
- xync_client/Abc/Ex.py +11 -9
- xync_client/Abc/PmAgent.py +1 -1
- xync_client/Abc/xtype.py +3 -1
- xync_client/Bybit/InAgent.py +16 -11
- xync_client/Bybit/agent.py +181 -391
- xync_client/Bybit/etype/ad.py +1 -1
- xync_client/Bybit/etype/cred.py +1 -0
- xync_client/Bybit/etype/order.py +34 -16
- xync_client/Gmail/__init__.py +16 -3
- xync_client/Pms/Payeer/__init__.py +7 -5
- xync_client/Pms/Volet/__init__.py +1 -1
- {xync_client-0.0.145.dist-info → xync_client-0.0.148.dist-info}/METADATA +4 -1
- {xync_client-0.0.145.dist-info → xync_client-0.0.148.dist-info}/RECORD +17 -16
- {xync_client-0.0.145.dist-info → xync_client-0.0.148.dist-info}/WHEEL +0 -0
- {xync_client-0.0.145.dist-info → xync_client-0.0.148.dist-info}/top_level.txt +0 -0
xync_client/Bybit/agent.py
CHANGED
|
@@ -2,7 +2,6 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
4
|
from asyncio import sleep, gather
|
|
5
|
-
from collections import defaultdict
|
|
6
5
|
from datetime import datetime, timedelta, timezone
|
|
7
6
|
from difflib import SequenceMatcher
|
|
8
7
|
from enum import IntEnum
|
|
@@ -18,7 +17,7 @@ from payeer_api import PayeerAPI
|
|
|
18
17
|
from pyro_client.client.file import FileClient
|
|
19
18
|
from tortoise import BaseDBAsyncClient
|
|
20
19
|
from tortoise.exceptions import IntegrityError
|
|
21
|
-
from tortoise.expressions import
|
|
20
|
+
from tortoise.expressions import Q
|
|
22
21
|
from tortoise.functions import Count
|
|
23
22
|
from tortoise.signals import post_save
|
|
24
23
|
from urllib3.exceptions import ReadTimeoutError
|
|
@@ -28,10 +27,10 @@ from xync_bot import XyncBot
|
|
|
28
27
|
from xync_schema import models
|
|
29
28
|
from xync_schema.enums import OrderStatus
|
|
30
29
|
|
|
31
|
-
from xync_schema.models import Actor,
|
|
30
|
+
from xync_schema.models import Actor, PmCur, Agent
|
|
32
31
|
|
|
33
32
|
from xync_client.Abc.Agent import BaseAgentClient
|
|
34
|
-
from xync_client.Abc.xtype import
|
|
33
|
+
from xync_client.Abc.xtype import FlatDict, BaseOrderReq
|
|
35
34
|
from xync_client.Bybit.etype.ad import AdPostRequest, AdUpdateRequest, Ad, AdStatus
|
|
36
35
|
from xync_client.Bybit.etype.cred import CredEpyd
|
|
37
36
|
from xync_client.Bybit.etype.order import (
|
|
@@ -43,6 +42,8 @@ from xync_client.Bybit.etype.order import (
|
|
|
43
42
|
OrderFull,
|
|
44
43
|
Message,
|
|
45
44
|
Status,
|
|
45
|
+
OrderSellRequest,
|
|
46
|
+
TakeAdReq,
|
|
46
47
|
)
|
|
47
48
|
from xync_client.loader import TORM, NET_TOKEN, PAY_TOKEN
|
|
48
49
|
|
|
@@ -83,16 +84,12 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
83
84
|
"actionType": "MODIFY",
|
|
84
85
|
"securityRiskToken": "",
|
|
85
86
|
}
|
|
86
|
-
all_conds: dict[int, tuple[str, set[int]]] = {}
|
|
87
|
-
cond_sims: dict[int, int] = defaultdict(set)
|
|
88
|
-
rcond_sims: dict[int, set[int]] = defaultdict(set) # backward
|
|
89
|
-
tree: dict = {}
|
|
90
87
|
|
|
91
88
|
def __init__(self, agent: Agent, fbot: FileClient, bbot: XyncBot, **kwargs):
|
|
92
89
|
super().__init__(agent, fbot, bbot, **kwargs)
|
|
93
90
|
self.api = P2P(testnet=False, api_key=agent.auth["key"], api_secret=agent.auth["sec"])
|
|
94
|
-
self.hist: dict = None
|
|
95
|
-
self.completed_orders: list[int] = None
|
|
91
|
+
self.hist: dict | None = None
|
|
92
|
+
self.completed_orders: list[int] | None = None
|
|
96
93
|
|
|
97
94
|
""" Private METHs"""
|
|
98
95
|
|
|
@@ -116,12 +113,8 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
116
113
|
else:
|
|
117
114
|
return logging.exception(method1)
|
|
118
115
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if fiat_id:
|
|
122
|
-
fiat = [m for m in list_methods if m["id"] == fiat_id][0]
|
|
123
|
-
return fiat
|
|
124
|
-
return list_methods[1]
|
|
116
|
+
def get_payment_method(self, fiat_id: int) -> CredEpyd:
|
|
117
|
+
return self.creds()[fiat_id]
|
|
125
118
|
|
|
126
119
|
def creds(self) -> dict[int, CredEpyd]:
|
|
127
120
|
data = self.api.get_user_payment_types()
|
|
@@ -204,13 +197,13 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
204
197
|
# 27
|
|
205
198
|
async def fiat_upd(self, fiat_id: int, detail: str, name: str = None) -> dict:
|
|
206
199
|
fiat = self.get_payment_method(fiat_id)
|
|
207
|
-
fiat
|
|
208
|
-
fiat
|
|
209
|
-
result = await self._post("/fiat/otc/user/payment/new_update", fiat)
|
|
200
|
+
fiat.realName = name
|
|
201
|
+
fiat.accountNo = detail
|
|
202
|
+
result = await self._post("/fiat/otc/user/payment/new_update", fiat.model_dump(exclude_none=True))
|
|
210
203
|
srt = result["result"]["securityRiskToken"]
|
|
211
204
|
await self._check_2fa(srt)
|
|
212
|
-
fiat
|
|
213
|
-
result2 = await self._post("/fiat/otc/user/payment/new_update", fiat)
|
|
205
|
+
fiat.securityRiskToken = srt
|
|
206
|
+
result2 = await self._post("/fiat/otc/user/payment/new_update", fiat.model_dump(exclude_none=True))
|
|
214
207
|
return result2
|
|
215
208
|
|
|
216
209
|
# 28
|
|
@@ -242,10 +235,6 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
242
235
|
cnx.exid, crx.exid, is_sell, [pmex.exid for pmex in pmxs or []], amount, lim, vm_filter
|
|
243
236
|
)
|
|
244
237
|
|
|
245
|
-
def online_ads(self) -> str:
|
|
246
|
-
online = self._get("/fiat/otc/maker/work-config/get")
|
|
247
|
-
return online["result"]["workStatus"]
|
|
248
|
-
|
|
249
238
|
@staticmethod
|
|
250
239
|
def get_rate(list_ads: list) -> float:
|
|
251
240
|
ads = [ad for ad in list_ads if set(ad["payments"]) - {"5", "51"}]
|
|
@@ -262,7 +251,7 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
262
251
|
ads = self.my_ads(True)
|
|
263
252
|
if not active:
|
|
264
253
|
ads += self.my_ads(False)
|
|
265
|
-
res = [await self.
|
|
254
|
+
res = [await self.ex_client.ad_load(ad, maker=self.actor) for ad in ads]
|
|
266
255
|
res = [await models.MyAd.update_or_create(ad=ad) for ad in res]
|
|
267
256
|
return len(res)
|
|
268
257
|
|
|
@@ -273,20 +262,39 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
273
262
|
security_risk_token = data["result"]["securityRiskToken"]
|
|
274
263
|
return security_risk_token
|
|
275
264
|
|
|
276
|
-
def _check_2fa(self, risk_token):
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
265
|
+
async def _check_2fa(self, risk_token) -> int:
|
|
266
|
+
cres = await self._post("/user/public/risk/components", {"risk_token": risk_token})
|
|
267
|
+
if cres["ret_msg"] != "success":
|
|
268
|
+
raise HTTPException("get")
|
|
269
|
+
cres = cres["result"]["component_list"]
|
|
270
|
+
res = await self._post(
|
|
271
|
+
"/user/public/risk/verify",
|
|
272
|
+
{
|
|
273
|
+
"risk_token": risk_token,
|
|
274
|
+
"component_list": {c["component_id"]: self.__get_2fa(c["component_id"]) for c in cres},
|
|
275
|
+
},
|
|
284
276
|
)
|
|
285
277
|
if res["ret_msg"] != "success":
|
|
286
|
-
|
|
287
|
-
sleep(5)
|
|
288
|
-
self._check_2fa(risk_token)
|
|
289
|
-
return res
|
|
278
|
+
logging.error("Wrong 2fa, wait 5 secs and retry..")
|
|
279
|
+
await sleep(5)
|
|
280
|
+
await self._check_2fa(risk_token)
|
|
281
|
+
return res["ret_code"]
|
|
282
|
+
|
|
283
|
+
async def __get_2fa(self, typ: Literal["google2fa", "email_verify", "payment_password_verify"], rt: str = None):
|
|
284
|
+
if typ == "google2fa":
|
|
285
|
+
bybit_secret = self.agent.auth["2fa"]
|
|
286
|
+
totp = pyotp.TOTP(bybit_secret)
|
|
287
|
+
return totp.now()
|
|
288
|
+
elif typ == "email_verify":
|
|
289
|
+
res = await self._post("/user/public/risk/send/code", {"risk_token": rt, "component_id": "email_verify"})
|
|
290
|
+
if res["ret_msg"] != "success":
|
|
291
|
+
return self.gmail.bybit_code()
|
|
292
|
+
elif cool_down := int(res["result"]["cool_down"]):
|
|
293
|
+
await sleep(cool_down)
|
|
294
|
+
return self.gmail.bybit_code()
|
|
295
|
+
elif typ == "payment_password_verify":
|
|
296
|
+
return self.agent.auth["pass"]
|
|
297
|
+
raise Exception("2fa fail")
|
|
290
298
|
|
|
291
299
|
def _post_ad(self, risk_token: str):
|
|
292
300
|
self.create_ad_body.update({"securityRiskToken": risk_token})
|
|
@@ -340,27 +348,56 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
340
348
|
data = self.api.remove_ad(itemId=ad_id)
|
|
341
349
|
return data
|
|
342
350
|
|
|
343
|
-
async def
|
|
344
|
-
|
|
345
|
-
if
|
|
346
|
-
|
|
347
|
-
|
|
351
|
+
async def __preorder_request(self, ad_id: int) -> PreOrderResp:
|
|
352
|
+
res = await self._post("/fiat/otc/item/simple", data={"item_id": str(ad_id)})
|
|
353
|
+
if res["ret_code"] == 0:
|
|
354
|
+
res = res["result"]
|
|
355
|
+
return PreOrderResp.model_validate(res)
|
|
356
|
+
|
|
357
|
+
async def __order_request_build(self, por: PreOrderResp, br: BaseOrderReq) -> OrderRequest:
|
|
348
358
|
req = OrderRequest(
|
|
349
|
-
itemId=
|
|
359
|
+
itemId=por.id,
|
|
350
360
|
tokenId=br.coin_exid,
|
|
351
361
|
currencyId=br.cur_exid,
|
|
352
|
-
side=
|
|
353
|
-
amount=str(br.fiat_amount
|
|
354
|
-
curPrice=
|
|
355
|
-
quantity=str(
|
|
356
|
-
flag="amount"
|
|
362
|
+
side="1" if br.is_sell else "0",
|
|
363
|
+
amount=str(br.fiat_amount),
|
|
364
|
+
curPrice=por.curPrice,
|
|
365
|
+
quantity=str(round(br.fiat_amount / float(por.price), br.coin_scale)),
|
|
366
|
+
flag="amount",
|
|
367
|
+
# paymentType="51",
|
|
368
|
+
# paymentId="20399134",
|
|
369
|
+
# online="0"
|
|
357
370
|
)
|
|
371
|
+
if br.is_sell:
|
|
372
|
+
credex = await models.CredEx.get(
|
|
373
|
+
cred__person_id=self.actor.person_id,
|
|
374
|
+
pmcur__pm__pmexs__exid=por.payments[0],
|
|
375
|
+
pmcur__pm__pmexs__ex_id=self.ex_client.ex.id,
|
|
376
|
+
pmcur__cur_id=br.cur_exid,
|
|
377
|
+
)
|
|
378
|
+
req = OrderSellRequest(**req.model_dump(), paymentType=por.payments[0], paymentId=str(credex.exid))
|
|
379
|
+
return req
|
|
380
|
+
|
|
381
|
+
async def _order_request(self, bor: BaseOrderReq) -> OrderResp:
|
|
382
|
+
por: PreOrderResp = await self.__preorder_request(bor.ad_id)
|
|
383
|
+
req: OrderRequest | OrderSellRequest = await self.__order_request_build(por, bor)
|
|
358
384
|
# вот непосредственно сам запрос на ордер
|
|
359
|
-
|
|
385
|
+
return await self.__order_create(req, bor)
|
|
386
|
+
|
|
387
|
+
async def __order_create(self, req: OrderRequest | OrderSellRequest, bor: BaseOrderReq) -> OrderResp:
|
|
388
|
+
res: dict = await self._post("/fiat/otc/order/create", data=req.model_dump())
|
|
360
389
|
if res["ret_code"] == 0:
|
|
361
|
-
|
|
390
|
+
resp = OrderResp.model_validate(res["result"])
|
|
362
391
|
elif res["ret_code"] == 912120030 or res["ret_msg"] == "The price has changed, please try again later.":
|
|
363
|
-
|
|
392
|
+
resp = await self._order_request(bor)
|
|
393
|
+
if not resp.orderId and resp.needSecurityRisk:
|
|
394
|
+
if rc := await self._check_2fa(resp.securityRiskToken):
|
|
395
|
+
await self.bbot.send(self.actor.person.user.username_id, f"Bybit 2fa: {rc}")
|
|
396
|
+
raise Exception(f"Bybit 2fa: {rc}")
|
|
397
|
+
# еще раз уже с токеном
|
|
398
|
+
req.securityRiskToken = resp.securityRiskToken
|
|
399
|
+
resp = await self.__order_create(req, bor)
|
|
400
|
+
return resp
|
|
364
401
|
|
|
365
402
|
async def cancel_order(self, order_id: str) -> bool:
|
|
366
403
|
cr = CancelOrderReq(orderId=order_id)
|
|
@@ -444,12 +481,14 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
444
481
|
maker_name = order.buyerRealName, order.sellerRealName
|
|
445
482
|
im_maker = int(order.makerUserId == order.userId)
|
|
446
483
|
taker_id = (order.userId, order.targetUserId)[im_maker]
|
|
447
|
-
taker_person = await self.
|
|
484
|
+
taker_person = await self.ex_client.person_name_update(maker_name[::-1][ad.side], taker_id)
|
|
448
485
|
seller_person = (
|
|
449
|
-
self.actor.person
|
|
486
|
+
self.actor.person
|
|
487
|
+
if order.side
|
|
488
|
+
else await self.ex_client.person_name_update(order.sellerRealName, int(order.targetUserId))
|
|
450
489
|
)
|
|
451
490
|
taker_nick = (self.actor.name, order.targetNickName)[im_maker] # todo: check
|
|
452
|
-
ad_db, cond_isnew = await self.
|
|
491
|
+
ad_db, cond_isnew = await self.ex_client.cond_load(ad, force=True, rname=maker_name[ad.side])
|
|
453
492
|
if not ad_db:
|
|
454
493
|
...
|
|
455
494
|
ecredex: CredEpyd = order.confirmedPayTerm
|
|
@@ -546,13 +585,13 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
546
585
|
to = ((odb.payed_at or odb.created_at) + timedelta(minutes=180 + 30)).isoformat(sep=" ").split("+")[0]
|
|
547
586
|
tsa = [
|
|
548
587
|
t
|
|
549
|
-
for tid, t in self.hist.items()
|
|
588
|
+
for tid, t in (self.hist.items() if self.hist else [])
|
|
550
589
|
if (ecredex.accountNo == t["to"] and t["from"] != "@merchant" and frm < t["date"] < to)
|
|
551
590
|
]
|
|
552
591
|
buyer_person = (
|
|
553
592
|
self.actor.person
|
|
554
593
|
if not order.side
|
|
555
|
-
else await self.
|
|
594
|
+
else await self.ex_client.person_name_update(order.buyerRealName, int(order.targetUserId))
|
|
556
595
|
)
|
|
557
596
|
ts = [t for t in tsa if floor(fa := float(order.amount)) <= float(t["creditedAmount"]) <= round(fa)]
|
|
558
597
|
if len(ts) != 1:
|
|
@@ -705,7 +744,7 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
705
744
|
pmexs: list[models.PmEx] = await models.PmEx.filter(pm_id__in=pm_ids, ex=self.actor.ex).prefetch_related("pm")
|
|
706
745
|
k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
|
|
707
746
|
sleep_sec = 3 # 1 if set(pms) & {"volet"} and coinex.coin_id == 1 else 5
|
|
708
|
-
creds: list[models.CredEx] = await self.
|
|
747
|
+
creds: list[models.CredEx] = await self.actor.get_credexs_by(pm_ids, curex.cur_id)
|
|
709
748
|
_lstat, volume = None, 0
|
|
710
749
|
|
|
711
750
|
while self.actor.person.user.status > 0:
|
|
@@ -764,7 +803,7 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
764
803
|
await sleep(15)
|
|
765
804
|
continue
|
|
766
805
|
(cur_plc,) = cur_plc # может упасть если в списке > 1 наш ad
|
|
767
|
-
[(await self.
|
|
806
|
+
[(await self.ex_client.cond_load(ad, race.road.ad.pair_side, True))[0] for ad in ads[:cur_plc]]
|
|
768
807
|
# rivals = [
|
|
769
808
|
# (await models.RaceStat.update_or_create({"place": plc, "price": ad.price, "premium": ad.premium}, ad=ad))[
|
|
770
809
|
# 0
|
|
@@ -905,296 +944,87 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
905
944
|
bc, sc = mdl + mdl * (perc / 2), mdl - mdl * (perc / 2)
|
|
906
945
|
return (bc, sc), hp, vmf, zplace
|
|
907
946
|
|
|
908
|
-
async def
|
|
909
|
-
self
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
):
|
|
919
|
-
k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
|
|
920
|
-
|
|
921
|
-
if pms:
|
|
922
|
-
creds: dict[models.PmEx, models.CredEx] = await self.get_credexs_by_norms(pms, curex.cur_id)
|
|
923
|
-
[str(p.exid) for p in creds.values()]
|
|
924
|
-
|
|
925
|
-
if taker_side: # гонка в стакане продажи - мы покупаем монету за ФИАТ
|
|
926
|
-
fiats = await models.Fiat.filter(
|
|
927
|
-
cred_id__in=[cx.cred_id for cx in creds.values()], amount__not=F("target")
|
|
928
|
-
)
|
|
929
|
-
volume = min(volume, max(fiats, key=lambda f: f.target - f.amount).amount / ceil)
|
|
930
|
-
else: # гонка в стакане покупки - мы продаем МОНЕТУ за фиат
|
|
931
|
-
asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
|
|
932
|
-
volume = min(volume, asset.free)
|
|
933
|
-
volume = str(round(volume, coinex.coin.scale))
|
|
934
|
-
ps = await PairSide.get(
|
|
935
|
-
is_sell=taker_side,
|
|
936
|
-
pair__coin_id=coinex.coin_id,
|
|
937
|
-
pair__cur_id=curex.cur_id,
|
|
938
|
-
)
|
|
939
|
-
while self.actor.person.user.status > 0: # todo: depends on rest asset/fiat
|
|
940
|
-
ads: list[Ad] = await self.ads(coinex, curex, taker_side, pms and list(creds.keys()))
|
|
941
|
-
|
|
942
|
-
if not ads:
|
|
943
|
-
print(coinex.exid, curex.exid, taker_side, "no ads!")
|
|
944
|
-
await sleep(300)
|
|
945
|
-
continue
|
|
946
|
-
|
|
947
|
-
for i, ad in enumerate(ads):
|
|
948
|
-
if (ceil - float(ad.price)) * k < 0:
|
|
949
|
-
break
|
|
950
|
-
if int(ad.userId) == self.actor.exid:
|
|
951
|
-
logging.info(f"My ad {'-' if taker_side else '+'}{coinex.exid}/{curex.exid} on place#{i}")
|
|
952
|
-
continue
|
|
953
|
-
ad_db, isnew = await self.cond_upsert(ad, ps=ps)
|
|
954
|
-
if isnew:
|
|
955
|
-
s = f"{'-' if taker_side else '+'}{ad.price}[{ad.minAmount}-{ad.maxAmount}]{coinex.exid}/{curex.exid}"
|
|
956
|
-
print(s, end=" | ", flush=True)
|
|
957
|
-
try:
|
|
958
|
-
# take
|
|
959
|
-
...
|
|
960
|
-
except FailedRequestError as e:
|
|
961
|
-
if ExcCode(e.status_code) == ExcCode.RareLimit:
|
|
962
|
-
await sleep(195)
|
|
963
|
-
elif ExcCode(e.status_code) == ExcCode.Timestamp:
|
|
964
|
-
await sleep(2)
|
|
965
|
-
else:
|
|
966
|
-
raise e
|
|
967
|
-
except (ReadTimeoutError, ConnectionDoesNotExistError):
|
|
968
|
-
logging.warning("Connection failed. Restarting..")
|
|
969
|
-
await sleep(3)
|
|
970
|
-
|
|
971
|
-
async def cond_upsert(
|
|
972
|
-
self, ad: Ad, rname: str = None, ps: PairSide = None, force: bool = False
|
|
973
|
-
) -> tuple[models.Ad, bool]:
|
|
974
|
-
_sim, cid = None, None
|
|
975
|
-
ad_db = await models.Ad.get_or_none(exid=ad.id, maker__ex=self.ex_client.ex).prefetch_related("cond")
|
|
976
|
-
# если точно такое условие уже есть в бд
|
|
977
|
-
if not (cleaned := clean(ad.remark)) or (cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned)):
|
|
978
|
-
# и объява с таким ид уже есть, но у нее другое условие
|
|
979
|
-
if ad_db and ad_db.cond_id != cid:
|
|
980
|
-
# то обновляем ид ее условия
|
|
981
|
-
ad_db.cond_id = cid
|
|
982
|
-
await ad_db.save()
|
|
983
|
-
logging.info(f"{ad.nickName} upd cond#{ad_db.cond_id}->{cid}")
|
|
984
|
-
# old_cid = ad_db.cond_id # todo: solve race-condition, а пока что очищаем при каждом запуске
|
|
985
|
-
# if not len((old_cond := await Cond.get(id=old_cid).prefetch_related('ads')).ads):
|
|
986
|
-
# await old_cond.delete()
|
|
987
|
-
# logging.warning(f"Cond#{old_cid} deleted!")
|
|
988
|
-
return (ad_db or force and await self.ad_create(ad, cid, rname, ps)), False
|
|
989
|
-
# если эта объява в таким ид уже есть в бд, но с другим условием (или без), а текущего условия еще нет в бд
|
|
990
|
-
if ad_db:
|
|
991
|
-
await ad_db.fetch_related("cond__ads", "maker")
|
|
992
|
-
if not ad_db.cond_id or (
|
|
993
|
-
# у измененного условия этой объявы есть другие объявы?
|
|
994
|
-
(rest_ads := set(ad_db.cond.ads) - {ad_db})
|
|
995
|
-
and
|
|
996
|
-
# другие объявы этого условия принадлежат другим юзерам
|
|
997
|
-
{ra.maker_id for ra in rest_ads} - {ad_db.maker_id}
|
|
998
|
-
):
|
|
999
|
-
# создадим новое условие и присвоим его только текущей объяве
|
|
1000
|
-
cid = await self.cond_new(cleaned, {int(ad.userId)})
|
|
1001
|
-
ad_db.cond_id = cid
|
|
1002
|
-
await ad_db.save()
|
|
1003
|
-
|
|
1004
|
-
return ad_db, True
|
|
1005
|
-
# а если других объяв со старым условием этой обявы нет, либо они все этого же юзера
|
|
1006
|
-
# обновляем условие (в тч во всех ЕГО объявах)
|
|
1007
|
-
ad_db.cond.last_ver = ad_db.cond.raw_txt
|
|
1008
|
-
ad_db.cond.raw_txt = cleaned
|
|
1009
|
-
await ad_db.cond.save()
|
|
1010
|
-
await self.cond_upd(ad_db.cond, {ad_db.maker.exid})
|
|
1011
|
-
# и подправим коэфициенты похожести нового текста
|
|
1012
|
-
await self.fix_rel_sims(ad_db.cond_id, cleaned)
|
|
1013
|
-
return ad_db, False
|
|
1014
|
-
|
|
1015
|
-
cid = await self.cond_new(cleaned, {int(ad.userId)})
|
|
1016
|
-
return await self.ad_create(ad, cid, rname, ps), True
|
|
1017
|
-
|
|
1018
|
-
async def cond_new(self, txt: str, uids: set[int]) -> int:
|
|
1019
|
-
new_cond, _ = await Cond.update_or_create(raw_txt=txt)
|
|
1020
|
-
# и максимально похожую связь для нового условия (если есть >= 60%)
|
|
1021
|
-
await self.cond_upd(new_cond, uids)
|
|
1022
|
-
return new_cond.id
|
|
1023
|
-
|
|
1024
|
-
async def cond_upd(self, cond: Cond, uids: set[int]):
|
|
1025
|
-
self.all_conds[cond.id] = cond.raw_txt, uids
|
|
1026
|
-
# и максимально похожую связь для нового условия (если есть >= 60%)
|
|
1027
|
-
old_cid, sim = await self.cond_get_max_sim(cond.id, cond.raw_txt, uids)
|
|
1028
|
-
await self.actual_sim(cond.id, old_cid, sim)
|
|
1029
|
-
|
|
1030
|
-
def find_in_tree(self, cid: int, old_cid: int) -> bool:
|
|
1031
|
-
if p := self.cond_sims.get(old_cid):
|
|
1032
|
-
if p == cid:
|
|
1033
|
-
return True
|
|
1034
|
-
return self.find_in_tree(cid, p)
|
|
1035
|
-
return False
|
|
1036
|
-
|
|
1037
|
-
async def cond_get_max_sim(self, cid: int, txt: str, uids: set[int]) -> tuple[int | None, int | None]:
|
|
1038
|
-
# находим все старые тексты похожие на 90% и более
|
|
1039
|
-
if len(txt) < 15:
|
|
1040
|
-
return None, None
|
|
1041
|
-
sims: dict[int, int] = {}
|
|
1042
|
-
for old_cid, (old_txt, old_uids) in self.all_conds.items():
|
|
1043
|
-
if len(old_txt) < 15 or uids == old_uids:
|
|
1044
|
-
continue
|
|
1045
|
-
elif not self.can_add_sim(cid, old_cid):
|
|
1046
|
-
continue
|
|
1047
|
-
if sim := get_sim(txt, old_txt):
|
|
1048
|
-
sims[old_cid] = sim
|
|
1049
|
-
# если есть, берем самый похожий из них
|
|
1050
|
-
if sims:
|
|
1051
|
-
old_cid, sim = max(sims.items(), key=lambda x: x[1])
|
|
1052
|
-
await sleep(0.3)
|
|
1053
|
-
return old_cid, sim
|
|
1054
|
-
return None, None
|
|
1055
|
-
|
|
1056
|
-
def can_add_sim(self, cid: int, old_cid: int) -> bool:
|
|
1057
|
-
if cid == old_cid:
|
|
1058
|
-
return False
|
|
1059
|
-
elif self.cond_sims.get(cid) == old_cid:
|
|
1060
|
-
return False
|
|
1061
|
-
elif self.find_in_tree(cid, old_cid):
|
|
1062
|
-
return False
|
|
1063
|
-
elif self.cond_sims.get(old_cid) == cid:
|
|
1064
|
-
return False
|
|
1065
|
-
elif cid in self.rcond_sims.get(old_cid, {}):
|
|
1066
|
-
return False
|
|
1067
|
-
elif old_cid in self.rcond_sims.get(cid, {}):
|
|
1068
|
-
return False
|
|
1069
|
-
return True
|
|
1070
|
-
|
|
1071
|
-
async def person_upsert(self, name: str, exid: int) -> models.Person:
|
|
1072
|
-
if actor := await models.Actor.get_or_none(exid=exid, ex=self.ex_client.ex).prefetch_related("person"):
|
|
1073
|
-
if not actor.person:
|
|
1074
|
-
actor.person = await models.Person.create(name=name)
|
|
1075
|
-
await actor.save()
|
|
1076
|
-
return actor.person
|
|
1077
|
-
return await models.Person.create(name=name)
|
|
1078
|
-
|
|
1079
|
-
async def ad_create(
|
|
1080
|
-
self, ad: Ad, cid: int = None, rname: str = None, ps: PairSide = None, actor: Actor = None
|
|
1081
|
-
) -> models.Ad:
|
|
1082
|
-
act_df = {}
|
|
1083
|
-
if int(ad.userId) != self.actor.exid:
|
|
1084
|
-
act_df |= {"name": ad.nickName}
|
|
1085
|
-
if rname:
|
|
1086
|
-
act_df |= {"person": await self.person_upsert(rname, int(ad.userId))}
|
|
1087
|
-
if not actor:
|
|
1088
|
-
act_df |= {"person": await self.person_upsert(ad.nickName, int(ad.userId))}
|
|
1089
|
-
actor, _ = await Actor.update_or_create(act_df, exid=ad.userId, ex=self.ex_client.ex)
|
|
1090
|
-
ps = ps or await PairSide.get_or_none(
|
|
1091
|
-
is_sell=ad.side,
|
|
1092
|
-
pair__coin__ticker=ad.tokenId,
|
|
1093
|
-
pair__cur__ticker=ad.currencyId,
|
|
1094
|
-
).prefetch_related("pair__cur", "pair__coin")
|
|
1095
|
-
if not ps or not ps.pair:
|
|
1096
|
-
... # THB/USDC
|
|
1097
|
-
ad_upd = models.Ad.validate(ad.model_dump(by_alias=True))
|
|
1098
|
-
cur_scale = 10**ps.pair.cur.scale
|
|
1099
|
-
coinex = await models.CoinEx.get(coin_id=ps.pair.coin_id, ex=self.ex_client.ex)
|
|
1100
|
-
df_unq = ad_upd.df_unq(
|
|
1101
|
-
maker_id=actor.id,
|
|
1102
|
-
pair_side_id=ps.id,
|
|
1103
|
-
amount=int(float(ad.quantity) * float(ad.price) * cur_scale),
|
|
1104
|
-
quantity=int(float(ad.quantity) * 10**coinex.scale),
|
|
1105
|
-
min_fiat=int(float(ad.minAmount) * cur_scale),
|
|
1106
|
-
max_fiat=ad.maxAmount and int(float(ad.maxAmount) * cur_scale),
|
|
1107
|
-
price=int(float(ad.price) * cur_scale),
|
|
1108
|
-
premium=int(float(ad.premium) * cur_scale),
|
|
947
|
+
async def take_ad(self, req: TakeAdReq):
|
|
948
|
+
ad: Ad = Ad.model_validate(self.api.get_ad_details(itemId=req.ad_id))
|
|
949
|
+
bor = BaseOrderReq(
|
|
950
|
+
ad_id=str(ad.id),
|
|
951
|
+
fiat_amount=req.amount,
|
|
952
|
+
is_sell=bool(ad.side),
|
|
953
|
+
cur_exid=ad.currencyId,
|
|
954
|
+
coin_exid=ad.tokenId,
|
|
955
|
+
coin_scale=ad.token.scale,
|
|
956
|
+
pm_id=req.pm_id,
|
|
1109
957
|
)
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
return node
|
|
1175
|
-
for key in node:
|
|
1176
|
-
subnode = tree.pop(key, {})
|
|
1177
|
-
d = subtree(subnode)
|
|
1178
|
-
node[key] |= d # actual tree rebuilding here!
|
|
1179
|
-
return node # todo: refact?
|
|
1180
|
-
|
|
1181
|
-
# Находим корни / без родителей
|
|
1182
|
-
roots = set(self.cond_sims.values()) - set(self.cond_sims.keys())
|
|
1183
|
-
for root in roots:
|
|
1184
|
-
_ = subtree(tree[root])
|
|
1185
|
-
|
|
1186
|
-
self.tree = tree
|
|
958
|
+
resp: OrderResp = await self._order_request(bor)
|
|
959
|
+
return resp
|
|
960
|
+
|
|
961
|
+
# async def parse_ads(
|
|
962
|
+
# self,
|
|
963
|
+
# coinex: models.CoinEx,
|
|
964
|
+
# curex: models.CurEx,
|
|
965
|
+
# taker_side: bool,
|
|
966
|
+
# pms: list[str] = None,
|
|
967
|
+
# ceil: float = None,
|
|
968
|
+
# volume: float = 9000,
|
|
969
|
+
# min_fiat: int = None,
|
|
970
|
+
# max_fiat: int = None,
|
|
971
|
+
# ):
|
|
972
|
+
# k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
|
|
973
|
+
# if pms:
|
|
974
|
+
# creds: dict[models.PmEx, models.CredEx] = await self.get_credexs_by_norms(pms, curex.cur_id)
|
|
975
|
+
# [str(p.exid) for p in creds.values()]
|
|
976
|
+
#
|
|
977
|
+
# if taker_side: # гонка в стакане продажи - мы покупаем монету за ФИАТ
|
|
978
|
+
# fiats = await models.Fiat.filter(
|
|
979
|
+
# cred_id__in=[cx.cred_id for cx in creds.values()], amount__not=F("target")
|
|
980
|
+
# )
|
|
981
|
+
# volume = min(volume, max(fiats, key=lambda f: f.target - f.amount).amount / ceil)
|
|
982
|
+
# else: # гонка в стакане покупки - мы продаем МОНЕТУ за фиат
|
|
983
|
+
# asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
|
|
984
|
+
# volume = min(volume, asset.free)
|
|
985
|
+
# volume = str(round(volume, coinex.coin.scale))
|
|
986
|
+
# ps = await PairSide.get(
|
|
987
|
+
# is_sell=taker_side,
|
|
988
|
+
# pair__coin_id=coinex.coin_id,
|
|
989
|
+
# pair__cur_id=curex.cur_id,
|
|
990
|
+
# )
|
|
991
|
+
# while self.actor.person.user.status > 0: # todo: depends on rest asset/fiat
|
|
992
|
+
# ads: list[Ad] = await self.ads(coinex, curex, taker_side, pms and list(creds.keys()))
|
|
993
|
+
#
|
|
994
|
+
# if not ads:
|
|
995
|
+
# print(coinex.exid, curex.exid, taker_side, "no ads!")
|
|
996
|
+
# await sleep(300)
|
|
997
|
+
# continue
|
|
998
|
+
#
|
|
999
|
+
# for i, ad in enumerate(ads):
|
|
1000
|
+
# if (ceil - float(ad.price)) * k < 0:
|
|
1001
|
+
# break
|
|
1002
|
+
# if int(ad.userId) == self.actor.exid:
|
|
1003
|
+
# logging.info(f"My ad {'-' if taker_side else '+'}{coinex.exid}/{curex.exid} on place#{i}")
|
|
1004
|
+
# continue
|
|
1005
|
+
# ad_db, isnew = await self.cond_upsert(ad, ps=ps)
|
|
1006
|
+
# if isnew:
|
|
1007
|
+
# s = f"{'-' if taker_side else '+'}{ad.price}[{ad.minAmount}-{ad.maxAmount}]{coinex.exid}/{curex.exid}"
|
|
1008
|
+
# print(s, end=" | ", flush=True)
|
|
1009
|
+
# try:
|
|
1010
|
+
# # take
|
|
1011
|
+
# ...
|
|
1012
|
+
# except FailedRequestError as e:
|
|
1013
|
+
# if ExcCode(e.status_code) == ExcCode.RareLimit:
|
|
1014
|
+
# await sleep(195)
|
|
1015
|
+
# elif ExcCode(e.status_code) == ExcCode.Timestamp:
|
|
1016
|
+
# await sleep(2)
|
|
1017
|
+
# else:
|
|
1018
|
+
# raise e
|
|
1019
|
+
# except (ReadTimeoutError, ConnectionDoesNotExistError):
|
|
1020
|
+
# logging.warning("Connection failed. Restarting..")
|
|
1021
|
+
# await sleep(3)
|
|
1187
1022
|
|
|
1188
1023
|
|
|
1189
1024
|
def ms2utc(msk_ts_str: str):
|
|
1190
1025
|
return datetime.fromtimestamp(int(msk_ts_str) / 1000, timezone(timedelta(hours=3), name="MSK"))
|
|
1191
1026
|
|
|
1192
1027
|
|
|
1193
|
-
def get_sim(s1, s2) -> int:
|
|
1194
|
-
sim = int((SequenceMatcher(None, s1, s2).ratio() - 0.6) * 10_000)
|
|
1195
|
-
return sim if sim > 0 else 0
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
1028
|
def detailed_diff(str1, str2):
|
|
1199
1029
|
matcher = SequenceMatcher(None, str1, str2)
|
|
1200
1030
|
result = []
|
|
@@ -1212,14 +1042,6 @@ def detailed_diff(str1, str2):
|
|
|
1212
1042
|
return "".join(result)
|
|
1213
1043
|
|
|
1214
1044
|
|
|
1215
|
-
def clean(s) -> str:
|
|
1216
|
-
clear = r"[^\w\s.,!?;:()\-]"
|
|
1217
|
-
repeat = r"(.)\1{2,}"
|
|
1218
|
-
s = re.sub(clear, "", s).lower()
|
|
1219
|
-
s = re.sub(repeat, r"\1", s)
|
|
1220
|
-
return s.replace("\n\n", "\n").replace(" ", " ").strip(" \n/.,!?-")
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
1045
|
def step_is_need(mad, cad) -> bool:
|
|
1224
1046
|
# todo: пока не решен непонятный кейс, почему то конкурент по всем параметрам слабже, но в списке ранжируется выше.
|
|
1225
1047
|
# текущая версия: recentExecuteRate округляется до целого, но на бэке байбита его дробная часть больше
|
|
@@ -1262,20 +1084,20 @@ async def main():
|
|
|
1262
1084
|
else: # параметры гонки изменены
|
|
1263
1085
|
...
|
|
1264
1086
|
|
|
1265
|
-
|
|
1087
|
+
agent = (
|
|
1266
1088
|
await models.Agent.filter(actor__ex_id=4, auth__isnull=False, active=True)
|
|
1267
|
-
.prefetch_related("actor__ex", "
|
|
1089
|
+
.prefetch_related("actor__ex", "actor__person__user__gmail")
|
|
1268
1090
|
.first()
|
|
1269
1091
|
)
|
|
1270
1092
|
filebot = FileClient(NET_TOKEN)
|
|
1271
1093
|
await filebot.start()
|
|
1272
1094
|
# b.add_handler(MessageHandler(cond_start_handler, command("cond")))
|
|
1273
|
-
cl: AgentClient =
|
|
1095
|
+
cl: AgentClient = agent.client(filebot, XyncBot(PAY_TOKEN, cn))
|
|
1274
1096
|
|
|
1275
1097
|
# await cl.ex_client.set_pairs()
|
|
1276
1098
|
# await cl.ex_client.set_pms()
|
|
1277
|
-
|
|
1278
|
-
|
|
1099
|
+
await cl.set_creds()
|
|
1100
|
+
await cl.export_my_ads()
|
|
1279
1101
|
|
|
1280
1102
|
# создание гонок по мои активным объявам:
|
|
1281
1103
|
# for ma in cl.my_ads():
|
|
@@ -1289,28 +1111,6 @@ async def main():
|
|
|
1289
1111
|
# s, _ = await models.Synonym.update_or_create(typ=SynonymType.name, txt=name)
|
|
1290
1112
|
# await s.curs.add(rub.cur)
|
|
1291
1113
|
|
|
1292
|
-
# пока порешали рейс-кондишн, очищаем сиротские условия при каждом запуске
|
|
1293
|
-
# [await c.delete() for c in await Cond.filter(ads__isnull=True)]
|
|
1294
|
-
cl.all_conds = {
|
|
1295
|
-
c.id: (c.raw_txt, {a.maker.exid for a in c.ads}) for c in await Cond.all().prefetch_related("ads__maker")
|
|
1296
|
-
}
|
|
1297
|
-
for curr, old in await CondSim.filter().values_list("cond_id", "cond_rel_id"):
|
|
1298
|
-
cl.cond_sims[curr] = old
|
|
1299
|
-
cl.rcond_sims[old] |= {curr}
|
|
1300
|
-
|
|
1301
|
-
cl.build_tree()
|
|
1302
|
-
a = set()
|
|
1303
|
-
|
|
1304
|
-
def check_tree(tre):
|
|
1305
|
-
for p, c in tre.items():
|
|
1306
|
-
a.add(p)
|
|
1307
|
-
check_tree(c)
|
|
1308
|
-
|
|
1309
|
-
for pr, ch in cl.tree.items():
|
|
1310
|
-
check_tree(ch)
|
|
1311
|
-
if ct := set(cl.tree.keys()) & a:
|
|
1312
|
-
logging.exception(f"cycle cids: {ct}")
|
|
1313
|
-
|
|
1314
1114
|
pauth = (await models.PmAgent[1]).auth
|
|
1315
1115
|
papi = PayeerAPI(pauth["email"], pauth["api_id"], pauth["api_sec"])
|
|
1316
1116
|
hist: dict = papi.history(count=1000)
|
|
@@ -1323,7 +1123,7 @@ async def main():
|
|
|
1323
1123
|
# )
|
|
1324
1124
|
# await cl.get_api_orders() # 43, 1741294800000, 1749157199999)
|
|
1325
1125
|
|
|
1326
|
-
races = await models.Race.filter(started=True).prefetch_related(
|
|
1126
|
+
races = await models.Race.filter(started=True, road__ad__maker_id=agent.actor_id).prefetch_related(
|
|
1327
1127
|
"road__ad__pair_side__pair__cur",
|
|
1328
1128
|
"road__ad__pms",
|
|
1329
1129
|
)
|
|
@@ -1335,20 +1135,10 @@ async def main():
|
|
|
1335
1135
|
# cl.get_api_orders(), # 10, 1738357200000, 1742504399999
|
|
1336
1136
|
)
|
|
1337
1137
|
except Exception as e:
|
|
1338
|
-
await filebot.send("🤬Bybit agent CRASHED!!!🤬", actor.person.user.username_id)
|
|
1339
|
-
await filebot.send(e.__repr__(), actor.person.user.username_id)
|
|
1138
|
+
await filebot.send("🤬Bybit agent CRASHED!!!🤬", agent.actor.person.user.username_id)
|
|
1139
|
+
await filebot.send(e.__repr__(), agent.actor.person.user.username_id)
|
|
1340
1140
|
raise e
|
|
1341
|
-
|
|
1342
|
-
# ad_id="1861440060199632896",
|
|
1343
|
-
# # asset_amount=40,
|
|
1344
|
-
# fiat_amount=3000,
|
|
1345
|
-
# amount_is_fiat=True,
|
|
1346
|
-
# is_sell=False,
|
|
1347
|
-
# cur_exid=rub.exid,
|
|
1348
|
-
# coin_exid=usdt.exid,
|
|
1349
|
-
# coin_scale=usdt.coin.scale,
|
|
1350
|
-
# )
|
|
1351
|
-
# res: OrderResp = await cl.order_request(bor)
|
|
1141
|
+
|
|
1352
1142
|
# await cl.cancel_order(res.orderId)
|
|
1353
1143
|
await filebot.stop()
|
|
1354
1144
|
await cl.close()
|