xync-client 0.0.155__py3-none-any.whl → 0.0.162__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 +326 -51
- xync_client/Abc/Ex.py +421 -12
- xync_client/Abc/Order.py +7 -14
- xync_client/Abc/xtype.py +35 -3
- xync_client/Bybit/InAgent.py +18 -447
- xync_client/Bybit/agent.py +531 -431
- xync_client/Bybit/etype/__init__.py +0 -0
- xync_client/Bybit/etype/ad.py +47 -34
- xync_client/Bybit/etype/order.py +34 -49
- xync_client/Bybit/ex.py +20 -46
- xync_client/Bybit/order.py +14 -12
- 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/Pms/Volet/{__init__.py → agent.py} +1 -2
- xync_client/loader.py +1 -0
- {xync_client-0.0.155.dist-info → xync_client-0.0.162.dist-info}/METADATA +2 -1
- {xync_client-0.0.155.dist-info → xync_client-0.0.162.dist-info}/RECORD +33 -29
- 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.162.dist-info}/WHEEL +0 -0
- {xync_client-0.0.155.dist-info → xync_client-0.0.162.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,10 +11,11 @@ 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
|
|
@@ -24,12 +26,11 @@ from tortoise.expressions import Q
|
|
|
24
26
|
from tortoise.functions import Count
|
|
25
27
|
from tortoise.signals import post_save
|
|
26
28
|
from tortoise.timezone import now
|
|
27
|
-
from
|
|
29
|
+
from tortoise.transactions import in_transaction
|
|
28
30
|
from x_client import df_hdrs
|
|
29
31
|
from x_model import init_db
|
|
30
32
|
from x_model.func import ArrayAgg
|
|
31
33
|
from xync_bot import XyncBot
|
|
32
|
-
from xync_client.Bybit.InAgent import InAgentClient
|
|
33
34
|
|
|
34
35
|
from xync_client.Bybit.ex import ExClient
|
|
35
36
|
from xync_schema import models
|
|
@@ -38,7 +39,7 @@ from xync_schema.enums import OrderStatus, AgentStatus
|
|
|
38
39
|
from xync_schema.models import Actor, PmCur, Agent
|
|
39
40
|
|
|
40
41
|
from xync_client.Abc.Agent import BaseAgentClient
|
|
41
|
-
from xync_client.Abc.xtype import FlatDict, BaseOrderReq
|
|
42
|
+
from xync_client.Abc.xtype import FlatDict, BaseOrderReq, AdUpd, GetAds
|
|
42
43
|
from xync_client.Bybit.etype.ad import AdPostRequest, AdUpdateRequest, Ad, AdStatus, MyAd
|
|
43
44
|
from xync_client.Bybit.etype.cred import CredEpyd
|
|
44
45
|
from xync_client.Bybit.etype.order import (
|
|
@@ -52,50 +53,41 @@ from xync_client.Bybit.etype.order import (
|
|
|
52
53
|
Status,
|
|
53
54
|
OrderSellRequest,
|
|
54
55
|
TakeAdReq,
|
|
56
|
+
StatusChange,
|
|
57
|
+
CountDown,
|
|
58
|
+
Receive,
|
|
59
|
+
Read,
|
|
60
|
+
SellerCancelChange,
|
|
55
61
|
)
|
|
56
|
-
from xync_client.
|
|
62
|
+
from xync_client.Pms.Payeer.agent import PmAgentClient
|
|
63
|
+
from xync_client.loader import TORM, NET_TOKEN, PAY_TOKEN, PRX
|
|
57
64
|
|
|
58
65
|
|
|
59
66
|
class NoMakerException(Exception):
|
|
60
67
|
pass
|
|
61
68
|
|
|
62
69
|
|
|
63
|
-
class
|
|
70
|
+
class ShareException(Exception):
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class AgentClient(BaseAgentClient): # Bybit client
|
|
64
75
|
headers = df_hdrs | {"accept-language": "ru-RU"}
|
|
65
76
|
sec_hdrs: dict[str, str]
|
|
66
77
|
# rewrite token for public methods
|
|
67
78
|
api: P2P
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"isKyc": "1",
|
|
81
|
-
"hasCompleteRateDay30": "0",
|
|
82
|
-
"completeRateDay30": "",
|
|
83
|
-
"hasOrderFinishNumberDay30": "0",
|
|
84
|
-
"orderFinishNumberDay30": "0",
|
|
85
|
-
"isMobile": "0",
|
|
86
|
-
"isEmail": "0",
|
|
87
|
-
"hasUnPostAd": "0",
|
|
88
|
-
"hasRegisterTime": "0",
|
|
89
|
-
"registerTimeThreshold": "0",
|
|
90
|
-
"hasNationalLimit": "0",
|
|
91
|
-
"nationalLimit": "",
|
|
92
|
-
},
|
|
93
|
-
"actionType": "MODIFY",
|
|
94
|
-
"securityRiskToken": "",
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
def __init__(self, agent: Agent, ex_client: ExClient, fbot: FileClient, bbot: XyncBot, **kwargs):
|
|
98
|
-
super().__init__(agent, ex_client, fbot, bbot, **kwargs)
|
|
79
|
+
orders: dict[int, tuple[models.Order, OrderFull]] = {} # pending
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
agent: Agent,
|
|
84
|
+
ex_client: ExClient,
|
|
85
|
+
fbot: FileClient,
|
|
86
|
+
bbot: XyncBot,
|
|
87
|
+
pm_clients: dict[int, PmAgentClient] = None,
|
|
88
|
+
**kwargs,
|
|
89
|
+
):
|
|
90
|
+
super().__init__(agent, ex_client, fbot, bbot, pm_clients, **kwargs)
|
|
99
91
|
self.sec_hdrs = {
|
|
100
92
|
"accept-language": "ru,en;q=0.9",
|
|
101
93
|
"gdfp": agent.auth["Risktoken"],
|
|
@@ -169,17 +161,23 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
169
161
|
xtr += (" | " if xtr else "") + ecdx.qrcode
|
|
170
162
|
elif ecdx.paymentExt1:
|
|
171
163
|
xtr += (" | " if xtr else "") + ecdx.paymentExt1
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
164
|
+
try:
|
|
165
|
+
cred_db, _ = await models.Cred.update_or_create(
|
|
166
|
+
{
|
|
167
|
+
"name": ecdx.realName,
|
|
168
|
+
"extra": xtr,
|
|
169
|
+
},
|
|
170
|
+
pmcur=pmcur,
|
|
171
|
+
person_id=pers_id or self.actor.person_id,
|
|
172
|
+
detail=ecdx.accountNo or ecdx.payMessage,
|
|
173
|
+
)
|
|
174
|
+
if cred_db.ovr_pm_id is None and (cred_db.detail.startswith("XyncPay") or xtr.startswith("XyncPay")):
|
|
175
|
+
cred_db.ovr_pm_id = 0
|
|
176
|
+
await cred_db.save()
|
|
177
|
+
credex_in = models.CredEx.validate({"exid": ecdx.id, "cred_id": cred_db.id, "ex_id": self.actor.ex.id})
|
|
178
|
+
credex_db, _ = await models.CredEx.update_or_create(**credex_in.df_unq())
|
|
179
|
+
except IntegrityError as e:
|
|
180
|
+
raise e
|
|
183
181
|
return credex_db
|
|
184
182
|
|
|
185
183
|
async def guess_cur(self, ecdx: CredEpyd, curs: list[models.Cur]):
|
|
@@ -245,33 +243,6 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
245
243
|
res = await self._post("/x-api/fiat/otc/maker/work-config/switch", data)
|
|
246
244
|
return res
|
|
247
245
|
|
|
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
246
|
@staticmethod
|
|
276
247
|
def get_rate(list_ads: list) -> float:
|
|
277
248
|
ads = [ad for ad in list_ads if set(ad["payments"]) - {"5", "51"}]
|
|
@@ -292,10 +263,48 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
292
263
|
ad_db = await self.ex_client.ad_load(ad, maker=self.actor)
|
|
293
264
|
mad_db, _ = await models.MyAd.update_or_create(ad=ad_db)
|
|
294
265
|
exids = [pt.id for pt in ad.paymentTerms]
|
|
295
|
-
credexs = await models.CredEx.filter(ex_id=self.actor.ex_id, exid__in=exids)
|
|
266
|
+
credexs = await models.CredEx.filter(ex_id=self.actor.ex_id, exid__in=exids).prefetch_related("cred")
|
|
267
|
+
await mad_db.credexs.clear()
|
|
296
268
|
await mad_db.credexs.add(*credexs)
|
|
297
269
|
return len(ads)
|
|
298
270
|
|
|
271
|
+
async def ads_share(self, cur_id: int = None) -> int:
|
|
272
|
+
mq = models.MyAd.hot_mads_query([4]).filter(ad__maker=self.actor)
|
|
273
|
+
if cur_id:
|
|
274
|
+
mq = mq.filter(ad__pair_side__pair__cur_id=cur_id)
|
|
275
|
+
mads: list[models.MyAd] = await mq.all()
|
|
276
|
+
return len([await self.ad_share(mad.id) for mad in mads])
|
|
277
|
+
|
|
278
|
+
async def ad_share(self, maid: int):
|
|
279
|
+
myad = await models.MyAd.get(id=maid).prefetch_related(
|
|
280
|
+
"ad__pair_side__pair__coin", "ad__pair_side__pair__cur", "ad__maker"
|
|
281
|
+
)
|
|
282
|
+
if myad.hex and myad.shared_at + timedelta(minutes=55) > now(): # check expired
|
|
283
|
+
# check validity
|
|
284
|
+
data = await self._post("/x-api/fiat/otc/item/shareItem/info", {"shareCode": myad.hex.hex()})
|
|
285
|
+
if data["ret_code"] == 0:
|
|
286
|
+
return myad.get_url()
|
|
287
|
+
data = await self._post("/x-api/fiat/otc/item/share", {"itemId": str(myad.ad.exid)})
|
|
288
|
+
if data["ret_code"] == 912300058:
|
|
289
|
+
raise ShareException(
|
|
290
|
+
f"Объява {myad.id}:{myad.ad.id}:{myad.ad.exid} агента {myad.ad.maker.agent_id} выключена"
|
|
291
|
+
)
|
|
292
|
+
if data["ret_code"] == 912300059:
|
|
293
|
+
raise ShareException(f"Торговля агента {myad.ad.maker.agent_id} выключена")
|
|
294
|
+
if data["ret_code"] == 10007:
|
|
295
|
+
raise ShareException(f"Авторизация агента {myad.ad.maker.agent_id} слетела")
|
|
296
|
+
if data["ret_code"] != 0: # Новая ошибка
|
|
297
|
+
raise ShareException(data)
|
|
298
|
+
url = data["result"]["shareLink"]
|
|
299
|
+
resp = await self.session.get(url)
|
|
300
|
+
side = "buy" if myad.ad.pair_side.is_sell else "sell" # inverse for taker
|
|
301
|
+
coin, cur = myad.ad.pair_side.pair.coin.ticker, myad.ad.pair_side.pair.cur.ticker
|
|
302
|
+
pref = models.MyAd.WEB.format(side=side, coin=coin, cur=cur)
|
|
303
|
+
hx = resp.url.query["by_web_link"].replace(pref, "")
|
|
304
|
+
_r = await models.MyAd.filter(id=maid).update(hex=bytes.fromhex(hx), shared_at=now())
|
|
305
|
+
await myad.refresh_from_db()
|
|
306
|
+
return myad.get_url()
|
|
307
|
+
|
|
299
308
|
def get_security_token_create(self):
|
|
300
309
|
data = self._post("/x-api/fiat/otc/item/create", self.create_ad_body)
|
|
301
310
|
if data["ret_code"] == 912120019: # Current user can not to create add as maker
|
|
@@ -342,6 +351,9 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
342
351
|
return self.__get_2fa(typ, rt)
|
|
343
352
|
raise Exception("2fa fail")
|
|
344
353
|
|
|
354
|
+
def get_ad(self, aid: int) -> Ad:
|
|
355
|
+
return Ad(**self.api.get_ad_details(itemId=aid)["result"])
|
|
356
|
+
|
|
345
357
|
def _post_ad(self, risk_token: str):
|
|
346
358
|
self.create_ad_body.update({"securityRiskToken": risk_token})
|
|
347
359
|
data = self._post("/x-api/fiat/otc/item/create", self.create_ad_body)
|
|
@@ -363,7 +375,8 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
363
375
|
data = self.api.post_new_ad(**ad.model_dump())
|
|
364
376
|
return data["result"]["itemId"] if data["ret_code"] == 0 else data
|
|
365
377
|
|
|
366
|
-
def
|
|
378
|
+
async def _ad_upd(self, req: AdUpd):
|
|
379
|
+
upd = AdUpdateRequest({})
|
|
367
380
|
params = upd.model_dump()
|
|
368
381
|
data = self.api.update_ad(**params)
|
|
369
382
|
return data["result"] if data["ret_code"] == 0 else data
|
|
@@ -487,10 +500,10 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
487
500
|
{"makerUserId": self.actor.exid, "page": "1", "size": "10", "appraiseType": "1"}, # "0" - bad
|
|
488
501
|
)
|
|
489
502
|
|
|
490
|
-
async def
|
|
503
|
+
async def get_pending_orders(
|
|
491
504
|
self, side: int = None, status: int = None, begin_time: int = None, end_time: int = None, token_id: str = None
|
|
492
505
|
):
|
|
493
|
-
|
|
506
|
+
res = await self._post(
|
|
494
507
|
"/x-api/fiat/otc/order/pending/simplifyList",
|
|
495
508
|
{
|
|
496
509
|
"status": status,
|
|
@@ -499,9 +512,12 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
499
512
|
"endTime": end_time,
|
|
500
513
|
"side": side, # 1 - продажа, 0 - покупка
|
|
501
514
|
"page": 1,
|
|
502
|
-
"size":
|
|
515
|
+
"size": 20,
|
|
503
516
|
},
|
|
504
517
|
)
|
|
518
|
+
if res["ret_code"] == 0:
|
|
519
|
+
return {int(o["id"]): OrderItem(**o) for o in res["result"]["items"]}
|
|
520
|
+
return res["ret_code"]
|
|
505
521
|
|
|
506
522
|
def get_orders_done(self, begin_time: int, end_time: int, status: int, side: int, token_id: str):
|
|
507
523
|
return self._post(
|
|
@@ -517,27 +533,27 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
517
533
|
},
|
|
518
534
|
)
|
|
519
535
|
|
|
520
|
-
async def
|
|
521
|
-
# ad = Ad(**self.api.get_ad_details(itemId=order.itemId)["result"])
|
|
522
|
-
await sleep(1)
|
|
536
|
+
async def create_order_db(self, order: OrderFull) -> models.Order:
|
|
523
537
|
curex = await models.CurEx.get_or_none(ex=self.ex_client.ex, exid=order.currencyId).prefetch_related("cur")
|
|
524
538
|
cur_scale = (curex.scale if curex.scale is not None else curex.cur.scale) if curex else 2
|
|
525
539
|
coinex = await models.CoinEx.get(ex=self.ex_client.ex, exid=order.tokenId).prefetch_related("coin")
|
|
526
540
|
coin_scale = coinex.scale if coinex.scale is not None else coinex.cur.scale
|
|
527
|
-
|
|
528
|
-
im_maker = int(order.makerUserId ==
|
|
541
|
+
sb_names = order.sellerRealName, order.buyerRealName
|
|
542
|
+
im_maker = int(int(order.makerUserId) == self.actor.exid)
|
|
529
543
|
taker_id = (order.userId, order.targetUserId)[im_maker]
|
|
530
|
-
|
|
544
|
+
taker_name = sb_names[order.side] # todo: double check
|
|
545
|
+
taker_person = await self.ex_client.person_name_update(taker_name, taker_id)
|
|
531
546
|
seller_person = (
|
|
532
547
|
self.actor.person
|
|
533
548
|
if order.side
|
|
534
549
|
else await self.ex_client.person_name_update(order.sellerRealName, int(order.targetUserId))
|
|
535
550
|
)
|
|
536
551
|
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
552
|
ad_db = await models.Ad.get(exid=order.itemId)
|
|
539
553
|
if not ad_db:
|
|
540
|
-
|
|
554
|
+
ad = self.get_ad(order.itemId)
|
|
555
|
+
# ad_db, cond_isnew = await self.ex_client.cond_load(ad, force=True, rname=maker_name[order.side])
|
|
556
|
+
ad_db = await self.ex_client.ad_load(ad, maker=self.actor)
|
|
541
557
|
ecredex: CredEpyd = order.confirmedPayTerm
|
|
542
558
|
|
|
543
559
|
if ecredex.paymentType == 0 and im_maker and order.side:
|
|
@@ -693,9 +709,8 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
693
709
|
for oid, o in ords.items():
|
|
694
710
|
if o.status != Status.completed.value or oid in self.completed_orders:
|
|
695
711
|
continue
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
order_db = await self.create_order(order)
|
|
712
|
+
order = await self.get_order_full(o.id)
|
|
713
|
+
order_db = await self.create_order_db(order)
|
|
699
714
|
await sleep(1)
|
|
700
715
|
dmsgs = self.api.get_chat_messages(orderId=oid, size=200)["result"]["result"][::-1]
|
|
701
716
|
msgs = [Message.model_validate(m) for m in dmsgs if m["msgType"] in (1, 2, 7, 8)]
|
|
@@ -720,54 +735,6 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
720
735
|
# for t in papi.history():
|
|
721
736
|
# os = self.api.get_orders(page=1, size=30)
|
|
722
737
|
|
|
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
738
|
# @staticmethod
|
|
772
739
|
# def premium_up(mad: Ad, cad: Ad, k: Literal[-1, 1]):
|
|
773
740
|
# mpc, mpm, cpc, cpm = Decimal(mad.price), Decimal(mad.premium), Decimal(cad.price), Decimal(cad.premium)
|
|
@@ -777,235 +744,6 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
777
744
|
# if round(cpc * new_premium / cpm, 2) == m
|
|
778
745
|
# mad.premium = new_premium.to_eng_string()
|
|
779
746
|
|
|
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
747
|
async def take_ad(self, req: TakeAdReq):
|
|
1010
748
|
if req.price and req.is_sell and req.cur_:
|
|
1011
749
|
... # todo call the get_ad_details() only if lack of data
|
|
@@ -1046,83 +784,90 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
1046
784
|
return resp
|
|
1047
785
|
|
|
1048
786
|
async def watch_payeer(self, mcs: dict[int, "AgentClient"]):
|
|
1049
|
-
|
|
1050
|
-
|
|
787
|
+
await models.CoinEx.get(coin_id=1, ex=self.actor.ex).prefetch_related("coin")
|
|
788
|
+
await models.CurEx.get(cur_id=1, ex=self.actor.ex).prefetch_related("cur")
|
|
1051
789
|
post_pmexs = set(await models.PmEx.filter(pm_id=366, ex=self.actor.ex).prefetch_related("pm"))
|
|
1052
790
|
i = 0
|
|
1053
791
|
while True:
|
|
1054
792
|
try:
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
if
|
|
1058
|
-
|
|
793
|
+
breq = GetAds(coin_id=1, cur_id=1, is_sell=False, limit=50)
|
|
794
|
+
bs = await self.ex_client.ads(breq, post_pmexs=post_pmexs)
|
|
795
|
+
bs = [b for b in bs if float(b.price) < 100 or int(b.userId) in mcs.keys()]
|
|
796
|
+
if bs:
|
|
797
|
+
ad: Ad = bs[0]
|
|
1059
798
|
await self.bbot.send(
|
|
1060
799
|
193017646,
|
|
1061
800
|
f"price: {ad.price}\nnick: {ad.nickName}\nprice: {ad.price}"
|
|
1062
801
|
f"\nqty: {ad.quantity} [{ad.minAmount}-{ad.maxAmount}]",
|
|
1063
802
|
)
|
|
1064
|
-
am = min(float(ad.maxAmount), max(
|
|
803
|
+
am = min(float(ad.maxAmount), max(8000 + i, float(ad.minAmount)))
|
|
1065
804
|
req = TakeAdReq(
|
|
1066
805
|
ad_id=ad.id,
|
|
1067
806
|
amount=am,
|
|
1068
807
|
pm_id=14,
|
|
1069
|
-
is_sell=
|
|
808
|
+
is_sell=False,
|
|
1070
809
|
coin_id=1,
|
|
1071
810
|
cur_id=1,
|
|
1072
811
|
)
|
|
1073
812
|
ord_resp: OrderResp = await self.take_ad(req)
|
|
1074
813
|
# order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
|
|
1075
814
|
order: OrderFull = await self.get_order_info(ord_resp.orderId)
|
|
1076
|
-
odb = await self.
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
# res = await self.pm_clients[366].
|
|
815
|
+
odb = await self.create_order_db(order)
|
|
816
|
+
t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
|
|
817
|
+
await t.fetch_related("order__cred__pmcur__cur")
|
|
818
|
+
# res = await self.pm_clients[366].send(t)
|
|
819
|
+
await sleep(2)
|
|
820
|
+
self.api.mark_as_paid(
|
|
821
|
+
orderId=str(odb.exid),
|
|
822
|
+
paymentType=str(order.paymentTermList[0].paymentType), # pmex.exid
|
|
823
|
+
paymentId=order.paymentTermList[0].id, # credex.exid
|
|
824
|
+
)
|
|
825
|
+
await sleep(3)
|
|
1080
826
|
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
|
-
...
|
|
827
|
+
mcs[int(ad.userId)].api.release_assets(orderId=order.id)
|
|
1088
828
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
829
|
+
await sleep(5)
|
|
830
|
+
|
|
831
|
+
sreq = GetAds(coin_id=1, cur_id=1, is_sell=True, limit=50, kwargs={"post_pmexs": post_pmexs})
|
|
832
|
+
ss = await self.ex_client.ads(sreq, post_pmexs=post_pmexs)
|
|
833
|
+
ss = [s for s in ss if float(s.price) > 92 or int(s.userId) in mcs.keys()]
|
|
834
|
+
if ss:
|
|
835
|
+
ad: Ad = ss[0]
|
|
1093
836
|
await self.bbot.send(
|
|
1094
837
|
193017646,
|
|
1095
838
|
f"price: {ad.price}\nnick: {ad.nickName}\nprice: {ad.price}"
|
|
1096
839
|
f"\nqty: {ad.quantity} [{ad.minAmount}-{ad.maxAmount}]",
|
|
1097
840
|
)
|
|
1098
|
-
am = min(float(ad.maxAmount), max(
|
|
841
|
+
am = min(float(ad.maxAmount), max(10000 + i, float(ad.minAmount)))
|
|
1099
842
|
req = TakeAdReq(
|
|
1100
843
|
ad_id=ad.id,
|
|
1101
844
|
amount=am,
|
|
1102
845
|
pm_id=14,
|
|
1103
|
-
is_sell=
|
|
846
|
+
is_sell=True,
|
|
1104
847
|
coin_id=1,
|
|
1105
848
|
cur_id=1,
|
|
1106
849
|
)
|
|
1107
850
|
ord_resp: OrderResp = await self.take_ad(req)
|
|
1108
851
|
# order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
|
|
1109
852
|
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
|
-
)
|
|
853
|
+
odb = await self.create_order_db(order)
|
|
854
|
+
# t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
|
|
855
|
+
# await t.fetch_related("order__cred__pmcur__cur")
|
|
856
|
+
# res = await self.pm_clients[366].check_in(t)
|
|
857
|
+
await sleep(2)
|
|
1119
858
|
if int(ad.userId) in mcs:
|
|
1120
|
-
mcs[int(ad.userId)].api.
|
|
1121
|
-
|
|
1122
|
-
|
|
859
|
+
mcs[int(ad.userId)].api.mark_as_paid(
|
|
860
|
+
orderId=str(odb.exid),
|
|
861
|
+
paymentType=str(order.paymentTermList[0].paymentType), # pmex.exid
|
|
862
|
+
paymentId=order.paymentTermList[0].id, # credex.exid
|
|
863
|
+
)
|
|
864
|
+
await sleep(3)
|
|
865
|
+
self.api.release_assets(orderId=order.id)
|
|
866
|
+
await sleep(5)
|
|
867
|
+
|
|
1123
868
|
except Exception as e:
|
|
1124
869
|
logging.exception(e)
|
|
1125
|
-
await sleep(
|
|
870
|
+
await sleep(30)
|
|
1126
871
|
except HttpProcessingError as e:
|
|
1127
872
|
logging.error(e)
|
|
1128
873
|
print(end=".", flush=True)
|
|
@@ -1135,13 +880,379 @@ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
|
|
|
1135
880
|
am = 500 + i
|
|
1136
881
|
req = TakeAdReq(ad_id="1856989782009487360", amount=am, pm_id=366)
|
|
1137
882
|
ord_resp: OrderResp = await self.take_ad(req)
|
|
1138
|
-
order: OrderFull =
|
|
1139
|
-
odb = await self.
|
|
883
|
+
order: OrderFull = await self.get_order_full(int(ord_resp.orderId))
|
|
884
|
+
odb = await self.create_order_db(order)
|
|
1140
885
|
t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
|
|
1141
886
|
await t.fetch_related("order__cred__pmcur__cur")
|
|
1142
887
|
await self.pm_clients[366].send(t)
|
|
1143
888
|
...
|
|
1144
889
|
|
|
890
|
+
async def load_pending_orders(self):
|
|
891
|
+
po: dict[int, OrderItem] = await self.get_pending_orders()
|
|
892
|
+
if isinstance(po, int): # если код ошибки вместо результата
|
|
893
|
+
raise ValueError(po)
|
|
894
|
+
self.orders = {
|
|
895
|
+
o.exid: (o, await self.get_order_full(o.exid)) for o in await models.Order.filter(exid__in=po.keys())
|
|
896
|
+
}
|
|
897
|
+
for oid in po.keys() - self.orders.keys():
|
|
898
|
+
await self.load_order(oid)
|
|
899
|
+
|
|
900
|
+
async def _start_listen(self):
|
|
901
|
+
t = await self.ott()
|
|
902
|
+
ts = int(float(t["time_now"]) * 1000)
|
|
903
|
+
did = self.agent.auth["cookies"]["deviceId"]
|
|
904
|
+
u = f"wss://ws2.bybit.com/private?appid=bybit&os=web&deviceid={did}×tamp={ts}"
|
|
905
|
+
async with websockets.connect(u) as websocket:
|
|
906
|
+
auth_msg = json.dumps({"req_id": did, "op": "login", "args": [t["result"]]})
|
|
907
|
+
await websocket.send(auth_msg)
|
|
908
|
+
|
|
909
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
|
|
910
|
+
await websocket.send(sub_msg)
|
|
911
|
+
sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"SUPER_DEAL"}']})
|
|
912
|
+
await websocket.send(sub_msg)
|
|
913
|
+
sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"OTC_ORDER_STATUS"}']})
|
|
914
|
+
await websocket.send(sub_msg)
|
|
915
|
+
sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"WEB_THREE_SELL"}']})
|
|
916
|
+
await websocket.send(sub_msg)
|
|
917
|
+
sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"APPEALED_CHANGE"}']})
|
|
918
|
+
await websocket.send(sub_msg)
|
|
919
|
+
|
|
920
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order"]})
|
|
921
|
+
await websocket.send(sub_msg)
|
|
922
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order-eftd-complete-privilege-event"]})
|
|
923
|
+
await websocket.send(sub_msg)
|
|
924
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order-savings-product-event"]})
|
|
925
|
+
await websocket.send(sub_msg)
|
|
926
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.deal-core.order-savings-complete-event"]})
|
|
927
|
+
await websocket.send(sub_msg)
|
|
928
|
+
|
|
929
|
+
sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
|
|
930
|
+
await websocket.send(sub_msg)
|
|
931
|
+
while resp := await websocket.recv():
|
|
932
|
+
if data := json.loads(resp):
|
|
933
|
+
logging.info(f" {now().strftime('%H:%M:%S')} upd: {data.get('topic')}:{data.get('type')}")
|
|
934
|
+
await self.proc(data)
|
|
935
|
+
|
|
936
|
+
async def proc(self, data: dict):
|
|
937
|
+
match data.get("topic"):
|
|
938
|
+
case "OTC_ORDER_STATUS":
|
|
939
|
+
match data["type"]:
|
|
940
|
+
case "STATUS_CHANGE":
|
|
941
|
+
upd = StatusChange.model_validate(data["data"])
|
|
942
|
+
order_db, order = await self.load_order(upd.id)
|
|
943
|
+
match upd.status:
|
|
944
|
+
case Status.ws_new:
|
|
945
|
+
logging.info(f"Order {upd.id} CREATED at {upd.createDate}")
|
|
946
|
+
# await self.got_new_order(order_db, order)
|
|
947
|
+
|
|
948
|
+
# # сразу уменьшаем доступный остаток монеты/валюты
|
|
949
|
+
# await self.money_upd(order_db)
|
|
950
|
+
# if upd.side: # я покупатель - ждем мою оплату
|
|
951
|
+
# _dest = order.paymentTermList[0].accountNo
|
|
952
|
+
# if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
|
|
953
|
+
# return
|
|
954
|
+
# await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
|
|
955
|
+
# await self.send_payment(order_db)
|
|
956
|
+
case Status.created:
|
|
957
|
+
if upd.side == 0: # я продавец, ждем когда покупатель оплатит
|
|
958
|
+
# check_payment() # again
|
|
959
|
+
...
|
|
960
|
+
# if not (pmacdx := await self.get_pma_by_cdex(order)):
|
|
961
|
+
# return
|
|
962
|
+
# pma, cdx = pmacdx
|
|
963
|
+
# am, tid = await pma.check_in(
|
|
964
|
+
# float(order.amount),
|
|
965
|
+
# cdx.cred.pmcur.cur.ticker,
|
|
966
|
+
# # todo: почему в московском час.поясе?
|
|
967
|
+
# datetime.fromtimestamp(float(order.transferDate) / 1000),
|
|
968
|
+
# )
|
|
969
|
+
# if not tid:
|
|
970
|
+
# logging.info(f"Order {order.id} created at {order.createDate}, not paid yet")
|
|
971
|
+
# return
|
|
972
|
+
# try:
|
|
973
|
+
# t, is_new = await models.Transfer.update_or_create(
|
|
974
|
+
# dict(
|
|
975
|
+
# amount=int(float(order.amount) * 100),
|
|
976
|
+
# order=order_db,
|
|
977
|
+
# ),
|
|
978
|
+
# pmid=tid,
|
|
979
|
+
# )
|
|
980
|
+
# except IntegrityError as e:
|
|
981
|
+
# logging.error(tid)
|
|
982
|
+
# logging.error(order)
|
|
983
|
+
# logging.exception(e)
|
|
984
|
+
#
|
|
985
|
+
# if not is_new: # если по этому платежу уже отпущен другая продажа
|
|
986
|
+
# return
|
|
987
|
+
#
|
|
988
|
+
# # если висят незавершенные продажи с такой же суммой
|
|
989
|
+
# pos = (await self.get_orders_active(1))["result"]
|
|
990
|
+
# pos = [
|
|
991
|
+
# o
|
|
992
|
+
# for o in pos.get("items", [])
|
|
993
|
+
# if (
|
|
994
|
+
# o["amount"] == order.amount
|
|
995
|
+
# and o["id"] != upd.id
|
|
996
|
+
# and int(order.createDate) < int(o["createDate"]) + 15 * 60 * 1000
|
|
997
|
+
# # get full_order from o, and cred or pm from full_order:
|
|
998
|
+
# and self.api.get_order_details(orderId=o["id"])["result"][
|
|
999
|
+
# "paymentTermList"
|
|
1000
|
+
# ][0]["accountNo"]
|
|
1001
|
+
# == order.paymentTermList[0].accountNo
|
|
1002
|
+
# )
|
|
1003
|
+
# ]
|
|
1004
|
+
# curex = await models.CurEx.get(cur__ticker=order.currencyId, ex=self.ex_client.ex)
|
|
1005
|
+
# pos_db = await models.Order.filter(
|
|
1006
|
+
# exid__not=order.id,
|
|
1007
|
+
# cred_id=order_db.cred_id,
|
|
1008
|
+
# amount=int(float(order.amount) * 10**curex.scale),
|
|
1009
|
+
# status__not_in=[OrderStatus.completed, OrderStatus.canceled],
|
|
1010
|
+
# created_at__gt=now() - timedelta(minutes=15),
|
|
1011
|
+
# )
|
|
1012
|
+
# if pos or pos_db:
|
|
1013
|
+
# await self.ex_client.bot.send(
|
|
1014
|
+
# f"[Duplicate amount!]"
|
|
1015
|
+
# f"(https://www.bybit.com/ru-RU/p2p/orderList/{order.id})",
|
|
1016
|
+
# self.actor.person.user.username_id,
|
|
1017
|
+
# )
|
|
1018
|
+
# logging.warning("Duplicate amount!")
|
|
1019
|
+
# return
|
|
1020
|
+
#
|
|
1021
|
+
# # !!! ОТПРАВЛЯЕМ ДЕНЬГИ !!!
|
|
1022
|
+
# self.api.release_assets(orderId=upd.id)
|
|
1023
|
+
# logging.info(
|
|
1024
|
+
# f"Order {order.id} created, paid before #{tid}:{am} at {order.createDate}, and RELEASED at {now()}"
|
|
1025
|
+
# )
|
|
1026
|
+
elif upd.side == 1: # я покупатель - ждем мою оплату
|
|
1027
|
+
# pay()
|
|
1028
|
+
logging.warning(f"Order {upd.id} CREATED2 at {now()}")
|
|
1029
|
+
|
|
1030
|
+
case Status.paid:
|
|
1031
|
+
if order_db.status == OrderStatus.paid:
|
|
1032
|
+
return
|
|
1033
|
+
await order_db.update_from_dict(
|
|
1034
|
+
{
|
|
1035
|
+
"status": OrderStatus.paid,
|
|
1036
|
+
"payed_at": datetime.fromtimestamp(float(order.transferDate) / 1000),
|
|
1037
|
+
}
|
|
1038
|
+
).save()
|
|
1039
|
+
logging.info(f"Order {order.id} payed at {order_db.payed_at}")
|
|
1040
|
+
|
|
1041
|
+
case Status.appealed_by_seller: # just any appealed
|
|
1042
|
+
# todo: appealed by WHO? щас наугад стоит by_seller
|
|
1043
|
+
await order_db.update_from_dict(
|
|
1044
|
+
{
|
|
1045
|
+
"status": OrderStatus.appealed_by_seller,
|
|
1046
|
+
"appealed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
|
|
1047
|
+
}
|
|
1048
|
+
).save()
|
|
1049
|
+
logging.info(f"Order {order.id} appealed at {order_db.appealed_at}")
|
|
1050
|
+
|
|
1051
|
+
case Status.canceled:
|
|
1052
|
+
await order_db.update_from_dict({"status": OrderStatus.canceled}).save()
|
|
1053
|
+
logging.info(f"Order {order.id} canceled at {datetime.now()}")
|
|
1054
|
+
# await self.money_upd(order_db)
|
|
1055
|
+
|
|
1056
|
+
case Status.completed:
|
|
1057
|
+
await order_db.refresh_from_db()
|
|
1058
|
+
if order_db.status != OrderStatus.completed:
|
|
1059
|
+
await order_db.update_from_dict(
|
|
1060
|
+
{
|
|
1061
|
+
"status": OrderStatus.completed,
|
|
1062
|
+
"confirmed_at": datetime.fromtimestamp(float(order.updateDate) / 1000),
|
|
1063
|
+
}
|
|
1064
|
+
).save(update_fields=["status", "confirmed_at"])
|
|
1065
|
+
# await self.money_upd(order_db)
|
|
1066
|
+
|
|
1067
|
+
case _:
|
|
1068
|
+
logging.warning(f"Order {order.id} UNKNOWN STATUS {datetime.now()}")
|
|
1069
|
+
case "COUNT_DOWN":
|
|
1070
|
+
upd = CountDown.model_validate(data["data"])
|
|
1071
|
+
|
|
1072
|
+
case "OTC_USER_CHAT_MSG":
|
|
1073
|
+
match data["type"]:
|
|
1074
|
+
case "RECEIVE":
|
|
1075
|
+
upd = Receive.model_validate(data["data"])
|
|
1076
|
+
order_db, order = await self.load_order(upd.orderId)
|
|
1077
|
+
# got_msg()
|
|
1078
|
+
...
|
|
1079
|
+
# im_taker = order_db.taker_id == self.actor.id
|
|
1080
|
+
# im_buyer = order_db.ad.pair_side.is_sell == im_taker
|
|
1081
|
+
# if order_db.ad.auto_msg != upd.message and upd.roleType == "user":
|
|
1082
|
+
# msg, _ = await models.Msg.update_or_create(
|
|
1083
|
+
# {
|
|
1084
|
+
# "to_maker": upd.userId == self.actor.exid and im_taker,
|
|
1085
|
+
# "sent_at": datetime.fromtimestamp(float(upd.createDate) / 1000),
|
|
1086
|
+
# },
|
|
1087
|
+
# txt=upd.message,
|
|
1088
|
+
# order=order_db,
|
|
1089
|
+
# )
|
|
1090
|
+
# if not upd.message:
|
|
1091
|
+
# ...
|
|
1092
|
+
# if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
|
|
1093
|
+
# if not order_db.cred.detail.startswith(dest := g.group()):
|
|
1094
|
+
# order_db.cred.detail = dest
|
|
1095
|
+
# await order_db.save()
|
|
1096
|
+
# await self.send_payment(order_db)
|
|
1097
|
+
case "READ":
|
|
1098
|
+
# msg_read()
|
|
1099
|
+
upd = Read.model_validate(data["data"])
|
|
1100
|
+
|
|
1101
|
+
case "CLEAR":
|
|
1102
|
+
return
|
|
1103
|
+
case "OTC_USER_CHAT_MSG_V2":
|
|
1104
|
+
# msg dup
|
|
1105
|
+
...
|
|
1106
|
+
# match data["type"]:
|
|
1107
|
+
# case "RECEIVE":
|
|
1108
|
+
# upd = Receive.model_validate(data["data"])
|
|
1109
|
+
# case "READ":
|
|
1110
|
+
# upd = Read.model_validate(data["data"])
|
|
1111
|
+
# case "CLEAR":
|
|
1112
|
+
# pass
|
|
1113
|
+
# case _:
|
|
1114
|
+
# self.listen(data)
|
|
1115
|
+
case "SELLER_CANCEL_CHANGE":
|
|
1116
|
+
upd = SellerCancelChange.model_validate(data["data"])
|
|
1117
|
+
case None:
|
|
1118
|
+
if not data.get("success"):
|
|
1119
|
+
logging.error(data, "NOT SUCCESS!")
|
|
1120
|
+
else:
|
|
1121
|
+
return # success login, subscribes, input
|
|
1122
|
+
case _:
|
|
1123
|
+
logging.warning(data, "UNKNOWN TOPIC")
|
|
1124
|
+
|
|
1125
|
+
async def get_order_full(self, oid: int) -> OrderFull:
|
|
1126
|
+
order = self.api.get_order_details(orderId=oid)
|
|
1127
|
+
return OrderFull.model_validate(order["result"])
|
|
1128
|
+
|
|
1129
|
+
async def load_order(self, oid: int) -> tuple[models.Order, OrderFull]:
|
|
1130
|
+
if not self.orders.get(oid):
|
|
1131
|
+
order = await self.get_order_full(oid)
|
|
1132
|
+
if not (
|
|
1133
|
+
order_db := await models.Order.get_or_none(exid=oid, ad__maker__ex=self.actor.ex).prefetch_related(
|
|
1134
|
+
"ad__pair_side__pair", "cred__pmcur__cur"
|
|
1135
|
+
)
|
|
1136
|
+
):
|
|
1137
|
+
order_db = await self.create_order_db(order)
|
|
1138
|
+
self.orders[oid] = order_db, order
|
|
1139
|
+
return self.orders[oid]
|
|
1140
|
+
|
|
1141
|
+
async def money_upd(self, odb: models.Order):
|
|
1142
|
+
# обновляем остаток монеты
|
|
1143
|
+
await odb.fetch_related("ad__pair_side__pair", "ad__my_ad__credexs__cred__fiat", "cred__pmcur", "transfer")
|
|
1144
|
+
ass = await models.Asset.get(addr__coin_id=odb.ad.pair_side.pair.coin_id, addr__actor=self.actor)
|
|
1145
|
+
# обновляем остаток валюты
|
|
1146
|
+
im_maker = odb.ad.maker_id == self.actor.id
|
|
1147
|
+
im_seller = odb.ad.pair_side.is_sell == im_maker
|
|
1148
|
+
if im_maker:
|
|
1149
|
+
if _fiats := [cx.cred.fiat for cx in odb.ad.my_ad.credexs if cx.cred.fiat]:
|
|
1150
|
+
fiat = _fiats[0]
|
|
1151
|
+
await fiat.fetch_related("cred__pmcur__pm")
|
|
1152
|
+
else:
|
|
1153
|
+
raise ValueError(odb, "No Fiat")
|
|
1154
|
+
elif im_seller: # im taker
|
|
1155
|
+
fltr = dict(cred__person_id=self.actor.person_id)
|
|
1156
|
+
fltr |= (
|
|
1157
|
+
{"cred__ovr_pm_id": odb.cred.ovr_pm_id, "cred__pmcur__cur_id": odb.cred.pmcur.cur_id}
|
|
1158
|
+
if odb.cred.ovr_pm_id
|
|
1159
|
+
else {"cred__pmcur_id": odb.cred.pmcur_id}
|
|
1160
|
+
)
|
|
1161
|
+
if not (fiat := await models.Fiat.get_or_none(**fltr).prefetch_related("cred__pmcur__pm")):
|
|
1162
|
+
raise ValueError(odb, "No Fiat")
|
|
1163
|
+
fee = round(odb.amount * (fiat.cred.pmcur.pm.fee or 0) * 0.0001)
|
|
1164
|
+
# k = int(im_seller) * 2 - 1 # im_seller: 1, im_buyer: -1
|
|
1165
|
+
if odb.status == OrderStatus.created:
|
|
1166
|
+
if im_seller:
|
|
1167
|
+
ass.free -= odb.quantity
|
|
1168
|
+
ass.freeze += odb.quantity
|
|
1169
|
+
else: # я покупатель
|
|
1170
|
+
fiat.amount -= odb.amount + fee
|
|
1171
|
+
elif odb.status == OrderStatus.completed:
|
|
1172
|
+
if im_seller:
|
|
1173
|
+
fiat.amount += odb.amount
|
|
1174
|
+
else: # я покупатель
|
|
1175
|
+
ass.free += odb.quantity
|
|
1176
|
+
elif odb.status == OrderStatus.canceled:
|
|
1177
|
+
if im_seller:
|
|
1178
|
+
ass.free += odb.quantity
|
|
1179
|
+
ass.freeze -= odb.quantity
|
|
1180
|
+
else: # я покупатель
|
|
1181
|
+
fiat.amount += odb.amount + fee
|
|
1182
|
+
else:
|
|
1183
|
+
logging.exception(odb.id, f"STATUS: {odb.status.name}")
|
|
1184
|
+
await ass.save(update_fields=["free", "freeze"])
|
|
1185
|
+
await fiat.save(update_fields=["amount"])
|
|
1186
|
+
logging.info(f"Order #{odb.id} {odb.status.name}. Fiat: {fiat.amount}, Asset: {ass.free}")
|
|
1187
|
+
|
|
1188
|
+
async def send_payment(self, order_db: models.Order):
|
|
1189
|
+
if order_db.status != OrderStatus.created:
|
|
1190
|
+
return
|
|
1191
|
+
fmt_am = round(order_db.amount * 10**-2, 2)
|
|
1192
|
+
pma, cur = await self.get_pma_by_pmex(order_db)
|
|
1193
|
+
async with in_transaction():
|
|
1194
|
+
# отмечаем ордер на бирже "оплачен"
|
|
1195
|
+
pmex = await models.PmEx.get(pm_id=order_db.cred.pmcur.pm_id, ex=self.actor.ex)
|
|
1196
|
+
credex = await models.CredEx.get(cred=order_db.cred, ex=self.actor.ex)
|
|
1197
|
+
self.api.mark_as_paid(
|
|
1198
|
+
orderId=str(order_db.exid),
|
|
1199
|
+
paymentType=pmex.exid, # pmex.exid
|
|
1200
|
+
paymentId=str(credex.exid), # credex.exid
|
|
1201
|
+
)
|
|
1202
|
+
# проверяем не отправляли ли мы уже перевод по этому ордеру
|
|
1203
|
+
if t := await models.Transfer.get_or_none(order=order_db, amount=order_db.amount):
|
|
1204
|
+
await pma.bot.send(
|
|
1205
|
+
f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {order_db.cred.detail} #{t.pmid}!",
|
|
1206
|
+
self.actor.person.user.username_id,
|
|
1207
|
+
)
|
|
1208
|
+
raise Exception(
|
|
1209
|
+
f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {order_db.cred.detail} #{t.pmid}!"
|
|
1210
|
+
)
|
|
1211
|
+
|
|
1212
|
+
# ставим в бд статус "оплачен"
|
|
1213
|
+
order_db.status = OrderStatus.paid
|
|
1214
|
+
order_db.payed_at = datetime.now(timezone.utc)
|
|
1215
|
+
await order_db.save()
|
|
1216
|
+
# создаем перевод в бд
|
|
1217
|
+
t = models.Transfer(order=order_db, amount=order_db.amount, updated_at=now())
|
|
1218
|
+
# отправляем деньги
|
|
1219
|
+
tid, img = await pma.send(t)
|
|
1220
|
+
t.pmid = tid
|
|
1221
|
+
await t.save()
|
|
1222
|
+
await self.send_receipt(str(order_db.exid), tid) # отправляем продавцу чек
|
|
1223
|
+
logging.info(f"Order {order_db.exid} PAID at {datetime.now()}: {fmt_am}!")
|
|
1224
|
+
|
|
1225
|
+
async def send_receipt(self, oexid: str, tid: int) -> tuple[PmAgentClient | None, models.CredEx] | None:
|
|
1226
|
+
try:
|
|
1227
|
+
if res := self.api.upload_chat_file(upload_file=f"tmp/{tid}.png").get("result"):
|
|
1228
|
+
await sleep(0.5)
|
|
1229
|
+
self.api.send_chat_message(orderId=oexid, contentType="pic", message=res["url"], msgUuid=uuid4().hex)
|
|
1230
|
+
except Exception as e:
|
|
1231
|
+
logging.error(e)
|
|
1232
|
+
await sleep(0.5)
|
|
1233
|
+
self.api.send_chat_message(orderId=oexid, contentType="str", message=f"#{tid}", msgUuid=uuid4().hex)
|
|
1234
|
+
|
|
1235
|
+
async def get_pma_by_cdex(self, order: OrderFull) -> tuple[PmAgentClient | None, models.CredEx] | None:
|
|
1236
|
+
cdxs = await models.CredEx.filter(
|
|
1237
|
+
ex=self.ex_client.ex,
|
|
1238
|
+
exid__in=[ptl.id for ptl in order.paymentTermList],
|
|
1239
|
+
cred__person=self.actor.person,
|
|
1240
|
+
).prefetch_related("cred__pmcur__cur")
|
|
1241
|
+
pmas = [pma for cdx in cdxs if (pma := self.pm_clients.get(cdx.cred.pmcur.pm_id))]
|
|
1242
|
+
if not len(pmas):
|
|
1243
|
+
# raise ValueError(order.paymentTermList, f"No pm_agents for {order.paymentTermList[0].paymentType}")
|
|
1244
|
+
return None
|
|
1245
|
+
elif len(pmas) > 1:
|
|
1246
|
+
logging.error(order.paymentTermList, f">1 pm_agents for {cdxs[0].cred.pmcur.pm_id}")
|
|
1247
|
+
else:
|
|
1248
|
+
return pmas[0], cdxs[0]
|
|
1249
|
+
|
|
1250
|
+
async def get_pma_by_pmex(self, order_db: models.Order) -> tuple[PmAgentClient, str]:
|
|
1251
|
+
pma = self.pm_clients.get(order_db.cred.pmcur.pm_id)
|
|
1252
|
+
if pma:
|
|
1253
|
+
return pma, order_db.cred.pmcur.cur.ticker
|
|
1254
|
+
logging.error(f"No pm_agents for {order_db.cred.pmcur.pm_id}")
|
|
1255
|
+
|
|
1145
1256
|
|
|
1146
1257
|
def ms2utc(msk_ts_str: str):
|
|
1147
1258
|
return datetime.fromtimestamp(int(msk_ts_str) / 1000, timezone(timedelta(hours=3), name="MSK"))
|
|
@@ -1164,23 +1275,6 @@ def detailed_diff(str1, str2):
|
|
|
1164
1275
|
return "".join(result)
|
|
1165
1276
|
|
|
1166
1277
|
|
|
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
1278
|
class ExcCode(IntEnum):
|
|
1185
1279
|
FixPriceLimit = 912120022
|
|
1186
1280
|
RareLimit = 912120050
|
|
@@ -1209,7 +1303,7 @@ async def main():
|
|
|
1209
1303
|
cn = await init_db(TORM)
|
|
1210
1304
|
|
|
1211
1305
|
agent = (
|
|
1212
|
-
await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=
|
|
1306
|
+
await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=5)
|
|
1213
1307
|
.prefetch_related(
|
|
1214
1308
|
"actor__ex",
|
|
1215
1309
|
"actor__person__user__gmail",
|
|
@@ -1225,7 +1319,10 @@ async def main():
|
|
|
1225
1319
|
ex = await models.Ex.get(name="Bybit")
|
|
1226
1320
|
ecl: ExClient = ex.client(filebot)
|
|
1227
1321
|
abot = XyncBot(PAY_TOKEN, cn)
|
|
1228
|
-
|
|
1322
|
+
# pmas = await models.PmAgent.filter(active=True, user_id=1).prefetch_related("pm", "user__gmail")
|
|
1323
|
+
# pm_clients = {pma.pm_id: pma.client(abot) for pma in pmas}
|
|
1324
|
+
prx = PRX and "http://" + PRX
|
|
1325
|
+
cl: AgentClient = agent.client(ecl, filebot, abot, proxy=prx)
|
|
1229
1326
|
|
|
1230
1327
|
# req = TakeAdReq(ad_id=1955696985964089344, amount=504, pm_id=128)
|
|
1231
1328
|
# await cl.take_ad(req)
|
|
@@ -1236,11 +1333,14 @@ async def main():
|
|
|
1236
1333
|
# await cl.ex_client.set_pairs()
|
|
1237
1334
|
# await cl.ex_client.set_pms()
|
|
1238
1335
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1336
|
+
await cl.set_creds()
|
|
1337
|
+
await cl.export_my_ads()
|
|
1338
|
+
|
|
1339
|
+
my_ad = await models.MyAd[5]
|
|
1340
|
+
await cl.ad_share(my_ad.id)
|
|
1241
1341
|
|
|
1242
1342
|
ms = await models.Agent.filter(
|
|
1243
|
-
actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, actor__person__user__id__in=[
|
|
1343
|
+
actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, actor__person__user__id__in=[3]
|
|
1244
1344
|
).prefetch_related(
|
|
1245
1345
|
"actor__ex",
|
|
1246
1346
|
"actor__person__user__gmail",
|
|
@@ -1251,7 +1351,7 @@ async def main():
|
|
|
1251
1351
|
mcs = {m.actor.exid: m.client(ecl, filebot, abot) for m in ms}
|
|
1252
1352
|
|
|
1253
1353
|
await gather(
|
|
1254
|
-
create_task(cl.start(
|
|
1354
|
+
# create_task(cl.start()),
|
|
1255
1355
|
create_task(cl.watch_payeer(mcs)),
|
|
1256
1356
|
)
|
|
1257
1357
|
# ensure_future(cl.start(True))
|