xync-client 0.0.155__py3-none-any.whl → 0.0.156.dev18__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/AdLoader.py +0 -294
- xync_client/Abc/Agent.py +313 -51
- xync_client/Abc/Ex.py +414 -12
- xync_client/Abc/xtype.py +34 -2
- xync_client/Bybit/InAgent.py +225 -436
- xync_client/Bybit/agent.py +529 -397
- xync_client/Bybit/etype/__init__.py +0 -0
- xync_client/Bybit/etype/ad.py +47 -34
- xync_client/Bybit/etype/order.py +33 -48
- xync_client/Bybit/ex.py +20 -46
- xync_client/Htx/agent.py +82 -40
- xync_client/Htx/etype/ad.py +22 -5
- xync_client/Htx/etype/order.py +194 -0
- xync_client/Htx/ex.py +16 -16
- xync_client/Mexc/agent.py +196 -13
- xync_client/Mexc/api.py +955 -336
- xync_client/Mexc/etype/ad.py +52 -1
- xync_client/Mexc/etype/order.py +131 -416
- xync_client/Mexc/ex.py +29 -19
- xync_client/Okx/1.py +14 -0
- xync_client/Okx/agent.py +39 -0
- xync_client/Okx/ex.py +8 -8
- xync_client/Pms/Payeer/agent.py +396 -0
- xync_client/Pms/Payeer/login.py +1 -63
- xync_client/Pms/Payeer/trade.py +58 -0
- xync_client/loader.py +1 -0
- {xync_client-0.0.155.dist-info → xync_client-0.0.156.dev18.dist-info}/METADATA +2 -1
- {xync_client-0.0.155.dist-info → xync_client-0.0.156.dev18.dist-info}/RECORD +30 -26
- xync_client/Pms/Payeer/__init__.py +0 -262
- xync_client/Pms/Payeer/api.py +0 -25
- {xync_client-0.0.155.dist-info → xync_client-0.0.156.dev18.dist-info}/WHEEL +0 -0
- {xync_client-0.0.155.dist-info → xync_client-0.0.156.dev18.dist-info}/top_level.txt +0 -0
xync_client/Bybit/agent.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import json
|
|
2
3
|
import logging
|
|
3
4
|
import re
|
|
4
5
|
from asyncio import sleep, gather
|
|
@@ -10,13 +11,15 @@ from hashlib import sha256
|
|
|
10
11
|
from http.client import HTTPException
|
|
11
12
|
from math import floor
|
|
12
13
|
from typing import Literal
|
|
14
|
+
from uuid import uuid4
|
|
13
15
|
|
|
14
16
|
import pyotp
|
|
17
|
+
import websockets
|
|
15
18
|
from aiohttp.http_exceptions import HttpProcessingError
|
|
16
|
-
from asyncpg import ConnectionDoesNotExistError
|
|
17
19
|
from bybit_p2p import P2P
|
|
18
20
|
from bybit_p2p._exceptions import FailedRequestError
|
|
19
21
|
from payeer_api import PayeerAPI
|
|
22
|
+
from pydantic import ValidationError
|
|
20
23
|
from pyro_client.client.file import FileClient
|
|
21
24
|
from tortoise import BaseDBAsyncClient
|
|
22
25
|
from tortoise.exceptions import IntegrityError
|
|
@@ -24,12 +27,11 @@ from tortoise.expressions import Q
|
|
|
24
27
|
from tortoise.functions import Count
|
|
25
28
|
from tortoise.signals import post_save
|
|
26
29
|
from tortoise.timezone import now
|
|
27
|
-
from
|
|
30
|
+
from tortoise.transactions import in_transaction
|
|
28
31
|
from x_client import df_hdrs
|
|
29
32
|
from x_model import init_db
|
|
30
33
|
from x_model.func import ArrayAgg
|
|
31
34
|
from xync_bot import XyncBot
|
|
32
|
-
from xync_client.Bybit.InAgent import InAgentClient
|
|
33
35
|
|
|
34
36
|
from xync_client.Bybit.ex import ExClient
|
|
35
37
|
from xync_schema import models
|
|
@@ -38,7 +40,7 @@ from xync_schema.enums import OrderStatus, AgentStatus
|
|
|
38
40
|
from xync_schema.models import Actor, PmCur, Agent
|
|
39
41
|
|
|
40
42
|
from xync_client.Abc.Agent import BaseAgentClient
|
|
41
|
-
from xync_client.Abc.xtype import FlatDict, BaseOrderReq
|
|
43
|
+
from xync_client.Abc.xtype import FlatDict, BaseOrderReq, AdUpd, GetAds
|
|
42
44
|
from xync_client.Bybit.etype.ad import AdPostRequest, AdUpdateRequest, Ad, AdStatus, MyAd
|
|
43
45
|
from xync_client.Bybit.etype.cred import CredEpyd
|
|
44
46
|
from xync_client.Bybit.etype.order import (
|
|
@@ -52,15 +54,25 @@ from xync_client.Bybit.etype.order import (
|
|
|
52
54
|
Status,
|
|
53
55
|
OrderSellRequest,
|
|
54
56
|
TakeAdReq,
|
|
57
|
+
StatusChange,
|
|
58
|
+
CountDown,
|
|
59
|
+
Receive,
|
|
60
|
+
Read,
|
|
61
|
+
SellerCancelChange,
|
|
55
62
|
)
|
|
56
|
-
from xync_client.
|
|
63
|
+
from xync_client.Pms.Payeer.agent import PmAgentClient
|
|
64
|
+
from xync_client.loader import TORM, NET_TOKEN, PAY_TOKEN, PRX
|
|
57
65
|
|
|
58
66
|
|
|
59
67
|
class NoMakerException(Exception):
|
|
60
68
|
pass
|
|
61
69
|
|
|
62
70
|
|
|
63
|
-
class
|
|
71
|
+
class ShareException(Exception):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class AgentClient(BaseAgentClient): # Bybit client
|
|
64
76
|
headers = df_hdrs | {"accept-language": "ru-RU"}
|
|
65
77
|
sec_hdrs: dict[str, str]
|
|
66
78
|
# rewrite token for public methods
|
|
@@ -94,8 +106,16 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
94
106
|
"securityRiskToken": "",
|
|
95
107
|
}
|
|
96
108
|
|
|
97
|
-
def __init__(
|
|
98
|
-
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
agent: Agent,
|
|
112
|
+
ex_client: ExClient,
|
|
113
|
+
fbot: FileClient,
|
|
114
|
+
bbot: XyncBot,
|
|
115
|
+
pm_clients: dict[int, PmAgentClient] = None,
|
|
116
|
+
**kwargs,
|
|
117
|
+
):
|
|
118
|
+
super().__init__(agent, ex_client, fbot, bbot, pm_clients, **kwargs)
|
|
99
119
|
self.sec_hdrs = {
|
|
100
120
|
"accept-language": "ru,en;q=0.9",
|
|
101
121
|
"gdfp": agent.auth["Risktoken"],
|
|
@@ -169,17 +189,23 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
169
189
|
xtr += (" | " if xtr else "") + ecdx.qrcode
|
|
170
190
|
elif ecdx.paymentExt1:
|
|
171
191
|
xtr += (" | " if xtr else "") + ecdx.paymentExt1
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
try:
|
|
193
|
+
cred_db, _ = await models.Cred.update_or_create(
|
|
194
|
+
{
|
|
195
|
+
"name": ecdx.realName,
|
|
196
|
+
"extra": xtr,
|
|
197
|
+
},
|
|
198
|
+
pmcur=pmcur,
|
|
199
|
+
person_id=pers_id or self.actor.person_id,
|
|
200
|
+
detail=ecdx.accountNo or ecdx.payMessage,
|
|
201
|
+
)
|
|
202
|
+
if cred_db.ovr_pm_id is None and (cred_db.detail.startswith("XyncPay") or xtr.startswith("XyncPay")):
|
|
203
|
+
cred_db.ovr_pm_id = 0
|
|
204
|
+
await cred_db.save()
|
|
205
|
+
credex_in = models.CredEx.validate({"exid": ecdx.id, "cred_id": cred_db.id, "ex_id": self.actor.ex.id})
|
|
206
|
+
credex_db, _ = await models.CredEx.update_or_create(**credex_in.df_unq())
|
|
207
|
+
except IntegrityError as e:
|
|
208
|
+
raise e
|
|
183
209
|
return credex_db
|
|
184
210
|
|
|
185
211
|
async def guess_cur(self, ecdx: CredEpyd, curs: list[models.Cur]):
|
|
@@ -245,33 +271,6 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
245
271
|
res = await self._post("/x-api/fiat/otc/maker/work-config/switch", data)
|
|
246
272
|
return res
|
|
247
273
|
|
|
248
|
-
async def ads(
|
|
249
|
-
self,
|
|
250
|
-
cnx: models.CoinEx,
|
|
251
|
-
crx: models.CurEx,
|
|
252
|
-
is_sell: bool,
|
|
253
|
-
pmexs: list[models.PmEx],
|
|
254
|
-
amount: int = None,
|
|
255
|
-
lim: int = 50,
|
|
256
|
-
vm_filter: bool = False,
|
|
257
|
-
post_pmexs: set[models.PmEx] = None,
|
|
258
|
-
) -> list[Ad]:
|
|
259
|
-
if post_pmexs:
|
|
260
|
-
pm_exids = None
|
|
261
|
-
lim = min(1000, lim * 25)
|
|
262
|
-
post_pmexids = {p.exid for p in post_pmexs}
|
|
263
|
-
else:
|
|
264
|
-
pm_exids = [px.exid for px in pmexs]
|
|
265
|
-
post_pmexids = set()
|
|
266
|
-
ads: list[Ad] = await self.ex_client.ads(cnx.exid, crx.exid, is_sell, pm_exids, amount, lim, vm_filter)
|
|
267
|
-
if post_pmexs:
|
|
268
|
-
ads = [
|
|
269
|
-
ad
|
|
270
|
-
for ad in ads
|
|
271
|
-
if (set(ad.payments) & post_pmexids or [True for px in post_pmexs if px.pm.norm in ad.remark.lower()])
|
|
272
|
-
]
|
|
273
|
-
return ads
|
|
274
|
-
|
|
275
274
|
@staticmethod
|
|
276
275
|
def get_rate(list_ads: list) -> float:
|
|
277
276
|
ads = [ad for ad in list_ads if set(ad["payments"]) - {"5", "51"}]
|
|
@@ -292,10 +291,39 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
292
291
|
ad_db = await self.ex_client.ad_load(ad, maker=self.actor)
|
|
293
292
|
mad_db, _ = await models.MyAd.update_or_create(ad=ad_db)
|
|
294
293
|
exids = [pt.id for pt in ad.paymentTerms]
|
|
295
|
-
credexs = await models.CredEx.filter(ex_id=self.actor.ex_id, exid__in=exids)
|
|
294
|
+
credexs = await models.CredEx.filter(ex_id=self.actor.ex_id, exid__in=exids).prefetch_related("cred")
|
|
296
295
|
await mad_db.credexs.add(*credexs)
|
|
296
|
+
# share
|
|
297
|
+
if [cx for cx in credexs if cx.cred.ovr_pm_id == 0]:
|
|
298
|
+
try:
|
|
299
|
+
await self.ad_share(mad_db.id)
|
|
300
|
+
except ShareException as e:
|
|
301
|
+
logging.warning(e.args[0])
|
|
302
|
+
await sleep(1)
|
|
303
|
+
|
|
297
304
|
return len(ads)
|
|
298
305
|
|
|
306
|
+
async def ad_share(self, maid: int):
|
|
307
|
+
myad = await models.MyAd.get(id=maid).prefetch_related("ad")
|
|
308
|
+
if myad.hex and myad.shared_at + timedelta(minutes=55) > now(): # check expired
|
|
309
|
+
# check validity
|
|
310
|
+
data = await self._post("/x-api/fiat/otc/item/shareItem/info", {"shareCode": myad.hex.hex()})
|
|
311
|
+
if data["ret_code"] == 0:
|
|
312
|
+
return myad.get_url()
|
|
313
|
+
data = await self._post("/x-api/fiat/otc/item/share", {"itemId": str(myad.ad.exid)})
|
|
314
|
+
if data["ret_code"] == 912300058:
|
|
315
|
+
raise ShareException(f"Объява {myad.id}:{myad.ad.id}:{myad.ad.exid} выключена")
|
|
316
|
+
if data["ret_code"] == 912300059:
|
|
317
|
+
raise ShareException("Торговля выключена")
|
|
318
|
+
if data["ret_code"] != 0: # Новая ошибка
|
|
319
|
+
raise ShareException(data)
|
|
320
|
+
url = data["result"]["shareLink"]
|
|
321
|
+
resp = await self.session.get(url)
|
|
322
|
+
hx = resp.url.query["by_web_link"].replace(models.MyAd.WEB, "")
|
|
323
|
+
await models.MyAd.filter(id=maid).update(hex=bytes.fromhex(hx), shared_at=now())
|
|
324
|
+
await myad.refresh_from_db()
|
|
325
|
+
return myad.get_url()
|
|
326
|
+
|
|
299
327
|
def get_security_token_create(self):
|
|
300
328
|
data = self._post("/x-api/fiat/otc/item/create", self.create_ad_body)
|
|
301
329
|
if data["ret_code"] == 912120019: # Current user can not to create add as maker
|
|
@@ -342,6 +370,9 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
342
370
|
return self.__get_2fa(typ, rt)
|
|
343
371
|
raise Exception("2fa fail")
|
|
344
372
|
|
|
373
|
+
def get_ad(self, aid: int) -> Ad:
|
|
374
|
+
return Ad(**self.api.get_ad_details(itemId=aid)["result"])
|
|
375
|
+
|
|
345
376
|
def _post_ad(self, risk_token: str):
|
|
346
377
|
self.create_ad_body.update({"securityRiskToken": risk_token})
|
|
347
378
|
data = self._post("/x-api/fiat/otc/item/create", self.create_ad_body)
|
|
@@ -363,7 +394,8 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
363
394
|
data = self.api.post_new_ad(**ad.model_dump())
|
|
364
395
|
return data["result"]["itemId"] if data["ret_code"] == 0 else data
|
|
365
396
|
|
|
366
|
-
def
|
|
397
|
+
async def _ad_upd(self, req: AdUpd):
|
|
398
|
+
upd = AdUpdateRequest({})
|
|
367
399
|
params = upd.model_dump()
|
|
368
400
|
data = self.api.update_ad(**params)
|
|
369
401
|
return data["result"] if data["ret_code"] == 0 else data
|
|
@@ -487,10 +519,10 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
487
519
|
{"makerUserId": self.actor.exid, "page": "1", "size": "10", "appraiseType": "1"}, # "0" - bad
|
|
488
520
|
)
|
|
489
521
|
|
|
490
|
-
async def
|
|
522
|
+
async def get_pending_orders(
|
|
491
523
|
self, side: int = None, status: int = None, begin_time: int = None, end_time: int = None, token_id: str = None
|
|
492
524
|
):
|
|
493
|
-
|
|
525
|
+
res = await self._post(
|
|
494
526
|
"/x-api/fiat/otc/order/pending/simplifyList",
|
|
495
527
|
{
|
|
496
528
|
"status": status,
|
|
@@ -499,9 +531,12 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
499
531
|
"endTime": end_time,
|
|
500
532
|
"side": side, # 1 - продажа, 0 - покупка
|
|
501
533
|
"page": 1,
|
|
502
|
-
"size":
|
|
534
|
+
"size": 20,
|
|
503
535
|
},
|
|
504
536
|
)
|
|
537
|
+
if res["ret_code"] == 0:
|
|
538
|
+
return {o.id: OrderItem(**o) for o in res["result"]["items"]}
|
|
539
|
+
return res["ret_code"]
|
|
505
540
|
|
|
506
541
|
def get_orders_done(self, begin_time: int, end_time: int, status: int, side: int, token_id: str):
|
|
507
542
|
return self._post(
|
|
@@ -517,27 +552,27 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
517
552
|
},
|
|
518
553
|
)
|
|
519
554
|
|
|
520
|
-
async def
|
|
521
|
-
# ad = Ad(**self.api.get_ad_details(itemId=order.itemId)["result"])
|
|
522
|
-
await sleep(1)
|
|
555
|
+
async def create_order_db(self, order: OrderFull) -> models.Order:
|
|
523
556
|
curex = await models.CurEx.get_or_none(ex=self.ex_client.ex, exid=order.currencyId).prefetch_related("cur")
|
|
524
557
|
cur_scale = (curex.scale if curex.scale is not None else curex.cur.scale) if curex else 2
|
|
525
558
|
coinex = await models.CoinEx.get(ex=self.ex_client.ex, exid=order.tokenId).prefetch_related("coin")
|
|
526
559
|
coin_scale = coinex.scale if coinex.scale is not None else coinex.cur.scale
|
|
527
|
-
|
|
528
|
-
im_maker = int(order.makerUserId ==
|
|
560
|
+
sb_names = order.sellerRealName, order.buyerRealName
|
|
561
|
+
im_maker = int(int(order.makerUserId) == self.actor.exid)
|
|
529
562
|
taker_id = (order.userId, order.targetUserId)[im_maker]
|
|
530
|
-
|
|
563
|
+
taker_name = sb_names[order.side] # todo: double check
|
|
564
|
+
taker_person = await self.ex_client.person_name_update(taker_name, taker_id)
|
|
531
565
|
seller_person = (
|
|
532
566
|
self.actor.person
|
|
533
567
|
if order.side
|
|
534
568
|
else await self.ex_client.person_name_update(order.sellerRealName, int(order.targetUserId))
|
|
535
569
|
)
|
|
536
570
|
taker_nick = (self.actor.name, order.targetNickName)[im_maker] # todo: check
|
|
537
|
-
# ad_db, cond_isnew = await self.ex_client.cond_load(ad, force=True, rname=maker_name[order.side])
|
|
538
571
|
ad_db = await models.Ad.get(exid=order.itemId)
|
|
539
572
|
if not ad_db:
|
|
540
|
-
|
|
573
|
+
ad = self.get_ad(order.itemId)
|
|
574
|
+
# ad_db, cond_isnew = await self.ex_client.cond_load(ad, force=True, rname=maker_name[order.side])
|
|
575
|
+
ad_db = await self.ex_client.ad_load(ad, maker=self.actor)
|
|
541
576
|
ecredex: CredEpyd = order.confirmedPayTerm
|
|
542
577
|
|
|
543
578
|
if ecredex.paymentType == 0 and im_maker and order.side:
|
|
@@ -695,7 +730,7 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
695
730
|
continue
|
|
696
731
|
fo = self.api.get_order_details(orderId=o.id)
|
|
697
732
|
order = OrderFull.model_validate(fo["result"])
|
|
698
|
-
order_db = await self.
|
|
733
|
+
order_db = await self.create_order_db(order)
|
|
699
734
|
await sleep(1)
|
|
700
735
|
dmsgs = self.api.get_chat_messages(orderId=oid, size=200)["result"]["result"][::-1]
|
|
701
736
|
msgs = [Message.model_validate(m) for m in dmsgs if m["msgType"] in (1, 2, 7, 8)]
|
|
@@ -720,54 +755,6 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
720
755
|
# for t in papi.history():
|
|
721
756
|
# os = self.api.get_orders(page=1, size=30)
|
|
722
757
|
|
|
723
|
-
async def mad_upd(self, mad: Ad, attrs: dict, cxids: list[str]):
|
|
724
|
-
if not [setattr(mad, k, v) for k, v in attrs.items() if getattr(mad, k) != v]:
|
|
725
|
-
print(end="v" if mad.side else "^", flush=True)
|
|
726
|
-
return await sleep(5)
|
|
727
|
-
req = AdUpdateRequest.model_validate({**mad.model_dump(), "paymentIds": cxids})
|
|
728
|
-
try:
|
|
729
|
-
return self.ad_upd(req)
|
|
730
|
-
except FailedRequestError as e:
|
|
731
|
-
if ExcCode(e.status_code) == ExcCode.FixPriceLimit:
|
|
732
|
-
if limits := re.search(
|
|
733
|
-
r"The fixed price set is lower than ([0-9]+\.?[0-9]{0,2}) or higher than ([0-9]+\.?[0-9]{0,2})",
|
|
734
|
-
e.message,
|
|
735
|
-
):
|
|
736
|
-
return await self.mad_upd(mad, {"price": limits.group(1 if mad.side else 2)}, cxids)
|
|
737
|
-
elif ExcCode(e.status_code) == ExcCode.RareLimit:
|
|
738
|
-
await sleep(180)
|
|
739
|
-
else:
|
|
740
|
-
raise e
|
|
741
|
-
except (ReadTimeoutError, ConnectionDoesNotExistError):
|
|
742
|
-
logging.warning("Connection failed. Restarting..")
|
|
743
|
-
print("-" if mad.side else "+", end=req.price, flush=True)
|
|
744
|
-
await sleep(60)
|
|
745
|
-
|
|
746
|
-
def overprice_filter(self, ads: list[Ad], ceil: float, k: Literal[-1, 1]):
|
|
747
|
-
# вырезаем ads с ценами выше потолка
|
|
748
|
-
if ads and (ceil - float(ads[0].price)) * k > 0:
|
|
749
|
-
if int(ads[0].userId) != self.actor.exid:
|
|
750
|
-
ads.pop(0)
|
|
751
|
-
self.overprice_filter(ads, ceil, k)
|
|
752
|
-
|
|
753
|
-
def get_cad(self, ads: list[Ad], ceil: float, k: Literal[-1, 1], target_place: int, cur_plc: int) -> Ad:
|
|
754
|
-
if not ads:
|
|
755
|
-
return None
|
|
756
|
-
# чью цену будем обгонять, предыдущей или слещующей объявы?
|
|
757
|
-
# cad: Ad = ads[place] if cur_plc > place else ads[cur_plc]
|
|
758
|
-
# переделал пока на жесткую установку целевого места, даже если текущее выше:
|
|
759
|
-
if len(ads) <= target_place:
|
|
760
|
-
logging.error(f"target place {target_place} not found in ads {len(ads)}-lenght list")
|
|
761
|
-
target_place = len(ads) - 1
|
|
762
|
-
cad: Ad = ads[target_place]
|
|
763
|
-
# а цена обгоняемой объявы не выше нашего потолка?
|
|
764
|
-
if (float(cad.price) - ceil) * k <= 0:
|
|
765
|
-
# тогда берем следующую
|
|
766
|
-
ads.pop(target_place)
|
|
767
|
-
cad = self.get_cad(ads, ceil, k, target_place, cur_plc)
|
|
768
|
-
# todo: добавить фильтр по лимитам min-max
|
|
769
|
-
return cad
|
|
770
|
-
|
|
771
758
|
# @staticmethod
|
|
772
759
|
# def premium_up(mad: Ad, cad: Ad, k: Literal[-1, 1]):
|
|
773
760
|
# mpc, mpm, cpc, cpm = Decimal(mad.price), Decimal(mad.premium), Decimal(cad.price), Decimal(cad.premium)
|
|
@@ -777,235 +764,6 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
777
764
|
# if round(cpc * new_premium / cpm, 2) == m
|
|
778
765
|
# mad.premium = new_premium.to_eng_string()
|
|
779
766
|
|
|
780
|
-
async def start_race(self):
|
|
781
|
-
races = await models.Race.filter(started=True, road__ad__maker_id=self.actor.id).prefetch_related(
|
|
782
|
-
"road__ad__pair_side__pair__cur", "road__credexs__cred", "road__ad__pms__pmexs__pm"
|
|
783
|
-
)
|
|
784
|
-
tasks = [create_task(self.racing(race), name=f"Rc{race.id}") for race in races]
|
|
785
|
-
return await gather(*tasks)
|
|
786
|
-
|
|
787
|
-
async def racing(self, race: models.Race):
|
|
788
|
-
coinex: models.CoinEx = await models.CoinEx.get(
|
|
789
|
-
coin_id=race.road.ad.pair_side.pair.coin_id, ex=self.actor.ex
|
|
790
|
-
).prefetch_related("coin")
|
|
791
|
-
curex: models.CurEx = await models.CurEx.get(
|
|
792
|
-
cur_id=race.road.ad.pair_side.pair.cur_id, ex=self.actor.ex
|
|
793
|
-
).prefetch_related("cur")
|
|
794
|
-
taker_side: bool = not race.road.ad.pair_side.is_sell
|
|
795
|
-
creds = [c.cred for c in race.road.credexs]
|
|
796
|
-
pmexs: list[models.PmEx] = [pmex for pm in race.road.ad.pms for pmex in pm.pmexs if pmex.ex_id == 4]
|
|
797
|
-
post_pm_ids = {c.cred.ovr_pm_id for c in race.road.credexs if c.cred.ovr_pm_id}
|
|
798
|
-
post_pmexs = set(await models.PmEx.filter(pm_id__in=post_pm_ids, ex=self.actor.ex).prefetch_related("pm"))
|
|
799
|
-
|
|
800
|
-
k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
|
|
801
|
-
sleep_sec = 3 # 1 if set(pms) & {"volet"} and coinex.coin_id == 1 else 5
|
|
802
|
-
_lstat, volume = None, 0
|
|
803
|
-
|
|
804
|
-
while self.actor.person.user.status > 0:
|
|
805
|
-
# обновляем все обновления по текущей гонке из бд
|
|
806
|
-
await race.refresh_from_db()
|
|
807
|
-
if not race.started:
|
|
808
|
-
await sleep(5)
|
|
809
|
-
continue
|
|
810
|
-
# если гонка дольше Х минут не обновлялась, обновляем ее (и ее пары) потолок
|
|
811
|
-
expiration = datetime.now(timezone.utc) - timedelta(minutes=15)
|
|
812
|
-
amt = race.filter_amount * 10**-curex.cur.scale if race.filter_amount else None
|
|
813
|
-
if race.updated_at < expiration:
|
|
814
|
-
ceils, hp, vmf, zplace = await self.get_ceils(coinex, curex, pmexs, 0.003, False, 0, amt, post_pmexs)
|
|
815
|
-
race.ceil = int(ceils[int(taker_side)] * 10**curex.scale)
|
|
816
|
-
await race.save()
|
|
817
|
-
# upd pair race
|
|
818
|
-
if prace := await models.Race.annotate(pms_count=Count("road__ad__pms")).get_or_none(
|
|
819
|
-
road__ad__pair_side__pair_id=race.road.ad.pair_side.pair_id,
|
|
820
|
-
road__ad__pair_side__is_sell=taker_side,
|
|
821
|
-
road__ad__maker=self.actor,
|
|
822
|
-
updated_at__lt=expiration,
|
|
823
|
-
road__credexs__id__in=[c.id for c in race.road.credexs],
|
|
824
|
-
pms_count=len(pmexs),
|
|
825
|
-
):
|
|
826
|
-
prace.ceil = int(ceils[int(not taker_side)] * 10**curex.scale)
|
|
827
|
-
await prace.save()
|
|
828
|
-
|
|
829
|
-
last_vol = volume
|
|
830
|
-
if taker_side: # гонка в стакане продажи - мы покупаем монету за ФИАТ
|
|
831
|
-
fiat = max(await models.Fiat.filter(cred_id__in=[c.id for c in creds]), key=lambda x: x.amount)
|
|
832
|
-
volume = (fiat.amount * 10**-curex.cur.scale) / (race.road.ad.price * 10**-curex.scale)
|
|
833
|
-
else: # гонка в стакане покупки - мы продаем МОНЕТУ за фиат
|
|
834
|
-
asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
|
|
835
|
-
volume = asset.free * 10**-coinex.scale
|
|
836
|
-
volume = str(round(volume, coinex.scale))
|
|
837
|
-
try:
|
|
838
|
-
ads: list[Ad] = await self.ads(coinex, curex, taker_side, pmexs, amt, 50, race.vm_filter, post_pmexs)
|
|
839
|
-
except Exception:
|
|
840
|
-
await sleep(1)
|
|
841
|
-
ads: list[Ad] = await self.ads(coinex, curex, taker_side, pmexs, amt, 50, race.vm_filter, post_pmexs)
|
|
842
|
-
|
|
843
|
-
self.overprice_filter(ads, race.ceil * 10**-curex.scale, k) # обрезаем сверху все ads дороже нашего потолка
|
|
844
|
-
|
|
845
|
-
if not ads:
|
|
846
|
-
print(coinex.exid, curex.exid, taker_side, "no ads!")
|
|
847
|
-
await sleep(15)
|
|
848
|
-
continue
|
|
849
|
-
# определяем наше текущее место в уже обрезанном списке ads
|
|
850
|
-
if not (cur_plc := [i for i, ad in enumerate(ads) if int(ad.userId) == self.actor.exid]):
|
|
851
|
-
logging.warning(f"No racing in {pmexs[0].name} {'-' if taker_side else '+'}{coinex.exid}/{curex.exid}")
|
|
852
|
-
await sleep(15)
|
|
853
|
-
continue
|
|
854
|
-
(cur_plc,) = cur_plc # может упасть если в списке > 1 наш ad
|
|
855
|
-
[(await self.ex_client.cond_load(ad, race.road.ad.pair_side, True))[0] for ad in ads[:cur_plc]]
|
|
856
|
-
# rivals = [
|
|
857
|
-
# (await models.RaceStat.update_or_create({"place": plc, "price": ad.price, "premium": ad.premium}, ad=ad))[
|
|
858
|
-
# 0
|
|
859
|
-
# ]
|
|
860
|
-
# for plc, ad in enumerate(rads)
|
|
861
|
-
# ]
|
|
862
|
-
mad: Ad = ads.pop(cur_plc)
|
|
863
|
-
# if (
|
|
864
|
-
# not (lstat := lstat or await race.stats.order_by("-created_at").first())
|
|
865
|
-
# or lstat.place != cur_plc
|
|
866
|
-
# or lstat.price != float(mad.price)
|
|
867
|
-
# or set(rivals) != set(await lstat.rivals)
|
|
868
|
-
# ):
|
|
869
|
-
# lstat = await models.RaceStat.create(race=race, place=cur_plc, price=mad.price, premium=mad.premium)
|
|
870
|
-
# await lstat.rivals.add(*rivals)
|
|
871
|
-
if not ads:
|
|
872
|
-
await sleep(60)
|
|
873
|
-
continue
|
|
874
|
-
if not (cad := self.get_cad(ads, race.ceil * 10**-curex.scale, k, race.target_place, cur_plc)):
|
|
875
|
-
continue
|
|
876
|
-
new_price = round(float(cad.price) - k * step(mad, cad, curex.scale), curex.scale)
|
|
877
|
-
if (
|
|
878
|
-
float(mad.price) == new_price and volume == last_vol
|
|
879
|
-
): # Если место уже нужное или нужная цена и так уже стоит
|
|
880
|
-
print(
|
|
881
|
-
f"{'v' if taker_side else '^'}{mad.price}",
|
|
882
|
-
end=f"[{race.ceil * 10**-curex.scale}+{cur_plc}] ",
|
|
883
|
-
flush=True,
|
|
884
|
-
)
|
|
885
|
-
await sleep(sleep_sec)
|
|
886
|
-
continue
|
|
887
|
-
if cad.priceType: # Если цена конкурента плавающая, то повышаем себе не цену, а %
|
|
888
|
-
new_premium = (float(mad.premium) or float(cad.premium)) - k * step(mad, cad, 2)
|
|
889
|
-
# if float(mad.premium) == new_premium: # Если нужный % и так уже стоит
|
|
890
|
-
# if mad.priceType and cur_plc != race.target_place:
|
|
891
|
-
# new_premium -= k * step(mad, cad, 2)
|
|
892
|
-
# elif volume == last_vol:
|
|
893
|
-
# print(end="v" if taker_side else "^", flush=True)
|
|
894
|
-
# await sleep(sleep_sec)
|
|
895
|
-
# continue
|
|
896
|
-
mad.premium = str(round(new_premium, 2))
|
|
897
|
-
mad.priceType = cad.priceType
|
|
898
|
-
mad.quantity = volume
|
|
899
|
-
mad.maxAmount = str(2_000_000 if curex.cur_id == 1 else 40_000)
|
|
900
|
-
req = AdUpdateRequest.model_validate(
|
|
901
|
-
{
|
|
902
|
-
**mad.model_dump(),
|
|
903
|
-
"price": str(round(new_price, curex.scale)),
|
|
904
|
-
"paymentIds": [str(cx.exid) for cx in race.road.credexs],
|
|
905
|
-
}
|
|
906
|
-
)
|
|
907
|
-
try:
|
|
908
|
-
print(
|
|
909
|
-
f"c{race.ceil * 10**-curex.scale}+{cur_plc} {coinex.coin.ticker}{'-' if taker_side else '+'}{req.price}{curex.cur.ticker}"
|
|
910
|
-
f"{[pm.norm for pm in race.road.ad.pms]}{f'({req.premium}%)' if req.premium != '0' else ''} "
|
|
911
|
-
f"t{race.target_place} ;",
|
|
912
|
-
flush=True,
|
|
913
|
-
)
|
|
914
|
-
_res = self.ad_upd(req)
|
|
915
|
-
except FailedRequestError as e:
|
|
916
|
-
if ExcCode(e.status_code) == ExcCode.FixPriceLimit:
|
|
917
|
-
if limits := re.search(
|
|
918
|
-
r"The fixed price set is lower than ([0-9]+\.?[0-9]{0,2}) or higher than ([0-9]+\.?[0-9]{0,2})",
|
|
919
|
-
e.message,
|
|
920
|
-
):
|
|
921
|
-
req.price = limits.group(1 if taker_side else 2)
|
|
922
|
-
if req.price != mad.price:
|
|
923
|
-
_res = self.ad_upd(req)
|
|
924
|
-
else:
|
|
925
|
-
raise e
|
|
926
|
-
elif ExcCode(e.status_code) == ExcCode.InsufficientBalance:
|
|
927
|
-
asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
|
|
928
|
-
req.quantity = str(round(asset.free * 10**-coinex.scale, coinex.scale))
|
|
929
|
-
_res = self.ad_upd(req)
|
|
930
|
-
elif ExcCode(e.status_code) == ExcCode.RareLimit:
|
|
931
|
-
if not (
|
|
932
|
-
sads := [
|
|
933
|
-
ma
|
|
934
|
-
for ma in self.my_ads(False)
|
|
935
|
-
if (
|
|
936
|
-
ma.currencyId == curex.exid
|
|
937
|
-
and ma.tokenId == coinex.exid
|
|
938
|
-
and taker_side != ma.side
|
|
939
|
-
and set(ma.payments) == set([pe.exid for pe in pmexs])
|
|
940
|
-
)
|
|
941
|
-
]
|
|
942
|
-
):
|
|
943
|
-
logging.error(f"Need reserve Ad {'sell' if taker_side else 'buy'} {coinex.exid}/{curex.exid}")
|
|
944
|
-
await sleep(90)
|
|
945
|
-
continue
|
|
946
|
-
self.ad_del(ad_id=int(mad.id))
|
|
947
|
-
req.id = sads[0].id
|
|
948
|
-
req.actionType = "ACTIVE"
|
|
949
|
-
self.api.update_ad(**req.model_dump())
|
|
950
|
-
logging.warning(f"Ad#{mad.id} recreated")
|
|
951
|
-
elif ExcCode(e.status_code) == ExcCode.Timestamp:
|
|
952
|
-
await sleep(3)
|
|
953
|
-
else:
|
|
954
|
-
raise e
|
|
955
|
-
except (ReadTimeoutError, ConnectionDoesNotExistError):
|
|
956
|
-
logging.warning("Connection failed. Restarting..")
|
|
957
|
-
await sleep(6)
|
|
958
|
-
|
|
959
|
-
async def get_books(
|
|
960
|
-
self,
|
|
961
|
-
coinex: models.CoinEx,
|
|
962
|
-
curex: models.CurEx,
|
|
963
|
-
pmexs: list[models.PmEx],
|
|
964
|
-
amount: int,
|
|
965
|
-
post_pmexs: list[models.PmEx] = None,
|
|
966
|
-
) -> tuple[list[Ad], list[Ad]]:
|
|
967
|
-
buy: list[Ad] = await self.ads(coinex, curex, False, pmexs, amount, 40, False, post_pmexs)
|
|
968
|
-
sell: list[Ad] = await self.ads(coinex, curex, True, pmexs, amount, 30, False, post_pmexs)
|
|
969
|
-
return buy, sell
|
|
970
|
-
|
|
971
|
-
async def get_spread(
|
|
972
|
-
self, bb: list[Ad], sb: list[Ad], perc: float, vmf: bool = None, place: int = 0, exact: bool = False
|
|
973
|
-
) -> tuple[tuple[float, float], float, bool, int]:
|
|
974
|
-
if len(bb) <= place or len(sb) <= place:
|
|
975
|
-
...
|
|
976
|
-
buy_price, sell_price = float(bb[place].price), float(sb[place].price)
|
|
977
|
-
half_spread = (buy_price - sell_price) / (buy_price + sell_price)
|
|
978
|
-
# if half_spread * 2 < perc: # todo: aA???
|
|
979
|
-
# if not exact:
|
|
980
|
-
# if vmf is None: # сначала фильтруем только VA
|
|
981
|
-
# return await self.get_spread(bb, sb, perc, True, place)
|
|
982
|
-
# # если даже по VA не хватает спреда - увеличиваем место
|
|
983
|
-
# return await self.get_spread(bb, sb, perc, vmf, place + 1)
|
|
984
|
-
|
|
985
|
-
return (buy_price, sell_price), half_spread, vmf, place
|
|
986
|
-
|
|
987
|
-
async def get_ceils(
|
|
988
|
-
self,
|
|
989
|
-
coinex: models.CoinEx,
|
|
990
|
-
curex: models.CurEx,
|
|
991
|
-
pmexs: list[models.PmEx],
|
|
992
|
-
min_prof=0.02,
|
|
993
|
-
vmf: bool = False,
|
|
994
|
-
place: int = 0,
|
|
995
|
-
amount: int = None,
|
|
996
|
-
post_pmexs: set[models.PmEx] = None,
|
|
997
|
-
) -> tuple[tuple[float, float], float, bool, int]: # todo: refact to Pairex
|
|
998
|
-
bb, sb = await self.get_books(coinex, curex, pmexs, amount, post_pmexs)
|
|
999
|
-
if vmf:
|
|
1000
|
-
# ориентируемся на цены объявлений только проверенных мерчантов
|
|
1001
|
-
bb = [b for b in bb if "VA" in b.authTag]
|
|
1002
|
-
sb = [s for s in sb if "VA" in s.authTag]
|
|
1003
|
-
perc = list(post_pmexs or pmexs)[0].pm.fee * 0.0001 + min_prof
|
|
1004
|
-
(bf, sf), hp, vmf, zplace = await self.get_spread(bb, sb, perc, vmf, place)
|
|
1005
|
-
mdl = (bf + sf) / 2
|
|
1006
|
-
bc, sc = mdl + mdl * (perc / 2), mdl - mdl * (perc / 2)
|
|
1007
|
-
return (bc, sc), hp, vmf, zplace
|
|
1008
|
-
|
|
1009
767
|
async def take_ad(self, req: TakeAdReq):
|
|
1010
768
|
if req.price and req.is_sell and req.cur_:
|
|
1011
769
|
... # todo call the get_ad_details() only if lack of data
|
|
@@ -1046,83 +804,90 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
1046
804
|
return resp
|
|
1047
805
|
|
|
1048
806
|
async def watch_payeer(self, mcs: dict[int, "AgentClient"]):
|
|
1049
|
-
|
|
1050
|
-
|
|
807
|
+
await models.CoinEx.get(coin_id=1, ex=self.actor.ex).prefetch_related("coin")
|
|
808
|
+
await models.CurEx.get(cur_id=1, ex=self.actor.ex).prefetch_related("cur")
|
|
1051
809
|
post_pmexs = set(await models.PmEx.filter(pm_id=366, ex=self.actor.ex).prefetch_related("pm"))
|
|
1052
810
|
i = 0
|
|
1053
811
|
while True:
|
|
1054
812
|
try:
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
if
|
|
1058
|
-
|
|
813
|
+
breq = GetAds(coin_id=1, cur_id=1, is_sell=False, limit=50)
|
|
814
|
+
bs = await self.ex_client.ads(breq, post_pmexs=post_pmexs)
|
|
815
|
+
bs = [b for b in bs if float(b.price) < 100 or int(b.userId) in mcs.keys()]
|
|
816
|
+
if bs:
|
|
817
|
+
ad: Ad = bs[0]
|
|
1059
818
|
await self.bbot.send(
|
|
1060
819
|
193017646,
|
|
1061
820
|
f"price: {ad.price}\nnick: {ad.nickName}\nprice: {ad.price}"
|
|
1062
821
|
f"\nqty: {ad.quantity} [{ad.minAmount}-{ad.maxAmount}]",
|
|
1063
822
|
)
|
|
1064
|
-
am = min(float(ad.maxAmount), max(
|
|
823
|
+
am = min(float(ad.maxAmount), max(8000 + i, float(ad.minAmount)))
|
|
1065
824
|
req = TakeAdReq(
|
|
1066
825
|
ad_id=ad.id,
|
|
1067
826
|
amount=am,
|
|
1068
827
|
pm_id=14,
|
|
1069
|
-
is_sell=
|
|
828
|
+
is_sell=False,
|
|
1070
829
|
coin_id=1,
|
|
1071
830
|
cur_id=1,
|
|
1072
831
|
)
|
|
1073
832
|
ord_resp: OrderResp = await self.take_ad(req)
|
|
1074
833
|
# order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
|
|
1075
834
|
order: OrderFull = await self.get_order_info(ord_resp.orderId)
|
|
1076
|
-
odb = await self.
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
# res = await self.pm_clients[366].
|
|
835
|
+
odb = await self.create_order_db(order)
|
|
836
|
+
t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
|
|
837
|
+
await t.fetch_related("order__cred__pmcur__cur")
|
|
838
|
+
# res = await self.pm_clients[366].send(t)
|
|
839
|
+
await sleep(2)
|
|
840
|
+
self.api.mark_as_paid(
|
|
841
|
+
orderId=str(odb.exid),
|
|
842
|
+
paymentType=str(order.paymentTermList[0].paymentType), # pmex.exid
|
|
843
|
+
paymentId=order.paymentTermList[0].id, # credex.exid
|
|
844
|
+
)
|
|
845
|
+
await sleep(3)
|
|
1080
846
|
if int(ad.userId) in mcs:
|
|
1081
|
-
mcs[int(ad.userId)].api.
|
|
1082
|
-
orderId=str(odb.exid),
|
|
1083
|
-
paymentType=ad.payments[0], # pmex.exid
|
|
1084
|
-
paymentId=order.paymentTermList[0].id, # credex.exid
|
|
1085
|
-
)
|
|
1086
|
-
self.api.release_assets(orderId=order.id)
|
|
1087
|
-
...
|
|
847
|
+
mcs[int(ad.userId)].api.release_assets(orderId=order.id)
|
|
1088
848
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
849
|
+
await sleep(5)
|
|
850
|
+
|
|
851
|
+
sreq = GetAds(coin_id=1, cur_id=1, is_sell=True, limit=50, kwargs={"post_pmexs": post_pmexs})
|
|
852
|
+
ss = await self.ex_client.ads(sreq, post_pmexs=post_pmexs)
|
|
853
|
+
ss = [s for s in ss if float(s.price) > 92 or int(s.userId) in mcs.keys()]
|
|
854
|
+
if ss:
|
|
855
|
+
ad: Ad = ss[0]
|
|
1093
856
|
await self.bbot.send(
|
|
1094
857
|
193017646,
|
|
1095
858
|
f"price: {ad.price}\nnick: {ad.nickName}\nprice: {ad.price}"
|
|
1096
859
|
f"\nqty: {ad.quantity} [{ad.minAmount}-{ad.maxAmount}]",
|
|
1097
860
|
)
|
|
1098
|
-
am = min(float(ad.maxAmount), max(
|
|
861
|
+
am = min(float(ad.maxAmount), max(10000 + i, float(ad.minAmount)))
|
|
1099
862
|
req = TakeAdReq(
|
|
1100
863
|
ad_id=ad.id,
|
|
1101
864
|
amount=am,
|
|
1102
865
|
pm_id=14,
|
|
1103
|
-
is_sell=
|
|
866
|
+
is_sell=True,
|
|
1104
867
|
coin_id=1,
|
|
1105
868
|
cur_id=1,
|
|
1106
869
|
)
|
|
1107
870
|
ord_resp: OrderResp = await self.take_ad(req)
|
|
1108
871
|
# order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
|
|
1109
872
|
order: OrderFull = await self.get_order_info(ord_resp.orderId)
|
|
1110
|
-
odb = await self.
|
|
1111
|
-
t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
|
|
1112
|
-
await t.fetch_related("order__cred__pmcur__cur")
|
|
1113
|
-
# res = await self.pm_clients[366].
|
|
1114
|
-
|
|
1115
|
-
orderId=str(odb.exid),
|
|
1116
|
-
paymentType=ad.payments[0], # pmex.exid
|
|
1117
|
-
paymentId=order.paymentTermList[0].id, # credex.exid
|
|
1118
|
-
)
|
|
873
|
+
odb = await self.create_order_db(order)
|
|
874
|
+
# t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
|
|
875
|
+
# await t.fetch_related("order__cred__pmcur__cur")
|
|
876
|
+
# res = await self.pm_clients[366].check_in(t)
|
|
877
|
+
await sleep(2)
|
|
1119
878
|
if int(ad.userId) in mcs:
|
|
1120
|
-
mcs[int(ad.userId)].api.
|
|
1121
|
-
|
|
1122
|
-
|
|
879
|
+
mcs[int(ad.userId)].api.mark_as_paid(
|
|
880
|
+
orderId=str(odb.exid),
|
|
881
|
+
paymentType=str(order.paymentTermList[0].paymentType), # pmex.exid
|
|
882
|
+
paymentId=order.paymentTermList[0].id, # credex.exid
|
|
883
|
+
)
|
|
884
|
+
await sleep(3)
|
|
885
|
+
self.api.release_assets(orderId=order.id)
|
|
886
|
+
await sleep(5)
|
|
887
|
+
|
|
1123
888
|
except Exception as e:
|
|
1124
889
|
logging.exception(e)
|
|
1125
|
-
await sleep(
|
|
890
|
+
await sleep(30)
|
|
1126
891
|
except HttpProcessingError as e:
|
|
1127
892
|
logging.error(e)
|
|
1128
893
|
print(end=".", flush=True)
|
|
@@ -1136,12 +901,389 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
1136
901
|
req = TakeAdReq(ad_id="1856989782009487360", amount=am, pm_id=366)
|
|
1137
902
|
ord_resp: OrderResp = await self.take_ad(req)
|
|
1138
903
|
order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
|
|
1139
|
-
odb = await self.
|
|
904
|
+
odb = await self.create_order_db(order)
|
|
1140
905
|
t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
|
|
1141
906
|
await t.fetch_related("order__cred__pmcur__cur")
|
|
1142
907
|
await self.pm_clients[366].send(t)
|
|
1143
908
|
...
|
|
1144
909
|
|
|
910
|
+
pm_clients: dict[int, PmAgentClient]
|
|
911
|
+
|
|
912
|
+
async def start_listen(self):
|
|
913
|
+
t = await self.ott()
|
|
914
|
+
ts = int(float(t["time_now"]) * 1000)
|
|
915
|
+
await self.ws_prv(self.agent.auth["cookies"]["deviceId"], t["result"], ts)
|
|
916
|
+
|
|
917
|
+
# 3N: [T] - Уведомление об одобрении запроса на сделку
|
|
918
|
+
async def request_accepted_notify(self) -> int: ... # id
|
|
919
|
+
|
|
920
|
+
async def ws_prv(self, did: str, tok: str, ts: int):
|
|
921
|
+
u = f"wss://ws2.bybit.com/private?appid=bybit&os=web&deviceid={did}×tamp={ts}"
|
|
922
|
+
async with websockets.connect(u) as websocket:
|
|
923
|
+
auth_msg = json.dumps({"req_id": did, "op": "login", "args": [tok]})
|
|
924
|
+
await websocket.send(auth_msg)
|
|
925
|
+
|
|
926
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
|
|
927
|
+
await websocket.send(sub_msg)
|
|
928
|
+
sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"SUPER_DEAL"}']})
|
|
929
|
+
await websocket.send(sub_msg)
|
|
930
|
+
sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"OTC_ORDER_STATUS"}']})
|
|
931
|
+
await websocket.send(sub_msg)
|
|
932
|
+
sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"WEB_THREE_SELL"}']})
|
|
933
|
+
await websocket.send(sub_msg)
|
|
934
|
+
sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"APPEALED_CHANGE"}']})
|
|
935
|
+
await websocket.send(sub_msg)
|
|
936
|
+
|
|
937
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order"]})
|
|
938
|
+
await websocket.send(sub_msg)
|
|
939
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order-eftd-complete-privilege-event"]})
|
|
940
|
+
await websocket.send(sub_msg)
|
|
941
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order-savings-product-event"]})
|
|
942
|
+
await websocket.send(sub_msg)
|
|
943
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.deal-core.order-savings-complete-event"]})
|
|
944
|
+
await websocket.send(sub_msg)
|
|
945
|
+
|
|
946
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
|
|
947
|
+
await websocket.send(sub_msg)
|
|
948
|
+
while resp := await websocket.recv():
|
|
949
|
+
if data := json.loads(resp):
|
|
950
|
+
upd, order_db = None, None
|
|
951
|
+
logging.info(f" {now().strftime('%H:%M:%S')} upd: {data.get('topic')}:{data.get('type')}")
|
|
952
|
+
match data.get("topic"):
|
|
953
|
+
case "OTC_ORDER_STATUS":
|
|
954
|
+
match data["type"]:
|
|
955
|
+
case "STATUS_CHANGE":
|
|
956
|
+
try:
|
|
957
|
+
upd = StatusChange.model_validate(data["data"])
|
|
958
|
+
except ValidationError as e:
|
|
959
|
+
logging.error(e)
|
|
960
|
+
logging.error(data["data"])
|
|
961
|
+
order = self.api.get_order_details(orderId=upd.id)
|
|
962
|
+
order = OrderFull.model_validate(order["result"])
|
|
963
|
+
order_db = await models.Order.get_or_none(
|
|
964
|
+
exid=order.id, ad__exid=order.itemId
|
|
965
|
+
) or await self.create_order_db(order)
|
|
966
|
+
match upd.status:
|
|
967
|
+
case Status.created:
|
|
968
|
+
logging.info(f"Order {order.id} created at {order.createDate}")
|
|
969
|
+
# сразу уменьшаем доступный остаток монеты/валюты
|
|
970
|
+
await self.money_upd(order_db)
|
|
971
|
+
if upd.side: # я покупатель - ждем мою оплату
|
|
972
|
+
_dest = order.paymentTermList[0].accountNo
|
|
973
|
+
if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
|
|
974
|
+
continue
|
|
975
|
+
await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
|
|
976
|
+
await self.send_payment(order_db)
|
|
977
|
+
case Status.wait_for_buyer:
|
|
978
|
+
if upd.side == 0: # ждем когда покупатель оплатит
|
|
979
|
+
if not (pmacdx := await self.get_pma_by_cdex(order)):
|
|
980
|
+
continue
|
|
981
|
+
pma, cdx = pmacdx
|
|
982
|
+
am, tid = await pma.check_in(
|
|
983
|
+
float(order.amount),
|
|
984
|
+
cdx.cred.pmcur.cur.ticker,
|
|
985
|
+
# todo: почему в московском час.поясе?
|
|
986
|
+
datetime.fromtimestamp(float(order.transferDate) / 1000),
|
|
987
|
+
)
|
|
988
|
+
if not tid:
|
|
989
|
+
logging.info(
|
|
990
|
+
f"Order {order.id} created at {order.createDate}, not paid yet"
|
|
991
|
+
)
|
|
992
|
+
continue
|
|
993
|
+
try:
|
|
994
|
+
t, is_new = await models.Transfer.update_or_create(
|
|
995
|
+
dict(
|
|
996
|
+
amount=int(float(order.amount) * 100),
|
|
997
|
+
order=order_db,
|
|
998
|
+
),
|
|
999
|
+
pmid=tid,
|
|
1000
|
+
)
|
|
1001
|
+
except IntegrityError as e:
|
|
1002
|
+
logging.error(tid)
|
|
1003
|
+
logging.error(order)
|
|
1004
|
+
logging.exception(e)
|
|
1005
|
+
|
|
1006
|
+
if not is_new: # если по этому платежу уже отпущен другая продажа
|
|
1007
|
+
continue
|
|
1008
|
+
|
|
1009
|
+
# если висят незавершенные продажи с такой же суммой
|
|
1010
|
+
pos = (await self.get_pending_orders(1))["result"]
|
|
1011
|
+
pos = [
|
|
1012
|
+
o
|
|
1013
|
+
for o in pos.get("items", [])
|
|
1014
|
+
if (
|
|
1015
|
+
o["amount"] == order.amount
|
|
1016
|
+
and o["id"] != upd.id
|
|
1017
|
+
and int(order.createDate)
|
|
1018
|
+
< int(o["createDate"]) + 15 * 60 * 1000
|
|
1019
|
+
# get full_order from o, and cred or pm from full_order:
|
|
1020
|
+
and self.api.get_order_details(orderId=o["id"])["result"][
|
|
1021
|
+
"paymentTermList"
|
|
1022
|
+
][0]["accountNo"]
|
|
1023
|
+
== order.paymentTermList[0].accountNo
|
|
1024
|
+
)
|
|
1025
|
+
]
|
|
1026
|
+
curex = await models.CurEx.get(
|
|
1027
|
+
cur__ticker=order.currencyId, ex=self.ex_client.ex
|
|
1028
|
+
)
|
|
1029
|
+
pos_db = await models.Order.filter(
|
|
1030
|
+
exid__not=order.id,
|
|
1031
|
+
cred_id=order_db.cred_id,
|
|
1032
|
+
amount=int(float(order.amount) * 10**curex.scale),
|
|
1033
|
+
status__not_in=[OrderStatus.completed, OrderStatus.canceled],
|
|
1034
|
+
created_at__gt=now() - timedelta(minutes=15),
|
|
1035
|
+
)
|
|
1036
|
+
if pos or pos_db:
|
|
1037
|
+
await self.ex_client.bot.send(
|
|
1038
|
+
f"[Duplicate amount!]"
|
|
1039
|
+
f"(https://www.bybit.com/ru-RU/p2p/orderList/{order.id})",
|
|
1040
|
+
self.actor.person.user.username_id,
|
|
1041
|
+
)
|
|
1042
|
+
logging.warning("Duplicate amount!")
|
|
1043
|
+
continue
|
|
1044
|
+
|
|
1045
|
+
# !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
|
|
1046
|
+
self.api.release_assets(orderId=upd.id)
|
|
1047
|
+
logging.info(
|
|
1048
|
+
f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
|
|
1049
|
+
)
|
|
1050
|
+
elif upd.side == 1: # я покупатель - ждем мою оплату
|
|
1051
|
+
continue # logging.warning(f"Order {order.id} PAID at {now()}: {int_am}")
|
|
1052
|
+
else:
|
|
1053
|
+
...
|
|
1054
|
+
# todo: check is always canceling
|
|
1055
|
+
# await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
|
|
1056
|
+
# logging.info(f"Order {order.id} canceled at {datetime.now()}")
|
|
1057
|
+
|
|
1058
|
+
case Status.wait_for_seller:
|
|
1059
|
+
if order_db.status == OrderStatus.paid:
|
|
1060
|
+
continue
|
|
1061
|
+
await order_db.update_from_dict(
|
|
1062
|
+
{
|
|
1063
|
+
"status": OrderStatus.paid,
|
|
1064
|
+
"payed_at": datetime.fromtimestamp(
|
|
1065
|
+
float(order.transferDate) / 1000
|
|
1066
|
+
),
|
|
1067
|
+
}
|
|
1068
|
+
).save()
|
|
1069
|
+
logging.info(f"Order {order.id} payed at {order_db.payed_at}")
|
|
1070
|
+
|
|
1071
|
+
case Status.appealed:
|
|
1072
|
+
# todo: appealed by WHO? щас наугад стоит by_seller
|
|
1073
|
+
await order_db.update_from_dict(
|
|
1074
|
+
{
|
|
1075
|
+
"status": OrderStatus.appealed_by_seller,
|
|
1076
|
+
"appealed_at": datetime.fromtimestamp(
|
|
1077
|
+
float(order.updateDate) / 1000
|
|
1078
|
+
),
|
|
1079
|
+
}
|
|
1080
|
+
).save()
|
|
1081
|
+
logging.info(f"Order {order.id} appealed at {order_db.appealed_at}")
|
|
1082
|
+
|
|
1083
|
+
case Status.canceled:
|
|
1084
|
+
await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
|
|
1085
|
+
logging.info(f"Order {order.id} canceled at {datetime.now()}")
|
|
1086
|
+
await self.money_upd(order_db)
|
|
1087
|
+
|
|
1088
|
+
case Status.completed:
|
|
1089
|
+
await order_db.update_from_dict(
|
|
1090
|
+
{
|
|
1091
|
+
"status": OrderStatus.completed,
|
|
1092
|
+
"confirmed_at": datetime.fromtimestamp(
|
|
1093
|
+
float(order.updateDate) / 1000
|
|
1094
|
+
),
|
|
1095
|
+
}
|
|
1096
|
+
).save()
|
|
1097
|
+
await self.money_upd(order_db)
|
|
1098
|
+
|
|
1099
|
+
case _:
|
|
1100
|
+
logging.warning(f"Order {order.id} UNKNOWN STATUS {datetime.now()}")
|
|
1101
|
+
case "COUNT_DOWN":
|
|
1102
|
+
upd = CountDown.model_validate(data["data"])
|
|
1103
|
+
case _:
|
|
1104
|
+
self.listen(data)
|
|
1105
|
+
case "OTC_USER_CHAT_MSG":
|
|
1106
|
+
match data["type"]:
|
|
1107
|
+
case "RECEIVE":
|
|
1108
|
+
upd = Receive.model_validate(data["data"])
|
|
1109
|
+
if order_db := await models.Order.get_or_none(
|
|
1110
|
+
exid=upd.orderId, ad__maker__ex=self.actor.ex
|
|
1111
|
+
).prefetch_related("ad__pair_side__pair", "cred__pmcur__cur"):
|
|
1112
|
+
im_taker = order_db.taker_id == self.actor.id
|
|
1113
|
+
im_buyer = order_db.ad.pair_side.is_sell == im_taker
|
|
1114
|
+
if order_db.ad.auto_msg != upd.message and upd.roleType == "user":
|
|
1115
|
+
msg, _ = await models.Msg.update_or_create(
|
|
1116
|
+
{
|
|
1117
|
+
"to_maker": upd.userId == self.actor.exid and im_taker,
|
|
1118
|
+
"sent_at": datetime.fromtimestamp(float(upd.createDate) / 1000),
|
|
1119
|
+
},
|
|
1120
|
+
txt=upd.message,
|
|
1121
|
+
order=order_db,
|
|
1122
|
+
)
|
|
1123
|
+
if not upd.message:
|
|
1124
|
+
...
|
|
1125
|
+
if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
|
|
1126
|
+
if not order_db.cred.detail.startswith(dest := g.group()):
|
|
1127
|
+
order_db.cred.detail = dest
|
|
1128
|
+
await order_db.save()
|
|
1129
|
+
await self.send_payment(order_db)
|
|
1130
|
+
case "READ":
|
|
1131
|
+
upd = Read.model_validate(data["data"])
|
|
1132
|
+
# if upd.status not in (StatusWs.created, StatusWs.canceled, 10, StatusWs.completed):
|
|
1133
|
+
if upd.orderStatus in (
|
|
1134
|
+
Status.wait_for_buyer,
|
|
1135
|
+
): # todo: тут приходит ордер.статус=10, хотя покупатель еще не нажал оплачено
|
|
1136
|
+
order = self.api.get_order_details(orderId=upd.orderId)["result"]
|
|
1137
|
+
order = OrderFull.model_validate(order)
|
|
1138
|
+
|
|
1139
|
+
case "CLEAR":
|
|
1140
|
+
continue
|
|
1141
|
+
case _:
|
|
1142
|
+
self.listen(data)
|
|
1143
|
+
case "OTC_USER_CHAT_MSG_V2":
|
|
1144
|
+
# match data["type"]:
|
|
1145
|
+
# case "RECEIVE":
|
|
1146
|
+
# upd = Receive.model_validate(data["data"])
|
|
1147
|
+
# case "READ":
|
|
1148
|
+
# upd = Read.model_validate(data["data"])
|
|
1149
|
+
# case "CLEAR":
|
|
1150
|
+
# pass
|
|
1151
|
+
# case _:
|
|
1152
|
+
# self.listen(data)
|
|
1153
|
+
continue
|
|
1154
|
+
case "SELLER_CANCEL_CHANGE":
|
|
1155
|
+
upd = SellerCancelChange.model_validate(data["data"])
|
|
1156
|
+
case None:
|
|
1157
|
+
if not data.get("success"):
|
|
1158
|
+
logging.error(data, "NOT SUCCESS!")
|
|
1159
|
+
else:
|
|
1160
|
+
continue # success login, subscribes, input
|
|
1161
|
+
case _:
|
|
1162
|
+
logging.warning(data, "UNKNOWN TOPIC")
|
|
1163
|
+
...
|
|
1164
|
+
if not upd:
|
|
1165
|
+
logging.warning(data, "NOT PROCESSED UPDATE")
|
|
1166
|
+
|
|
1167
|
+
async def money_upd(self, odb: models.Order):
|
|
1168
|
+
# обновляем остаток монеты
|
|
1169
|
+
await odb.fetch_related("ad__pair_side__pair", "ad__my_ad__credexs__cred__fiat", "cred__pmcur", "transfer")
|
|
1170
|
+
ass = await models.Asset.get(addr__coin_id=odb.ad.pair_side.pair.coin_id, addr__actor=self.actor)
|
|
1171
|
+
# обновляем остаток валюты
|
|
1172
|
+
im_maker = odb.ad.maker_id == self.actor.id
|
|
1173
|
+
im_seller = odb.ad.pair_side.is_sell == im_maker
|
|
1174
|
+
if im_maker:
|
|
1175
|
+
if _fiats := [cx.cred.fiat for cx in odb.ad.my_ad.credexs if cx.cred.fiat]:
|
|
1176
|
+
fiat = _fiats[0]
|
|
1177
|
+
await fiat.fetch_related("cred__pmcur__pm")
|
|
1178
|
+
else:
|
|
1179
|
+
raise ValueError(odb, "No Fiat")
|
|
1180
|
+
elif im_seller: # im taker
|
|
1181
|
+
fltr = dict(cred__person_id=self.actor.person_id)
|
|
1182
|
+
fltr |= (
|
|
1183
|
+
{"cred__ovr_pm_id": odb.cred.ovr_pm_id, "cred__pmcur__cur_id": odb.cred.pmcur.cur_id}
|
|
1184
|
+
if odb.cred.ovr_pm_id
|
|
1185
|
+
else {"cred__pmcur_id": odb.cred.pmcur_id}
|
|
1186
|
+
)
|
|
1187
|
+
if not (fiat := await models.Fiat.get_or_none(**fltr).prefetch_related("cred__pmcur__pm")):
|
|
1188
|
+
raise ValueError(odb, "No Fiat")
|
|
1189
|
+
fee = round(odb.amount * (fiat.cred.pmcur.pm.fee or 0) * 0.0001)
|
|
1190
|
+
# k = int(im_seller) * 2 - 1 # im_seller: 1, im_buyer: -1
|
|
1191
|
+
if odb.status == OrderStatus.created:
|
|
1192
|
+
if im_seller:
|
|
1193
|
+
ass.free -= odb.quantity
|
|
1194
|
+
ass.freeze += odb.quantity
|
|
1195
|
+
else: # я покупатель
|
|
1196
|
+
fiat.amount -= odb.amount + fee
|
|
1197
|
+
elif odb.status == OrderStatus.completed:
|
|
1198
|
+
if im_seller:
|
|
1199
|
+
fiat.amount += odb.amount
|
|
1200
|
+
else: # я покупатель
|
|
1201
|
+
ass.free += odb.quantity
|
|
1202
|
+
elif odb.status == OrderStatus.canceled:
|
|
1203
|
+
if im_seller:
|
|
1204
|
+
ass.free += odb.quantity
|
|
1205
|
+
ass.freeze -= odb.quantity
|
|
1206
|
+
else: # я покупатель
|
|
1207
|
+
fiat.amount += odb.amount + fee
|
|
1208
|
+
else:
|
|
1209
|
+
logging.exception(odb.id, f"STATUS: {odb.status.name}")
|
|
1210
|
+
await ass.save(update_fields=["free", "freeze"])
|
|
1211
|
+
await fiat.save(update_fields=["amount"])
|
|
1212
|
+
logging.info(f"Order #{odb.id} {odb.status.name}. Fiat: {fiat.amount}, Asset: {ass.free}")
|
|
1213
|
+
|
|
1214
|
+
async def send_payment(self, order_db: models.Order):
|
|
1215
|
+
if order_db.status != OrderStatus.created:
|
|
1216
|
+
return
|
|
1217
|
+
fmt_am = round(order_db.amount * 10**-2, 2)
|
|
1218
|
+
pma, cur = await self.get_pma_by_pmex(order_db)
|
|
1219
|
+
async with in_transaction():
|
|
1220
|
+
# отмечаем ордер на бирже "оплачен"
|
|
1221
|
+
pmex = await models.PmEx.get(pm_id=order_db.cred.pmcur.pm_id, ex=self.actor.ex)
|
|
1222
|
+
credex = await models.CredEx.get(cred=order_db.cred, ex=self.actor.ex)
|
|
1223
|
+
self.api.mark_as_paid(
|
|
1224
|
+
orderId=str(order_db.exid),
|
|
1225
|
+
paymentType=pmex.exid, # pmex.exid
|
|
1226
|
+
paymentId=str(credex.exid), # credex.exid
|
|
1227
|
+
)
|
|
1228
|
+
# проверяем не отправляли ли мы уже перевод по этому ордеру
|
|
1229
|
+
if t := await models.Transfer.get_or_none(order=order_db, amount=order_db.amount):
|
|
1230
|
+
await pma.bot.send(
|
|
1231
|
+
f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {order_db.cred.detail} #{t.pmid}!",
|
|
1232
|
+
self.actor.person.user.username_id,
|
|
1233
|
+
)
|
|
1234
|
+
raise Exception(
|
|
1235
|
+
f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {order_db.cred.detail} #{t.pmid}!"
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
# ставим в бд статус "оплачен"
|
|
1239
|
+
order_db.status = OrderStatus.paid
|
|
1240
|
+
order_db.payed_at = datetime.now(timezone.utc)
|
|
1241
|
+
await order_db.save()
|
|
1242
|
+
# создаем перевод в бд
|
|
1243
|
+
t = models.Transfer(order=order_db, amount=order_db.amount, updated_at=now())
|
|
1244
|
+
# отправляем деньги
|
|
1245
|
+
tid, img = await pma.send(t)
|
|
1246
|
+
t.pmid = tid
|
|
1247
|
+
await t.save()
|
|
1248
|
+
await self.send_receipt(str(order_db.exid), tid) # отправляем продавцу чек
|
|
1249
|
+
logging.info(f"Order {order_db.exid} PAID at {datetime.now()}: {fmt_am}!")
|
|
1250
|
+
|
|
1251
|
+
async def send_receipt(self, oexid: str, tid: int) -> tuple[PmAgentClient | None, models.CredEx] | None:
|
|
1252
|
+
try:
|
|
1253
|
+
if res := self.api.upload_chat_file(upload_file=f"tmp/{tid}.png").get("result"):
|
|
1254
|
+
await sleep(0.5)
|
|
1255
|
+
self.api.send_chat_message(orderId=oexid, contentType="pic", message=res["url"], msgUuid=uuid4().hex)
|
|
1256
|
+
except Exception as e:
|
|
1257
|
+
logging.error(e)
|
|
1258
|
+
await sleep(0.5)
|
|
1259
|
+
self.api.send_chat_message(orderId=oexid, contentType="str", message=f"#{tid}", msgUuid=uuid4().hex)
|
|
1260
|
+
|
|
1261
|
+
async def get_pma_by_cdex(self, order: OrderFull) -> tuple[PmAgentClient | None, models.CredEx] | None:
|
|
1262
|
+
cdxs = await models.CredEx.filter(
|
|
1263
|
+
ex=self.ex_client.ex,
|
|
1264
|
+
exid__in=[ptl.id for ptl in order.paymentTermList],
|
|
1265
|
+
cred__person=self.actor.person,
|
|
1266
|
+
).prefetch_related("cred__pmcur__cur")
|
|
1267
|
+
pmas = [pma for cdx in cdxs if (pma := self.pm_clients.get(cdx.cred.pmcur.pm_id))]
|
|
1268
|
+
if not len(pmas):
|
|
1269
|
+
# raise ValueError(order.paymentTermList, f"No pm_agents for {order.paymentTermList[0].paymentType}")
|
|
1270
|
+
return None
|
|
1271
|
+
elif len(pmas) > 1:
|
|
1272
|
+
logging.error(order.paymentTermList, f">1 pm_agents for {cdxs[0].cred.pmcur.pm_id}")
|
|
1273
|
+
else:
|
|
1274
|
+
return pmas[0], cdxs[0]
|
|
1275
|
+
|
|
1276
|
+
async def get_pma_by_pmex(self, order_db: models.Order) -> tuple[PmAgentClient, str]:
|
|
1277
|
+
pma = self.pm_clients.get(order_db.cred.pmcur.pm_id)
|
|
1278
|
+
if pma:
|
|
1279
|
+
return pma, order_db.cred.pmcur.cur.ticker
|
|
1280
|
+
logging.error(f"No pm_agents for {order_db.cred.pmcur.pm_id}")
|
|
1281
|
+
|
|
1282
|
+
@staticmethod
|
|
1283
|
+
def listen(data: dict | None):
|
|
1284
|
+
# print(data)
|
|
1285
|
+
...
|
|
1286
|
+
|
|
1145
1287
|
|
|
1146
1288
|
def ms2utc(msk_ts_str: str):
|
|
1147
1289
|
return datetime.fromtimestamp(int(msk_ts_str) / 1000, timezone(timedelta(hours=3), name="MSK"))
|
|
@@ -1164,23 +1306,6 @@ def detailed_diff(str1, str2):
|
|
|
1164
1306
|
return "".join(result)
|
|
1165
1307
|
|
|
1166
1308
|
|
|
1167
|
-
def step_is_need(mad, cad) -> bool:
|
|
1168
|
-
# todo: пока не решен непонятный кейс, почему то конкурент по всем параметрам слабже, но в списке ранжируется выше.
|
|
1169
|
-
# текущая версия: recentExecuteRate округляется до целого, но на бэке байбита его дробная часть больше
|
|
1170
|
-
return (
|
|
1171
|
-
bool(set(cad.authTag) & {"VA2", "BA"})
|
|
1172
|
-
or cad.recentExecuteRate > mad.recentExecuteRate
|
|
1173
|
-
or (
|
|
1174
|
-
cad.recentExecuteRate
|
|
1175
|
-
== mad.recentExecuteRate # and cad.finishNum > mad.finishNum # пока прибавляем для равных
|
|
1176
|
-
)
|
|
1177
|
-
)
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
def step(mad, cad, scale: int = 2) -> float:
|
|
1181
|
-
return float(int(step_is_need(mad, cad)) * 10**-scale).__round__(scale)
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
1309
|
class ExcCode(IntEnum):
|
|
1185
1310
|
FixPriceLimit = 912120022
|
|
1186
1311
|
RareLimit = 912120050
|
|
@@ -1209,7 +1334,7 @@ async def main():
|
|
|
1209
1334
|
cn = await init_db(TORM)
|
|
1210
1335
|
|
|
1211
1336
|
agent = (
|
|
1212
|
-
await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=
|
|
1337
|
+
await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=2)
|
|
1213
1338
|
.prefetch_related(
|
|
1214
1339
|
"actor__ex",
|
|
1215
1340
|
"actor__person__user__gmail",
|
|
@@ -1225,7 +1350,10 @@ async def main():
|
|
|
1225
1350
|
ex = await models.Ex.get(name="Bybit")
|
|
1226
1351
|
ecl: ExClient = ex.client(filebot)
|
|
1227
1352
|
abot = XyncBot(PAY_TOKEN, cn)
|
|
1228
|
-
|
|
1353
|
+
# pmas = await models.PmAgent.filter(active=True, user_id=1).prefetch_related("pm", "user__gmail")
|
|
1354
|
+
# pm_clients = {pma.pm_id: pma.client(abot) for pma in pmas}
|
|
1355
|
+
prx = PRX and "http://" + PRX
|
|
1356
|
+
cl: AgentClient = agent.client(ecl, filebot, abot, proxy=prx)
|
|
1229
1357
|
|
|
1230
1358
|
# req = TakeAdReq(ad_id=1955696985964089344, amount=504, pm_id=128)
|
|
1231
1359
|
# await cl.take_ad(req)
|
|
@@ -1239,8 +1367,12 @@ async def main():
|
|
|
1239
1367
|
# await cl.set_creds()
|
|
1240
1368
|
# await cl.export_my_ads()
|
|
1241
1369
|
|
|
1370
|
+
my_ad = await models.MyAd[5]
|
|
1371
|
+
await cl.ad_share(my_ad.id)
|
|
1372
|
+
await cl.start_listen()
|
|
1373
|
+
|
|
1242
1374
|
ms = await models.Agent.filter(
|
|
1243
|
-
actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, actor__person__user__id__in=[
|
|
1375
|
+
actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, actor__person__user__id__in=[3]
|
|
1244
1376
|
).prefetch_related(
|
|
1245
1377
|
"actor__ex",
|
|
1246
1378
|
"actor__person__user__gmail",
|
|
@@ -1251,7 +1383,7 @@ async def main():
|
|
|
1251
1383
|
mcs = {m.actor.exid: m.client(ecl, filebot, abot) for m in ms}
|
|
1252
1384
|
|
|
1253
1385
|
await gather(
|
|
1254
|
-
create_task(cl.start(
|
|
1386
|
+
# create_task(cl.start()),
|
|
1255
1387
|
create_task(cl.watch_payeer(mcs)),
|
|
1256
1388
|
)
|
|
1257
1389
|
# ensure_future(cl.start(True))
|