xync-client 0.0.141__py3-none-any.whl → 0.0.155__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.
@@ -2,15 +2,17 @@ import asyncio
2
2
  import logging
3
3
  import re
4
4
  from asyncio import sleep, gather
5
- from collections import defaultdict
5
+ from asyncio.tasks import create_task
6
6
  from datetime import datetime, timedelta, timezone
7
7
  from difflib import SequenceMatcher
8
8
  from enum import IntEnum
9
+ from hashlib import sha256
9
10
  from http.client import HTTPException
10
11
  from math import floor
11
12
  from typing import Literal
12
13
 
13
14
  import pyotp
15
+ from aiohttp.http_exceptions import HttpProcessingError
14
16
  from asyncpg import ConnectionDoesNotExistError
15
17
  from bybit_p2p import P2P
16
18
  from bybit_p2p._exceptions import FailedRequestError
@@ -18,20 +20,26 @@ from payeer_api import PayeerAPI
18
20
  from pyro_client.client.file import FileClient
19
21
  from tortoise import BaseDBAsyncClient
20
22
  from tortoise.exceptions import IntegrityError
21
- from tortoise.expressions import F, Q
23
+ from tortoise.expressions import Q
22
24
  from tortoise.functions import Count
23
25
  from tortoise.signals import post_save
26
+ from tortoise.timezone import now
24
27
  from urllib3.exceptions import ReadTimeoutError
28
+ from x_client import df_hdrs
25
29
  from x_model import init_db
26
30
  from x_model.func import ArrayAgg
31
+ from xync_bot import XyncBot
32
+ from xync_client.Bybit.InAgent import InAgentClient
33
+
34
+ from xync_client.Bybit.ex import ExClient
27
35
  from xync_schema import models
28
- from xync_schema.enums import OrderStatus
36
+ from xync_schema.enums import OrderStatus, AgentStatus
29
37
 
30
- from xync_schema.models import Actor, Cond, CondSim, PmCur, PairSide
38
+ from xync_schema.models import Actor, PmCur, Agent
31
39
 
32
40
  from xync_client.Abc.Agent import BaseAgentClient
33
- from xync_client.Abc.xtype import BaseOrderReq, FlatDict
34
- from xync_client.Bybit.etype.ad import AdPostRequest, AdUpdateRequest, Ad, AdStatus
41
+ from xync_client.Abc.xtype import FlatDict, BaseOrderReq
42
+ from xync_client.Bybit.etype.ad import AdPostRequest, AdUpdateRequest, Ad, AdStatus, MyAd
35
43
  from xync_client.Bybit.etype.cred import CredEpyd
36
44
  from xync_client.Bybit.etype.order import (
37
45
  OrderRequest,
@@ -42,17 +50,20 @@ from xync_client.Bybit.etype.order import (
42
50
  OrderFull,
43
51
  Message,
44
52
  Status,
53
+ OrderSellRequest,
54
+ TakeAdReq,
45
55
  )
46
- from xync_client.loader import TORM, NET_TOKEN
56
+ from xync_client.loader import TORM, NET_TOKEN, PAY_TOKEN
47
57
 
48
58
 
49
59
  class NoMakerException(Exception):
50
60
  pass
51
61
 
52
62
 
53
- class AgentClient(BaseAgentClient): # Bybit client
54
- host = "api2.bybit.com"
55
- headers = {"cookie": ";"} # rewrite token for public methods
63
+ class AgentClient(BaseAgentClient, InAgentClient): # Bybit client
64
+ headers = df_hdrs | {"accept-language": "ru-RU"}
65
+ sec_hdrs: dict[str, str]
66
+ # rewrite token for public methods
56
67
  api: P2P
57
68
  last_ad_id: list[str] = []
58
69
  update_ad_body = {
@@ -82,28 +93,29 @@ class AgentClient(BaseAgentClient): # Bybit client
82
93
  "actionType": "MODIFY",
83
94
  "securityRiskToken": "",
84
95
  }
85
- all_conds: dict[int, tuple[str, set[int]]] = {}
86
- cond_sims: dict[int, int] = defaultdict(set)
87
- rcond_sims: dict[int, set[int]] = defaultdict(set) # backward
88
- tree: dict = {}
89
96
 
90
- def __init__(self, actor: Actor, bot: FileClient, **kwargs):
91
- super().__init__(actor, bot, **kwargs)
92
- self.api = P2P(testnet=False, api_key=actor.agent.auth["key"], api_secret=actor.agent.auth["sec"])
93
- self.hist: dict = None
94
- self.completed_orders: list[int] = None
97
+ def __init__(self, agent: Agent, ex_client: ExClient, fbot: FileClient, bbot: XyncBot, **kwargs):
98
+ super().__init__(agent, ex_client, fbot, bbot, **kwargs)
99
+ self.sec_hdrs = {
100
+ "accept-language": "ru,en;q=0.9",
101
+ "gdfp": agent.auth["Risktoken"],
102
+ "tx-id": agent.auth["Risktoken"],
103
+ }
104
+ self.api = P2P(testnet=False, api_key=agent.auth["key"], api_secret=agent.auth["sec"])
105
+ self.hist: dict | None = None
106
+ self.completed_orders: list[int] | None = None
95
107
 
96
108
  """ Private METHs"""
97
109
 
98
110
  async def fiat_new(self, payment_type: int, real_name: str, account_number: str) -> FlatDict | None:
99
111
  method1 = await self._post(
100
- "/fiat/otc/user/payment/new_create",
112
+ "/x-api/fiat/otc/user/payment/new_create",
101
113
  {"paymentType": payment_type, "realName": real_name, "accountNo": account_number, "securityRiskToken": ""},
102
114
  )
103
115
  if srt := method1["result"]["securityRiskToken"]:
104
116
  await self._check_2fa(srt)
105
117
  method2 = await self._post(
106
- "/fiat/otc/user/payment/new_create",
118
+ "/x-api/fiat/otc/user/payment/new_create",
107
119
  {
108
120
  "paymentType": payment_type,
109
121
  "realName": real_name,
@@ -115,12 +127,8 @@ class AgentClient(BaseAgentClient): # Bybit client
115
127
  else:
116
128
  return logging.exception(method1)
117
129
 
118
- async def get_payment_method(self, fiat_id: int = None) -> dict:
119
- list_methods = self.creds()
120
- if fiat_id:
121
- fiat = [m for m in list_methods if m["id"] == fiat_id][0]
122
- return fiat
123
- return list_methods[1]
130
+ def get_payment_method(self, fiat_id: int) -> CredEpyd:
131
+ return self.creds()[fiat_id]
124
132
 
125
133
  def creds(self) -> dict[int, CredEpyd]:
126
134
  data = self.api.get_user_payment_types()
@@ -144,29 +152,37 @@ class AgentClient(BaseAgentClient): # Bybit client
144
152
  elif not cur_id: # is new Cred
145
153
  cur_id = (
146
154
  pmex.pm.df_cur_id
155
+ or await self.guess_cur(ecdx, len(pmex.pm.curs) > 1 and pmex.pm.curs)
147
156
  or (pmex.pm.country_id and (await pmex.pm.country).cur_id)
148
- # or (ecdx.currencyBalance and await models.Cur.get_or_none(ticker=ecdx.currencyBalance[0]))
149
- or (0 < len(pmex.pm.curs) < 30 and pmex.pm.curs[-1].id)
150
- or await self.guess_cur(ecdx)
157
+ # or (ecdx.currencyBalance and await models.Cur.get_or_none(ticker=ecdx.currencyBalance[0])) # это че еще за хуйня?
151
158
  )
152
159
  if not cur_id:
153
160
  raise Exception(f"Set default cur for {pmex.name}")
154
161
  if not (pmcur := await models.PmCur.get_or_none(cur_id=cur_id, pm_id=pmex.pm_id)):
155
- raise HTTPException(f"No PmCur with cur#{ecdx.currencyBalance} and pm#{ecdx.paymentType}", 404)
156
- dct = {
157
- "pmcur_id": pmcur.id,
158
- "name": ecdx.realName,
159
- "person_id": pers_id or self.actor.person_id,
160
- "detail": ecdx.accountNo,
161
- "extra": ecdx.branchName or ecdx.bankName or ecdx.qrcode or ecdx.payMessage or ecdx.paymentExt1,
162
- } # todo: WTD with multicur pms?
163
- cred_in = models.Cred.validate(dct, False)
164
- cred_db, _ = await models.Cred.update_or_create(**cred_in.df_unq())
162
+ raise HTTPException(f"No PmCur with cur#{cur_id} and pm#{ecdx.paymentType}", 404)
163
+ xtr = ecdx.branchName
164
+ if ecdx.bankName:
165
+ xtr += (" | " if xtr else "") + ecdx.bankName
166
+ elif ecdx.payMessage:
167
+ xtr += (" | " if xtr else "") + ecdx.payMessage
168
+ elif ecdx.qrcode:
169
+ xtr += (" | " if xtr else "") + ecdx.qrcode
170
+ elif ecdx.paymentExt1:
171
+ xtr += (" | " if xtr else "") + ecdx.paymentExt1
172
+ cred_db, _ = await models.Cred.update_or_create(
173
+ {
174
+ "name": ecdx.realName,
175
+ "extra": xtr,
176
+ },
177
+ pmcur=pmcur,
178
+ person_id=pers_id or self.actor.person_id,
179
+ detail=ecdx.accountNo or ecdx.payMessage,
180
+ )
165
181
  credex_in = models.CredEx.validate({"exid": ecdx.id, "cred_id": cred_db.id, "ex_id": self.actor.ex.id})
166
182
  credex_db, _ = await models.CredEx.update_or_create(**credex_in.df_unq())
167
183
  return credex_db
168
184
 
169
- async def guess_cur(self, ecdx: CredEpyd):
185
+ async def guess_cur(self, ecdx: CredEpyd, curs: list[models.Cur]):
170
186
  mbs = ecdx.bankName.split(", ")
171
187
  mbs += ecdx.branchName.split(" / ")
172
188
  mbs = {mb.lower(): mb for mb in mbs}
@@ -178,7 +194,7 @@ class AgentClient(BaseAgentClient): # Bybit client
178
194
  .values("pmcurs__cur_id", "names", "ccnt")
179
195
  ):
180
196
  return pms[0]["pmcurs__cur_id"]
181
- curs = {c.ticker: c.id for c in await models.Cur.all()}
197
+ curs = {c.ticker: c.id for c in curs or await models.Cur.all()}
182
198
  for cur, cid in curs.items():
183
199
  if re.search(re.compile(rf"\({cur}\)$"), ecdx.bankName):
184
200
  return cid
@@ -188,6 +204,8 @@ class AgentClient(BaseAgentClient): # Bybit client
188
204
  return cid
189
205
  if re.search(re.compile(rf"\({cur}\)$"), ecdx.payMessage):
190
206
  return cid
207
+ if re.search(re.compile(rf"\({cur}\)$"), ecdx.paymentExt1):
208
+ return cid
191
209
  return None
192
210
 
193
211
  # 25: Список реквизитов моих платежных методов
@@ -197,34 +215,34 @@ class AgentClient(BaseAgentClient): # Bybit client
197
215
  return credexs
198
216
 
199
217
  async def ott(self):
200
- t = await self._post("/user/private/ott")
218
+ t = await self._post("/x-api/user/private/ott")
201
219
  return t
202
220
 
203
221
  # 27
204
222
  async def fiat_upd(self, fiat_id: int, detail: str, name: str = None) -> dict:
205
223
  fiat = self.get_payment_method(fiat_id)
206
- fiat["realName"] = name
207
- fiat["accountNo"] = detail
208
- result = await self._post("/fiat/otc/user/payment/new_update", fiat)
224
+ fiat.realName = name
225
+ fiat.accountNo = detail
226
+ result = await self._post("/x-api/fiat/otc/user/payment/new_update", fiat.model_dump(exclude_none=True))
209
227
  srt = result["result"]["securityRiskToken"]
210
228
  await self._check_2fa(srt)
211
- fiat["securityRiskToken"] = srt
212
- result2 = await self._post("/fiat/otc/user/payment/new_update", fiat)
229
+ fiat.securityRiskToken = srt
230
+ result2 = await self._post("/fiat/otc/user/payment/new_update", fiat.model_dump(exclude_none=True))
213
231
  return result2
214
232
 
215
233
  # 28
216
234
  async def fiat_del(self, fiat_id: int) -> dict | str:
217
235
  data = {"id": fiat_id, "securityRiskToken": ""}
218
- method = await self._post("/fiat/otc/user/payment/new_delete", data)
236
+ method = await self._post("/x-api/fiat/otc/user/payment/new_delete", data)
219
237
  srt = method["result"]["securityRiskToken"]
220
238
  await self._check_2fa(srt)
221
239
  data["securityRiskToken"] = srt
222
- delete = await self._post("/fiat/otc/user/payment/new_delete", data)
240
+ delete = await self._post("/x-api/fiat/otc/user/payment/new_delete", data)
223
241
  return delete
224
242
 
225
243
  async def switch_ads(self, new_status: AdStatus) -> dict:
226
244
  data = {"workStatus": new_status.name} # todo: переделать на апи, там status 0 -> 1
227
- res = await self._post("/fiat/otc/maker/work-config/switch", data)
245
+ res = await self._post("/x-api/fiat/otc/maker/work-config/switch", data)
228
246
  return res
229
247
 
230
248
  async def ads(
@@ -232,27 +250,36 @@ class AgentClient(BaseAgentClient): # Bybit client
232
250
  cnx: models.CoinEx,
233
251
  crx: models.CurEx,
234
252
  is_sell: bool,
235
- pmxs: list[models.PmEx],
253
+ pmexs: list[models.PmEx],
236
254
  amount: int = None,
237
255
  lim: int = 50,
238
256
  vm_filter: bool = False,
257
+ post_pmexs: set[models.PmEx] = None,
239
258
  ) -> list[Ad]:
240
- return await self.ex_client.ads(
241
- cnx.exid, crx.exid, is_sell, [pmex.exid for pmex in pmxs or []], amount, lim, vm_filter
242
- )
243
-
244
- def online_ads(self) -> str:
245
- online = self._get("/fiat/otc/maker/work-config/get")
246
- return online["result"]["workStatus"]
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
247
274
 
248
275
  @staticmethod
249
276
  def get_rate(list_ads: list) -> float:
250
277
  ads = [ad for ad in list_ads if set(ad["payments"]) - {"5", "51"}]
251
278
  return float(ads[0]["price"])
252
279
 
253
- def my_ads(self, active: bool = True, page: int = 1) -> list[Ad]:
280
+ def my_ads(self, active: bool = True, page: int = 1) -> list[MyAd]:
254
281
  resp = self.api.get_ads_list(size="30", page=str(page), status=AdStatus.active if active else AdStatus.sold_out)
255
- ads = [Ad.model_validate(ad) for ad in resp["result"]["items"]]
282
+ ads = [MyAd.model_validate(ad) for ad in resp["result"]["items"]]
256
283
  if resp["result"]["count"] > 30 * page:
257
284
  ads.extend(self.my_ads(active, page + 1))
258
285
  return ads
@@ -261,35 +288,63 @@ class AgentClient(BaseAgentClient): # Bybit client
261
288
  ads = self.my_ads(True)
262
289
  if not active:
263
290
  ads += self.my_ads(False)
264
- res = [await self.ad_create(ad, actor=self.actor) for ad in ads]
265
- res = [await models.MyAd.update_or_create(ad=ad) for ad in res]
266
- return len(res)
291
+ for ad in ads:
292
+ ad_db = await self.ex_client.ad_load(ad, maker=self.actor)
293
+ mad_db, _ = await models.MyAd.update_or_create(ad=ad_db)
294
+ exids = [pt.id for pt in ad.paymentTerms]
295
+ credexs = await models.CredEx.filter(ex_id=self.actor.ex_id, exid__in=exids)
296
+ await mad_db.credexs.add(*credexs)
297
+ return len(ads)
267
298
 
268
299
  def get_security_token_create(self):
269
- data = self._post("/fiat/otc/item/create", self.create_ad_body)
300
+ data = self._post("/x-api/fiat/otc/item/create", self.create_ad_body)
270
301
  if data["ret_code"] == 912120019: # Current user can not to create add as maker
271
302
  raise NoMakerException(data)
272
303
  security_risk_token = data["result"]["securityRiskToken"]
273
304
  return security_risk_token
274
305
 
275
- def _check_2fa(self, risk_token):
276
- # 2fa code
277
- bybit_secret = self.agent.auth["2fa"]
278
- totp = pyotp.TOTP(bybit_secret)
279
- totp_code = totp.now()
280
-
281
- res = self._post(
282
- "/user/public/risk/verify", {"risk_token": risk_token, "component_list": {"google2fa": totp_code}}
283
- )
306
+ async def _check_2fa(self, risk_token) -> int:
307
+ data = {"risk_token": risk_token}
308
+ res = await self._post("/x-api/user/public/risk/components", data, hdrs=self.sec_hdrs)
284
309
  if res["ret_msg"] != "success":
285
- print("Wrong 2fa, wait 5 secs and retry..")
286
- sleep(5)
287
- self._check_2fa(risk_token)
288
- return res
310
+ raise HTTPException("get")
311
+ cres = sorted(res["result"]["component_list"], key=lambda c: c["component_id"], reverse=True)
312
+ vdata = {
313
+ "risk_token": risk_token,
314
+ "component_list": {c["component_id"]: await self.__get_2fa(c["component_id"], risk_token) for c in cres},
315
+ }
316
+ res = await self._post("/x-api/user/public/risk/verify", vdata, hdrs=self.sec_hdrs)
317
+ if er_code := res["ret_code"] or res["result"]["ret_code"]: # если код не 0, значит ошибка
318
+ logging.error("Wrong 2fa, wait 5 secs and retry..")
319
+ await sleep(5)
320
+ return await self._check_2fa(risk_token)
321
+ return er_code
322
+
323
+ async def __get_2fa(
324
+ self, typ: Literal["google2fa", "email_verify", "payment_password_verify", "phone_verify"], rt: str = None
325
+ ):
326
+ res = {"ret_msg": "success"}
327
+ if typ != "google2fa":
328
+ data = {"risk_token": rt, "component_id": typ}
329
+ res = await self._post("/x-api/user/public/risk/send/code", data, hdrs=self.sec_hdrs)
330
+ if res["ret_msg"] == "success":
331
+ if typ == "google2fa":
332
+ bybit_secret = self.agent.auth["2fa"]
333
+ totp = pyotp.TOTP(bybit_secret)
334
+ return totp.now()
335
+ elif typ == "email_verify":
336
+ return self.gmail.bybit_code()
337
+ elif typ == "payment_password_verify":
338
+ hp = sha256(self.agent.auth["pass"].encode()).hexdigest()
339
+ return hp
340
+ elif cool_down := int(res["result"]["cool_down"]):
341
+ await sleep(cool_down)
342
+ return self.__get_2fa(typ, rt)
343
+ raise Exception("2fa fail")
289
344
 
290
345
  def _post_ad(self, risk_token: str):
291
346
  self.create_ad_body.update({"securityRiskToken": risk_token})
292
- data = self._post("/fiat/otc/item/create", self.create_ad_body)
347
+ data = self._post("/x-api/fiat/otc/item/create", self.create_ad_body)
293
348
  return data
294
349
 
295
350
  # создание объявлений
@@ -315,7 +370,7 @@ class AgentClient(BaseAgentClient): # Bybit client
315
370
 
316
371
  def get_security_token_update(self) -> str:
317
372
  self.update_ad_body["id"] = self.last_ad_id
318
- data = self._post("/fiat/otc/item/update", self.update_ad_body)
373
+ data = self._post("/x-api/fiat/otc/item/update", self.update_ad_body)
319
374
  security_risk_token = data["result"]["securityRiskToken"]
320
375
  return security_risk_token
321
376
 
@@ -332,46 +387,75 @@ class AgentClient(BaseAgentClient): # Bybit client
332
387
 
333
388
  def update_ad(self, risk_token: str):
334
389
  self.update_ad_body.update({"securityRiskToken": risk_token})
335
- data = self._post("/fiat/otc/item/update", self.update_ad_body)
390
+ data = self._post("/x-api/fiat/otc/item/update", self.update_ad_body)
336
391
  return data
337
392
 
338
393
  def ad_del(self, ad_id: int):
339
394
  data = self.api.remove_ad(itemId=ad_id)
340
395
  return data
341
396
 
342
- async def order_request(self, br: BaseOrderReq) -> OrderResp:
343
- res0 = await self._post("/fiat/otc/item/simple", data={"item_id": str(br.ad_id)})
344
- if res0["ret_code"] == 0:
345
- res0 = res0["result"]
346
- res0 = PreOrderResp.model_validate(res0)
397
+ async def __preorder_request(self, ad_id: int) -> PreOrderResp:
398
+ res = await self._post("/x-api/fiat/otc/item/simple", json={"item_id": str(ad_id)})
399
+ if res["ret_code"] == 0:
400
+ res = res["result"]
401
+ return PreOrderResp.model_validate(res)
402
+
403
+ async def _order_request(self, bor: BaseOrderReq) -> OrderResp:
404
+ por: PreOrderResp = await self.__preorder_request(bor.ad_id)
347
405
  req = OrderRequest(
348
- itemId=br.ad_id,
349
- tokenId=br.coin_exid,
350
- currencyId=br.cur_exid,
351
- side=str(OrderRequest.Side(int(br.is_sell))),
352
- amount=str(br.fiat_amount or br.asset_amount * float(res0.price)),
353
- curPrice=res0.curPrice,
354
- quantity=str(br.asset_amount or round(br.fiat_amount / float(res0.price), br.coin_scale)),
355
- flag="amount" if br.amount_is_fiat else "quantity",
406
+ itemId=por.id,
407
+ tokenId=bor.coin_exid,
408
+ currencyId=bor.cur_exid,
409
+ side="1" if bor.is_sell else "0",
410
+ amount=f"{bor.fiat_amount:.2f}".rstrip("0").rstrip("."),
411
+ curPrice=por.curPrice,
412
+ quantity=str(round(bor.fiat_amount / float(por.price), bor.coin_scale)),
413
+ flag="amount",
414
+ # online="0"
356
415
  )
416
+ if bor.is_sell:
417
+ credex = await models.CredEx.get(
418
+ cred__person_id=self.actor.person_id,
419
+ cred__pmcur__pm__pmexs__exid=[pp for pp in por.payments if pp == bor.pmex_exid][0], # bor.pmex_exid
420
+ cred__pmcur__pm__pmexs__ex_id=self.ex_client.ex.id,
421
+ cred__pmcur__cur__ticker=bor.cur_exid,
422
+ )
423
+ req = OrderSellRequest(**req.model_dump(), paymentType=bor.pmex_exid, paymentId=str(credex.exid))
357
424
  # вот непосредственно сам запрос на ордер
358
- res = await self._post("/fiat/otc/order/create", data=req.model_dump())
425
+ return await self.__order_create(req, bor)
426
+
427
+ async def __order_create(self, req: OrderRequest | OrderSellRequest, bor: BaseOrderReq) -> OrderResp:
428
+ hdrs = {"Risktoken": self.sec_hdrs["gdfp"]}
429
+ res: dict = await self._post("/x-api/fiat/otc/order/create", json=req.model_dump(), hdrs=hdrs)
359
430
  if res["ret_code"] == 0:
360
- return OrderResp.model_validate(res["result"])
431
+ resp = OrderResp.model_validate(res["result"])
432
+ elif res["ret_code"] == 10001:
433
+ logging.error(req.model_dump(), "POST", self.session._base_url)
434
+ raise HTTPException()
361
435
  elif res["ret_code"] == 912120030 or res["ret_msg"] == "The price has changed, please try again later.":
362
- return await self.order_request(br)
436
+ resp = await self._order_request(bor)
437
+ else:
438
+ logging.exception(res)
439
+ if not resp.orderId and resp.needSecurityRisk:
440
+ if rc := await self._check_2fa(resp.securityRiskToken):
441
+ await self.bbot.send(self.actor.person.user.username_id, f"Bybit 2fa: {rc}")
442
+ raise Exception(f"Bybit 2fa: {rc}")
443
+ # еще раз уже с токеном
444
+ req.securityRiskToken = resp.securityRiskToken
445
+ resp = await self.__order_create(req, bor)
446
+ return resp
363
447
 
364
448
  async def cancel_order(self, order_id: str) -> bool:
365
449
  cr = CancelOrderReq(orderId=order_id)
366
- res = await self._post("/fiat/otc/order/cancel", cr.model_dump())
450
+ res = await self._post("/x-api/fiat/otc/order/cancel", cr.model_dump())
367
451
  return res["ret_code"] == 0
368
452
 
369
- def get_order_info(self, order_id: str) -> dict:
370
- data = self._post("/fiat/otc/order/info", json={"orderId": order_id})
371
- return data["result"]
453
+ async def get_order_info(self, order_id: str) -> OrderFull:
454
+ data = await self._post("/x-api/fiat/otc/order/info", json={"orderId": order_id})
455
+ return OrderFull.model_validate(data["result"])
372
456
 
373
457
  def get_chat_msg(self, order_id):
374
- data = self._post("/fiat/otc/order/message/listpage", json={"orderId": order_id, "size": 100})
458
+ data = self._post("/x-api/fiat/otc/order/message/listpage", json={"orderId": order_id, "size": 100})
375
459
  msgs = [
376
460
  {"text": msg["message"], "type": msg["contentType"], "role": msg["roleType"], "user_id": msg["userId"]}
377
461
  for msg in data["result"]["result"]
@@ -380,14 +464,14 @@ class AgentClient(BaseAgentClient): # Bybit client
380
464
  return msgs
381
465
 
382
466
  def block_user(self, user_id: str):
383
- return self._post("/fiat/p2p/user/add_block_user", {"blockedUserId": user_id})
467
+ return self._post("/x-api/fiat/p2p/user/add_block_user", {"blockedUserId": user_id})
384
468
 
385
469
  def unblock_user(self, user_id: str):
386
- return self._post("/fiat/p2p/user/delete_block_user", {"blockedUserId": user_id})
470
+ return self._post("/x-api/fiat/p2p/user/delete_block_user", {"blockedUserId": user_id})
387
471
 
388
472
  def user_review_post(self, order_id: str):
389
473
  return self._post(
390
- "/fiat/otc/order/appraise/modify",
474
+ "/x-api/fiat/otc/order/appraise/modify",
391
475
  {
392
476
  "orderId": order_id,
393
477
  "anonymous": "0",
@@ -407,7 +491,7 @@ class AgentClient(BaseAgentClient): # Bybit client
407
491
  self, side: int = None, status: int = None, begin_time: int = None, end_time: int = None, token_id: str = None
408
492
  ):
409
493
  return await self._post(
410
- "/fiat/otc/order/pending/simplifyList",
494
+ "/x-api/fiat/otc/order/pending/simplifyList",
411
495
  {
412
496
  "status": status,
413
497
  "tokenId": token_id,
@@ -421,7 +505,7 @@ class AgentClient(BaseAgentClient): # Bybit client
421
505
 
422
506
  def get_orders_done(self, begin_time: int, end_time: int, status: int, side: int, token_id: str):
423
507
  return self._post(
424
- "/fiat/otc/order/simplifyList",
508
+ "/x-api/fiat/otc/order/simplifyList",
425
509
  {
426
510
  "status": status, # 50 - завершено
427
511
  "tokenId": token_id,
@@ -434,21 +518,24 @@ class AgentClient(BaseAgentClient): # Bybit client
434
518
  )
435
519
 
436
520
  async def create_order(self, order: OrderFull) -> models.Order:
437
- ad = Ad(**self.api.get_ad_details(itemId=order.itemId)["result"])
521
+ # ad = Ad(**self.api.get_ad_details(itemId=order.itemId)["result"])
438
522
  await sleep(1)
439
523
  curex = await models.CurEx.get_or_none(ex=self.ex_client.ex, exid=order.currencyId).prefetch_related("cur")
440
524
  cur_scale = (curex.scale if curex.scale is not None else curex.cur.scale) if curex else 2
441
525
  coinex = await models.CoinEx.get(ex=self.ex_client.ex, exid=order.tokenId).prefetch_related("coin")
442
526
  coin_scale = coinex.scale if coinex.scale is not None else coinex.cur.scale
443
- maker_name = order.buyerRealName, order.sellerRealName
527
+ maker_name = order.sellerRealName, order.buyerRealName
444
528
  im_maker = int(order.makerUserId == order.userId)
445
529
  taker_id = (order.userId, order.targetUserId)[im_maker]
446
- taker_person = await self.person_upsert(maker_name[::-1][ad.side], taker_id)
530
+ taker_person = await self.ex_client.person_name_update(maker_name[::-1][order.side], taker_id)
447
531
  seller_person = (
448
- self.actor.person if order.side else await self.person_upsert(order.sellerRealName, int(order.targetUserId))
532
+ self.actor.person
533
+ if order.side
534
+ else await self.ex_client.person_name_update(order.sellerRealName, int(order.targetUserId))
449
535
  )
450
536
  taker_nick = (self.actor.name, order.targetNickName)[im_maker] # todo: check
451
- ad_db, cond_isnew = await self.cond_upsert(ad, maker_name[ad.side], force=True)
537
+ # ad_db, cond_isnew = await self.ex_client.cond_load(ad, force=True, rname=maker_name[order.side])
538
+ ad_db = await models.Ad.get(exid=order.itemId)
452
539
  if not ad_db:
453
540
  ...
454
541
  ecredex: CredEpyd = order.confirmedPayTerm
@@ -458,22 +545,22 @@ class AgentClient(BaseAgentClient): # Bybit client
458
545
  if ecredex.paymentType:
459
546
  if ecredex.paymentType == 51:
460
547
  ecredex.accountNo = ecredex.accountNo.replace("p", "P").replace("р", "P").replace("Р", "P")
461
- if not re.match(r"^([Pp])\d{7,10}$", ecredex.accountNo):
462
- msgs = self.api.get_chat_messages(orderId=order.id, size=100)["result"]["result"]
463
- msgs = [m["message"] for m in msgs if m["roleType"] == "user" and m["userId"] == order.targetUserId]
464
- msgs = [g.group() for m in msgs if (g := re.match(r"([PpРр])\d{7,10}\b", m))]
465
- crd = await models.Cred.get_or_none(
466
- detail=ecredex.accountNo, credexs__exid=ecredex.id, credexs__ex=self.ex_client.ex
467
- )
468
- if not msgs and re.match(r"^\d{7,10}$", ecredex.accountNo):
469
- ecredex.accountNo = "P" + ecredex.accountNo
470
- elif msgs:
471
- ecredex.accountNo = msgs[-1]
472
- else:
473
- ...
474
- if crd:
475
- crd.detail = ecredex.accountNo
476
- await crd.save(update_fields=["detail"])
548
+ # if not re.match(r"^([Pp])\d{7,10}$", ecredex.accountNo):
549
+ # msgs = self.api.get_chat_messages(orderId=order.id, size=100)["result"]["result"]
550
+ # msgs = [m["message"] for m in msgs if m["roleType"] == "user" and m["userId"] == order.targetUserId]
551
+ # msgs = [g.group() for m in msgs if (g := re.match(r"([PpРр])\d{7,10}\b", m))]
552
+ # crd = await models.Cred.get_or_none(
553
+ # detail=ecredex.accountNo, credexs__exid=ecredex.id, credexs__ex=self.ex_client.ex
554
+ # )
555
+ # if not msgs and re.match(r"^\d{7,10}$", ecredex.accountNo):
556
+ # ecredex.accountNo = "P" + ecredex.accountNo
557
+ # elif msgs:
558
+ # ecredex.accountNo = msgs[-1]
559
+ # else:
560
+ # ...
561
+ # if crd:
562
+ # crd.detail = ecredex.accountNo
563
+ # await crd.save(update_fields=["detail"])
477
564
  if not (credex := await models.CredEx.get_or_none(exid=ecredex.id, ex=self.ex_client.ex)):
478
565
  # cur_id = await Cur.get(ticker=ad.currencyId).values_list('id', flat=True)
479
566
  # await self.cred_epyd2db(ecredex, ad_db.maker.person_id, cur_id)
@@ -481,7 +568,7 @@ class AgentClient(BaseAgentClient): # Bybit client
481
568
  await PmCur.filter(
482
569
  pm__pmexs__ex=self.ex_client.ex,
483
570
  pm__pmexs__exid=ecredex.paymentType,
484
- cur__ticker=ad.currencyId,
571
+ cur__ticker=order.currencyId,
485
572
  ).count()
486
573
  != 1
487
574
  ):
@@ -490,7 +577,7 @@ class AgentClient(BaseAgentClient): # Bybit client
490
577
  pmcur := await PmCur.get_or_none(
491
578
  pm__pmexs__ex=self.ex_client.ex,
492
579
  pm__pmexs__exid=ecredex.paymentType,
493
- cur__ticker=ad.currencyId,
580
+ cur__ticker=order.currencyId,
494
581
  )
495
582
  ):
496
583
  ...
@@ -545,13 +632,13 @@ class AgentClient(BaseAgentClient): # Bybit client
545
632
  to = ((odb.payed_at or odb.created_at) + timedelta(minutes=180 + 30)).isoformat(sep=" ").split("+")[0]
546
633
  tsa = [
547
634
  t
548
- for tid, t in self.hist.items()
635
+ for tid, t in (self.hist.items() if self.hist else [])
549
636
  if (ecredex.accountNo == t["to"] and t["from"] != "@merchant" and frm < t["date"] < to)
550
637
  ]
551
638
  buyer_person = (
552
639
  self.actor.person
553
640
  if not order.side
554
- else await self.person_upsert(order.buyerRealName, int(order.targetUserId))
641
+ else await self.ex_client.person_name_update(order.buyerRealName, int(order.targetUserId))
555
642
  )
556
643
  ts = [t for t in tsa if floor(fa := float(order.amount)) <= float(t["creditedAmount"]) <= round(fa)]
557
644
  if len(ts) != 1:
@@ -670,7 +757,8 @@ class AgentClient(BaseAgentClient): # Bybit client
670
757
  # cad: Ad = ads[place] if cur_plc > place else ads[cur_plc]
671
758
  # переделал пока на жесткую установку целевого места, даже если текущее выше:
672
759
  if len(ads) <= target_place:
673
- logging.error(f"target place {target_place} not found in ads list {ads}")
760
+ logging.error(f"target place {target_place} not found in ads {len(ads)}-lenght list")
761
+ target_place = len(ads) - 1
674
762
  cad: Ad = ads[target_place]
675
763
  # а цена обгоняемой объявы не выше нашего потолка?
676
764
  if (float(cad.price) - ceil) * k <= 0:
@@ -689,10 +777,14 @@ class AgentClient(BaseAgentClient): # Bybit client
689
777
  # if round(cpc * new_premium / cpm, 2) == m
690
778
  # mad.premium = new_premium.to_eng_string()
691
779
 
692
- async def racing(
693
- self,
694
- race: models.Race,
695
- ):
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):
696
788
  coinex: models.CoinEx = await models.CoinEx.get(
697
789
  coin_id=race.road.ad.pair_side.pair.coin_id, ex=self.actor.ex
698
790
  ).prefetch_related("coin")
@@ -700,11 +792,13 @@ class AgentClient(BaseAgentClient): # Bybit client
700
792
  cur_id=race.road.ad.pair_side.pair.cur_id, ex=self.actor.ex
701
793
  ).prefetch_related("cur")
702
794
  taker_side: bool = not race.road.ad.pair_side.is_sell
703
- pm_ids = [pm.id for pm in race.road.ad.pms]
704
- pmexs: list[models.PmEx] = await models.PmEx.filter(pm_id__in=pm_ids, ex=self.actor.ex).prefetch_related("pm")
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
+
705
800
  k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
706
801
  sleep_sec = 3 # 1 if set(pms) & {"volet"} and coinex.coin_id == 1 else 5
707
- creds: list[models.CredEx] = await self.get_credexs_by_pms(race.road.ad.pms, curex.cur_id)
708
802
  _lstat, volume = None, 0
709
803
 
710
804
  while self.actor.person.user.status > 0:
@@ -715,8 +809,9 @@ class AgentClient(BaseAgentClient): # Bybit client
715
809
  continue
716
810
  # если гонка дольше Х минут не обновлялась, обновляем ее (и ее пары) потолок
717
811
  expiration = datetime.now(timezone.utc) - timedelta(minutes=15)
812
+ amt = race.filter_amount * 10**-curex.cur.scale if race.filter_amount else None
718
813
  if race.updated_at < expiration:
719
- ceils, hp, vmf, zplace = await self.get_ceils(coinex, curex, pmexs, 0.001, True)
814
+ ceils, hp, vmf, zplace = await self.get_ceils(coinex, curex, pmexs, 0.003, False, 0, amt, post_pmexs)
720
815
  race.ceil = int(ceils[int(taker_side)] * 10**curex.scale)
721
816
  await race.save()
722
817
  # upd pair race
@@ -725,34 +820,28 @@ class AgentClient(BaseAgentClient): # Bybit client
725
820
  road__ad__pair_side__is_sell=taker_side,
726
821
  road__ad__maker=self.actor,
727
822
  updated_at__lt=expiration,
728
- road__ad__pms__id__in=pm_ids,
729
- pms_count=len(pm_ids),
823
+ road__credexs__id__in=[c.id for c in race.road.credexs],
824
+ pms_count=len(pmexs),
730
825
  ):
731
826
  prace.ceil = int(ceils[int(not taker_side)] * 10**curex.scale)
732
827
  await prace.save()
733
828
 
734
829
  last_vol = volume
735
830
  if taker_side: # гонка в стакане продажи - мы покупаем монету за ФИАТ
736
- fiat = max(await models.Fiat.filter(cred_id__in=[c.cred_id for c in creds]), key=lambda x: x.amount)
737
- volume = fiat.amount / race.road.ad.price
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)
738
833
  else: # гонка в стакане покупки - мы продаем МОНЕТУ за фиат
739
834
  asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
740
835
  volume = asset.free * 10**-coinex.scale
741
836
  volume = str(round(volume, coinex.scale))
742
-
743
837
  try:
744
- ads: list[Ad] = await self.ads(coinex, curex, taker_side, pmexs)
838
+ ads: list[Ad] = await self.ads(coinex, curex, taker_side, pmexs, amt, 50, race.vm_filter, post_pmexs)
745
839
  except Exception:
746
840
  await sleep(1)
747
- ads: list[Ad] = await self.ads(coinex, curex, taker_side, pmexs)
841
+ ads: list[Ad] = await self.ads(coinex, curex, taker_side, pmexs, amt, 50, race.vm_filter, post_pmexs)
748
842
 
749
- if race.vm_filter:
750
- ads = [ad for ad in ads if "VA" in ad.authTag]
751
843
  self.overprice_filter(ads, race.ceil * 10**-curex.scale, k) # обрезаем сверху все ads дороже нашего потолка
752
844
 
753
- if 571 in pm_ids and coinex.coin.ticker == "USDT" and not taker_side:
754
- ...
755
-
756
845
  if not ads:
757
846
  print(coinex.exid, curex.exid, taker_side, "no ads!")
758
847
  await sleep(15)
@@ -763,7 +852,7 @@ class AgentClient(BaseAgentClient): # Bybit client
763
852
  await sleep(15)
764
853
  continue
765
854
  (cur_plc,) = cur_plc # может упасть если в списке > 1 наш ad
766
- [(await self.cond_upsert(ad, ps=race.road.ad.pair_side, force=True))[0] for ad in ads[:cur_plc]]
855
+ [(await self.ex_client.cond_load(ad, race.road.ad.pair_side, True))[0] for ad in ads[:cur_plc]]
767
856
  # rivals = [
768
857
  # (await models.RaceStat.update_or_create({"place": plc, "price": ad.price, "premium": ad.premium}, ad=ad))[
769
858
  # 0
@@ -784,7 +873,7 @@ class AgentClient(BaseAgentClient): # Bybit client
784
873
  continue
785
874
  if not (cad := self.get_cad(ads, race.ceil * 10**-curex.scale, k, race.target_place, cur_plc)):
786
875
  continue
787
- new_price = round(float(cad.price) - k * step(mad, cad, curex.cur.scale), curex.cur.scale)
876
+ new_price = round(float(cad.price) - k * step(mad, cad, curex.scale), curex.scale)
788
877
  if (
789
878
  float(mad.price) == new_price and volume == last_vol
790
879
  ): # Если место уже нужное или нужная цена и так уже стоит
@@ -796,23 +885,23 @@ class AgentClient(BaseAgentClient): # Bybit client
796
885
  await sleep(sleep_sec)
797
886
  continue
798
887
  if cad.priceType: # Если цена конкурента плавающая, то повышаем себе не цену, а %
799
- new_premium = float(cad.premium) - k * step(mad, cad, 2)
800
- if float(mad.premium) == new_premium: # Если нужный % и так уже стоит
801
- if mad.priceType and cur_plc != race.target_place:
802
- new_premium -= k * step(mad, cad, 2)
803
- elif volume == last_vol:
804
- print(end="v" if taker_side else "^", flush=True)
805
- await sleep(sleep_sec)
806
- continue
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
807
896
  mad.premium = str(round(new_premium, 2))
808
897
  mad.priceType = cad.priceType
809
898
  mad.quantity = volume
810
- mad.maxAmount = str(2_000_000)
899
+ mad.maxAmount = str(2_000_000 if curex.cur_id == 1 else 40_000)
811
900
  req = AdUpdateRequest.model_validate(
812
901
  {
813
902
  **mad.model_dump(),
814
903
  "price": str(round(new_price, curex.scale)),
815
- "paymentIds": [str(p.exid) for p in creds],
904
+ "paymentIds": [str(cx.exid) for cx in race.road.credexs],
816
905
  }
817
906
  )
818
907
  try:
@@ -839,18 +928,23 @@ class AgentClient(BaseAgentClient): # Bybit client
839
928
  req.quantity = str(round(asset.free * 10**-coinex.scale, coinex.scale))
840
929
  _res = self.ad_upd(req)
841
930
  elif ExcCode(e.status_code) == ExcCode.RareLimit:
842
- sad = [
843
- ma
844
- for ma in self.my_ads(False)
845
- if (
846
- ma.currencyId == curex.exid
847
- and ma.tokenId == coinex.exid
848
- and taker_side == (not ma.side)
849
- and ma.payments == [pe.exid for pe in pmexs]
850
- )
851
- ][0]
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
852
946
  self.ad_del(ad_id=int(mad.id))
853
- req.id = sad.id
947
+ req.id = sads[0].id
854
948
  req.actionType = "ACTIVE"
855
949
  self.api.update_ad(**req.model_dump())
856
950
  logging.warning(f"Ad#{mad.id} recreated")
@@ -863,10 +957,15 @@ class AgentClient(BaseAgentClient): # Bybit client
863
957
  await sleep(6)
864
958
 
865
959
  async def get_books(
866
- self, coinex: models.CoinEx, curex: models.CurEx, pmexs: list[models.PmEx]
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,
867
966
  ) -> tuple[list[Ad], list[Ad]]:
868
- buy: list[Ad] = await self.ads(coinex, curex, False, pmexs, None, 30)
869
- sell: list[Ad] = await self.ads(coinex, curex, True, pmexs, None, 30)
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)
870
969
  return buy, sell
871
970
 
872
971
  async def get_spread(
@@ -876,12 +975,12 @@ class AgentClient(BaseAgentClient): # Bybit client
876
975
  ...
877
976
  buy_price, sell_price = float(bb[place].price), float(sb[place].price)
878
977
  half_spread = (buy_price - sell_price) / (buy_price + sell_price)
879
- if half_spread * 2 < perc:
880
- if not exact:
881
- if vmf is None: # сначала фильтруем только VA
882
- return await self.get_spread(bb, sb, perc, True, place)
883
- # если даже по VA не хватает спреда - увеличиваем место
884
- return await self.get_spread(bb, sb, perc, vmf, place + 1)
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)
885
984
 
886
985
  return (buy_price, sell_price), half_spread, vmf, place
887
986
 
@@ -893,307 +992,161 @@ class AgentClient(BaseAgentClient): # Bybit client
893
992
  min_prof=0.02,
894
993
  vmf: bool = False,
895
994
  place: int = 0,
995
+ amount: int = None,
996
+ post_pmexs: set[models.PmEx] = None,
896
997
  ) -> tuple[tuple[float, float], float, bool, int]: # todo: refact to Pairex
897
- bb, sb = await self.get_books(coinex, curex, pmexs)
998
+ bb, sb = await self.get_books(coinex, curex, pmexs, amount, post_pmexs)
898
999
  if vmf:
1000
+ # ориентируемся на цены объявлений только проверенных мерчантов
899
1001
  bb = [b for b in bb if "VA" in b.authTag]
900
1002
  sb = [s for s in sb if "VA" in s.authTag]
901
- perc = pmexs[0].pm.fee * 0.0001 + min_prof
1003
+ perc = list(post_pmexs or pmexs)[0].pm.fee * 0.0001 + min_prof
902
1004
  (bf, sf), hp, vmf, zplace = await self.get_spread(bb, sb, perc, vmf, place)
903
1005
  mdl = (bf + sf) / 2
904
1006
  bc, sc = mdl + mdl * (perc / 2), mdl - mdl * (perc / 2)
905
1007
  return (bc, sc), hp, vmf, zplace
906
1008
 
907
- async def parse_ads(
908
- self,
909
- coinex: models.CoinEx,
910
- curex: models.CurEx,
911
- taker_side: bool,
912
- pms: list[str] = None,
913
- ceil: float = None,
914
- volume: float = 9000,
915
- min_fiat: int = None,
916
- max_fiat: int = None,
917
- ):
918
- k = (-1) ** int(taker_side) # on_buy=1, on_sell=-1
919
-
920
- if pms:
921
- creds: dict[models.PmEx, models.CredEx] = await self.get_credexs_by_norms(pms, curex.cur_id)
922
- [str(p.exid) for p in creds.values()]
923
-
924
- if taker_side: # гонка в стакане продажи - мы покупаем монету за ФИАТ
925
- fiats = await models.Fiat.filter(
926
- cred_id__in=[cx.cred_id for cx in creds.values()], amount__not=F("target")
927
- )
928
- volume = min(volume, max(fiats, key=lambda f: f.target - f.amount).amount / ceil)
929
- else: # гонка в стакане покупки - мы продаем МОНЕТУ за фиат
930
- asset = await models.Asset.get(addr__actor=self.actor, addr__coin_id=coinex.coin_id)
931
- volume = min(volume, asset.free)
932
- volume = str(round(volume, coinex.coin.scale))
933
- ps = await PairSide.get(
934
- is_sell=taker_side,
935
- pair__coin_id=coinex.coin_id,
936
- pair__cur_id=curex.cur_id,
1009
+ async def take_ad(self, req: TakeAdReq):
1010
+ if req.price and req.is_sell and req.cur_:
1011
+ ... # todo call the get_ad_details() only if lack of data
1012
+ # res = self.api.get_ad_details(itemId=req.ad_id)["result"]
1013
+ # ad: Ad = Ad.model_validate(res)
1014
+ # pmexs = await models.PmEx.filter(ex_id=self.actor.ex_id, pm_id=req.pm_id)
1015
+ # if len(pmexs) > 1:
1016
+ # pmexs = [p for p in pmexs if p.exid in ad.payments]
1017
+ #
1018
+ # # todo: map pm->cred_pattern
1019
+ # pmexid = exids.pop() if (exids := set(ad.payments) & set(px.exid for px in pmexs)) else "40"
1020
+ pmexid = str(req.pm_id)
1021
+ coinex = await models.CoinEx.get(coin_id=req.coin_id, ex=self.ex_client.ex)
1022
+ curex = await models.CurEx.get(cur_id=req.cur_id, ex=self.ex_client.ex)
1023
+
1024
+ # if ad.side: # продажа, я (тейкер) покупатель
1025
+ # pmexs = await models.PmEx.filter(ex_id=self.actor.ex_id, pm_id=req.pm_id)
1026
+ # if len(pmexs) > 1:
1027
+ # pmexs = [p for p in pmexs if p.name.endswith(f" ({ad.currencyId})")]
1028
+ # else:
1029
+ # pmexs = await models.CredEx.filter(
1030
+ # ex_id=self.actor.ex_id, cred__person_id=self.actor.person_id,
1031
+ # cred__pmcur__pm_id=req.pm_id, cred__pmcur__cur__ticker=ad.currencyId
1032
+ # )
1033
+ # req.pm_id = pmexs[0].exid
1034
+ # req.quantity = round(req.amount / float(ad.price) - 0.00005, 4) # todo: to get the scale from coinEx
1035
+
1036
+ bor = BaseOrderReq(
1037
+ ad_id=str(req.ad_id),
1038
+ fiat_amount=req.amount,
1039
+ is_sell=req.is_sell,
1040
+ cur_exid=curex.exid,
1041
+ coin_exid=coinex.exid,
1042
+ coin_scale=coinex.scale,
1043
+ pmex_exid=pmexid,
937
1044
  )
938
- while self.actor.person.user.status > 0: # todo: depends on rest asset/fiat
939
- ads: list[Ad] = await self.ads(coinex, curex, taker_side, pms and list(creds.keys()))
940
-
941
- if not ads:
942
- print(coinex.exid, curex.exid, taker_side, "no ads!")
943
- await sleep(300)
944
- continue
945
-
946
- for i, ad in enumerate(ads):
947
- if (ceil - float(ad.price)) * k < 0:
948
- break
949
- if int(ad.userId) == self.actor.exid:
950
- logging.info(f"My ad {'-' if taker_side else '+'}{coinex.exid}/{curex.exid} on place#{i}")
951
- continue
952
- ad_db, isnew = await self.cond_upsert(ad, ps=ps)
953
- if isnew:
954
- s = f"{'-' if taker_side else '+'}{ad.price}[{ad.minAmount}-{ad.maxAmount}]{coinex.exid}/{curex.exid}"
955
- print(s, end=" | ", flush=True)
956
- try:
957
- # take
958
- ...
959
- except FailedRequestError as e:
960
- if ExcCode(e.status_code) == ExcCode.RareLimit:
961
- await sleep(195)
962
- elif ExcCode(e.status_code) == ExcCode.Timestamp:
963
- await sleep(2)
964
- else:
965
- raise e
966
- except (ReadTimeoutError, ConnectionDoesNotExistError):
967
- logging.warning("Connection failed. Restarting..")
968
- await sleep(3)
969
-
970
- async def cond_upsert(
971
- self, ad: Ad, rname: str = None, ps: PairSide = None, force: bool = False
972
- ) -> tuple[models.Ad, bool]:
973
- _sim, cid = None, None
974
- ad_db = await models.Ad.get_or_none(exid=ad.id, maker__ex=self.ex_client.ex).prefetch_related("cond")
975
- # если точно такое условие уже есть в бд
976
- if not (cleaned := clean(ad.remark)) or (cid := {oc[0]: ci for ci, oc in self.all_conds.items()}.get(cleaned)):
977
- # и объява с таким ид уже есть, но у нее другое условие
978
- if ad_db and ad_db.cond_id != cid:
979
- # то обновляем ид ее условия
980
- ad_db.cond_id = cid
981
- await ad_db.save()
982
- logging.info(f"{ad.nickName} upd cond#{ad_db.cond_id}->{cid}")
983
- # old_cid = ad_db.cond_id # todo: solve race-condition, а пока что очищаем при каждом запуске
984
- # if not len((old_cond := await Cond.get(id=old_cid).prefetch_related('ads')).ads):
985
- # await old_cond.delete()
986
- # logging.warning(f"Cond#{old_cid} deleted!")
987
- return (ad_db or force and await self.ad_create(ad, cid, rname, ps)), False
988
- # если эта объява в таким ид уже есть в бд, но с другим условием (или без), а текущего условия еще нет в бд
989
- if ad_db:
990
- await ad_db.fetch_related("cond__ads", "maker")
991
- if not ad_db.cond_id or (
992
- # у измененного условия этой объявы есть другие объявы?
993
- (rest_ads := set(ad_db.cond.ads) - {ad_db})
994
- and
995
- # другие объявы этого условия принадлежат другим юзерам
996
- {ra.maker_id for ra in rest_ads} - {ad_db.maker_id}
997
- ):
998
- # создадим новое условие и присвоим его только текущей объяве
999
- cid = await self.cond_new(cleaned, {int(ad.userId)})
1000
- ad_db.cond_id = cid
1001
- await ad_db.save()
1002
-
1003
- return ad_db, True
1004
- # а если других объяв со старым условием этой обявы нет, либо они все этого же юзера
1005
- # обновляем условие (в тч во всех ЕГО объявах)
1006
- ad_db.cond.last_ver = ad_db.cond.raw_txt
1007
- ad_db.cond.raw_txt = cleaned
1008
- await ad_db.cond.save()
1009
- await self.cond_upd(ad_db.cond, {ad_db.maker.exid})
1010
- # и подправим коэфициенты похожести нового текста
1011
- await self.fix_rel_sims(ad_db.cond_id, cleaned)
1012
- return ad_db, False
1013
-
1014
- cid = await self.cond_new(cleaned, {int(ad.userId)})
1015
- return await self.ad_create(ad, cid, rname, ps), True
1016
-
1017
- async def cond_new(self, txt: str, uids: set[int]) -> int:
1018
- new_cond, _ = await Cond.update_or_create(raw_txt=txt)
1019
- # и максимально похожую связь для нового условия (если есть >= 60%)
1020
- await self.cond_upd(new_cond, uids)
1021
- return new_cond.id
1022
-
1023
- async def cond_upd(self, cond: Cond, uids: set[int]):
1024
- self.all_conds[cond.id] = cond.raw_txt, uids
1025
- # и максимально похожую связь для нового условия (если есть >= 60%)
1026
- old_cid, sim = await self.cond_get_max_sim(cond.id, cond.raw_txt, uids)
1027
- await self.actual_sim(cond.id, old_cid, sim)
1028
-
1029
- def find_in_tree(self, cid: int, old_cid: int) -> bool:
1030
- if p := self.cond_sims.get(old_cid):
1031
- if p == cid:
1032
- return True
1033
- return self.find_in_tree(cid, p)
1034
- return False
1035
-
1036
- async def cond_get_max_sim(self, cid: int, txt: str, uids: set[int]) -> tuple[int | None, int | None]:
1037
- # находим все старые тексты похожие на 90% и более
1038
- if len(txt) < 15:
1039
- return None, None
1040
- sims: dict[int, int] = {}
1041
- for old_cid, (old_txt, old_uids) in self.all_conds.items():
1042
- if len(old_txt) < 15 or uids == old_uids:
1043
- continue
1044
- elif not self.can_add_sim(cid, old_cid):
1045
- continue
1046
- if sim := get_sim(txt, old_txt):
1047
- sims[old_cid] = sim
1048
- # если есть, берем самый похожий из них
1049
- if sims:
1050
- old_cid, sim = max(sims.items(), key=lambda x: x[1])
1051
- await sleep(0.3)
1052
- return old_cid, sim
1053
- return None, None
1054
-
1055
- def can_add_sim(self, cid: int, old_cid: int) -> bool:
1056
- if cid == old_cid:
1057
- return False
1058
- elif self.cond_sims.get(cid) == old_cid:
1059
- return False
1060
- elif self.find_in_tree(cid, old_cid):
1061
- return False
1062
- elif self.cond_sims.get(old_cid) == cid:
1063
- return False
1064
- elif cid in self.rcond_sims.get(old_cid, {}):
1065
- return False
1066
- elif old_cid in self.rcond_sims.get(cid, {}):
1067
- return False
1068
- return True
1069
-
1070
- async def person_upsert(self, name: str, exid: int) -> models.Person:
1071
- if actor := await models.Actor.get_or_none(exid=exid, ex=self.ex_client.ex).prefetch_related("person"):
1072
- if not actor.person:
1073
- actor.person = await models.Person.create(name=name)
1074
- await actor.save()
1075
- return actor.person
1076
- return await models.Person.create(name=name)
1077
-
1078
- async def ad_create(
1079
- self, ad: Ad, cid: int = None, rname: str = None, ps: PairSide = None, actor: Actor = None
1080
- ) -> models.Ad:
1081
- act_df = {}
1082
- if int(ad.userId) != self.actor.exid:
1083
- act_df |= {"name": ad.nickName}
1084
- if rname:
1085
- act_df |= {"person": await self.person_upsert(rname, int(ad.userId))}
1086
- if not actor:
1087
- act_df |= {"person": await self.person_upsert(ad.nickName, int(ad.userId))}
1088
- actor, _ = await Actor.update_or_create(act_df, exid=ad.userId, ex=self.ex_client.ex)
1089
- ps = ps or await PairSide.get_or_none(
1090
- is_sell=ad.side,
1091
- pair__coin__ticker=ad.tokenId,
1092
- pair__cur__ticker=ad.currencyId,
1093
- ).prefetch_related("pair__cur", "pair__coin")
1094
- if not ps or not ps.pair:
1095
- ... # THB/USDC
1096
- ad_upd = models.Ad.validate(ad.model_dump(by_alias=True))
1097
- cur_scale = 10**ps.pair.cur.scale
1098
- coinex = await models.CoinEx.get(coin_id=ps.pair.coin_id, ex=self.ex_client.ex)
1099
- df_unq = ad_upd.df_unq(
1100
- maker_id=actor.id,
1101
- pair_side_id=ps.id,
1102
- amount=int(float(ad.quantity) * float(ad.price) * cur_scale),
1103
- quantity=int(float(ad.quantity) * 10**coinex.scale),
1104
- min_fiat=int(float(ad.minAmount) * cur_scale),
1105
- max_fiat=ad.maxAmount and int(float(ad.maxAmount) * cur_scale),
1106
- price=int(float(ad.price) * cur_scale),
1107
- premium=int(float(ad.premium) * cur_scale),
1108
- )
1109
- ad_db, _ = await models.Ad.update_or_create(**df_unq)
1110
- await ad_db.pms.add(*(await models.Pm.filter(pmexs__ex=self.ex_client.ex, pmexs__exid__in=ad.payments)))
1111
- return ad_db
1112
-
1113
- async def fix_rel_sims(self, cid: int, new_txt: str):
1114
- for rel_sim in await CondSim.filter(cond_rel_id=cid).prefetch_related("cond"):
1115
- if sim := get_sim(new_txt, rel_sim.cond.raw_txt):
1116
- rel_sim.similarity = sim
1117
- await rel_sim.save()
1118
- else:
1119
- await rel_sim.delete()
1120
-
1121
- async def actual_cond(self):
1122
- for curr, old in await CondSim.all().values_list("cond_id", "cond_rel_id"):
1123
- self.cond_sims[curr] = old
1124
- self.rcond_sims[old] |= {curr}
1125
- for cid, (txt, uids) in self.all_conds.items():
1126
- old_cid, sim = await self.cond_get_max_sim(cid, txt, uids)
1127
- await self.actual_sim(cid, old_cid, sim)
1128
- # хз бля чо это ваще
1129
- # for ad_db in await models.Ad.filter(direction__pairex__ex=self.ex_client.ex).prefetch_related("cond", "maker"):
1130
- # ad = Ad(id=str(ad_db.exid), userId=str(ad_db.maker.exid), remark=ad_db.cond.raw_txt)
1131
- # await self.cond_upsert(ad, force=True)
1132
-
1133
- async def actual_sim(self, cid: int, old_cid: int, sim: int):
1134
- if not sim:
1135
- return
1136
- if old_sim := await CondSim.get_or_none(cond_id=cid):
1137
- if old_sim.cond_rel_id != old_cid:
1138
- if sim > old_sim.similarity:
1139
- logging.warning(f"R {cid}: {old_sim.similarity}->{sim} ({old_sim.cond_rel_id}->{old_cid})")
1140
- await old_sim.update_from_dict({"similarity": sim, "old_rel_id": old_cid}).save()
1141
- self._cond_sim_upd(cid, old_cid)
1142
- elif sim != old_sim.similarity:
1143
- logging.info(f"{cid}: {old_sim.similarity}->{sim}")
1144
- await old_sim.update_from_dict({"similarity": sim}).save()
1145
- else:
1146
- await CondSim.create(cond_id=cid, cond_rel_id=old_cid, similarity=sim)
1147
- self._cond_sim_upd(cid, old_cid)
1148
-
1149
- def _cond_sim_upd(self, cid: int, old_cid: int):
1150
- if old_old_cid := self.cond_sims.get(cid): # если старый cid уже был в дереве:
1151
- self.rcond_sims[old_old_cid].remove(cid) # удаляем из обратного
1152
- self.cond_sims[cid] = old_cid # а в прямом он автоматом переопределится, даже если и был
1153
- self.rcond_sims[old_cid] |= {cid} # ну и в обратное добавим новый
1154
-
1155
- async def get_credexs_by_pms(self, pms: list[models.Pm], cur_id: int) -> list[models.CredEx]:
1156
- return await models.CredEx.filter(
1157
- ex_id=self.actor.ex_id,
1158
- cred__pmcur__pm_id__in=[pm.id for pm in pms],
1159
- cred__person_id=self.actor.person_id,
1160
- cred__pmcur__cur_id=cur_id,
1161
- )
1162
-
1163
- def build_tree(self):
1164
- set(self.cond_sims.keys()) | set(self.cond_sims.values())
1165
- tree = defaultdict(dict)
1166
- # Группируем родителей по детям
1167
- for child, par in self.cond_sims.items():
1168
- tree[par] |= {child: {}} # todo: make from self.rcond_sim
1169
-
1170
- # Строим дерево снизу вверх
1171
- def subtree(node):
1172
- if not node:
1173
- return node
1174
- for key in node:
1175
- subnode = tree.pop(key, {})
1176
- d = subtree(subnode)
1177
- node[key] |= d # actual tree rebuilding here!
1178
- return node # todo: refact?
1179
-
1180
- # Находим корни / без родителей
1181
- roots = set(self.cond_sims.values()) - set(self.cond_sims.keys())
1182
- for root in roots:
1183
- _ = subtree(tree[root])
1045
+ resp: OrderResp = await self._order_request(bor)
1046
+ return resp
1047
+
1048
+ async def watch_payeer(self, mcs: dict[int, "AgentClient"]):
1049
+ coinex: models.CoinEx = await models.CoinEx.get(coin_id=1, ex=self.actor.ex).prefetch_related("coin")
1050
+ curex: models.CurEx = await models.CurEx.get(cur_id=1, ex=self.actor.ex).prefetch_related("cur")
1051
+ post_pmexs = set(await models.PmEx.filter(pm_id=366, ex=self.actor.ex).prefetch_related("pm"))
1052
+ i = 0
1053
+ while True:
1054
+ try:
1055
+ ss = await self.ads(coinex, curex, True, None, None, 1000, False, post_pmexs)
1056
+ ss = [s for s in ss if float(s.price) > 90.42 or int(s.userId) in mcs.keys()]
1057
+ if ss:
1058
+ ad: Ad = ss[0]
1059
+ await self.bbot.send(
1060
+ 193017646,
1061
+ f"price: {ad.price}\nnick: {ad.nickName}\nprice: {ad.price}"
1062
+ f"\nqty: {ad.quantity} [{ad.minAmount}-{ad.maxAmount}]",
1063
+ )
1064
+ am = min(float(ad.maxAmount), max(1000 + i, float(ad.minAmount)))
1065
+ req = TakeAdReq(
1066
+ ad_id=ad.id,
1067
+ amount=am,
1068
+ pm_id=14,
1069
+ is_sell=True,
1070
+ coin_id=1,
1071
+ cur_id=1,
1072
+ )
1073
+ ord_resp: OrderResp = await self.take_ad(req)
1074
+ # order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
1075
+ order: OrderFull = await self.get_order_info(ord_resp.orderId)
1076
+ odb = await self.create_order(order)
1077
+ # t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
1078
+ # await t.fetch_related("order__cred__pmcur__cur")
1079
+ # res = await self.pm_clients[366].check_in(t)
1080
+ if int(ad.userId) in mcs:
1081
+ mcs[int(ad.userId)].api.mark_as_paid(
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
+ ...
1184
1088
 
1185
- self.tree = tree
1089
+ bs = await self.ads(coinex, curex, False, None, None, 1000, False, post_pmexs)
1090
+ bs = [b for b in bs if float(b.price) < 89.56 or int(b.userId) in mcs.keys()]
1091
+ if bs:
1092
+ ad: Ad = bs[0]
1093
+ await self.bbot.send(
1094
+ 193017646,
1095
+ f"price: {ad.price}\nnick: {ad.nickName}\nprice: {ad.price}"
1096
+ f"\nqty: {ad.quantity} [{ad.minAmount}-{ad.maxAmount}]",
1097
+ )
1098
+ am = min(float(ad.maxAmount), max(600 + i, float(ad.minAmount)))
1099
+ req = TakeAdReq(
1100
+ ad_id=ad.id,
1101
+ amount=am,
1102
+ pm_id=14,
1103
+ is_sell=False,
1104
+ coin_id=1,
1105
+ cur_id=1,
1106
+ )
1107
+ ord_resp: OrderResp = await self.take_ad(req)
1108
+ # order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
1109
+ order: OrderFull = await self.get_order_info(ord_resp.orderId)
1110
+ odb = await self.create_order(order)
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].send(t)
1114
+ self.api.mark_as_paid(
1115
+ orderId=str(odb.exid),
1116
+ paymentType=ad.payments[0], # pmex.exid
1117
+ paymentId=order.paymentTermList[0].id, # credex.exid
1118
+ )
1119
+ if int(ad.userId) in mcs:
1120
+ mcs[int(ad.userId)].api.release_assets(orderId=order.id)
1121
+ await sleep(1)
1122
+ ...
1123
+ except Exception as e:
1124
+ logging.exception(e)
1125
+ await sleep(90)
1126
+ except HttpProcessingError as e:
1127
+ logging.error(e)
1128
+ print(end=".", flush=True)
1129
+ i += 1
1130
+ await sleep(5)
1131
+
1132
+ async def boost_acc(self):
1133
+ await sleep(45)
1134
+ for i in range(10):
1135
+ am = 500 + i
1136
+ req = TakeAdReq(ad_id="1856989782009487360", amount=am, pm_id=366)
1137
+ ord_resp: OrderResp = await self.take_ad(req)
1138
+ order: OrderFull = OrderFull(**self.api.get_order_details(orderId=ord_resp.orderId)["result"])
1139
+ odb = await self.create_order(order)
1140
+ t = await models.Transfer(order=odb, amount=odb.amount, updated_at=now())
1141
+ await t.fetch_related("order__cred__pmcur__cur")
1142
+ await self.pm_clients[366].send(t)
1143
+ ...
1186
1144
 
1187
1145
 
1188
1146
  def ms2utc(msk_ts_str: str):
1189
1147
  return datetime.fromtimestamp(int(msk_ts_str) / 1000, timezone(timedelta(hours=3), name="MSK"))
1190
1148
 
1191
1149
 
1192
- def get_sim(s1, s2) -> int:
1193
- sim = int((SequenceMatcher(None, s1, s2).ratio() - 0.6) * 10_000)
1194
- return sim if sim > 0 else 0
1195
-
1196
-
1197
1150
  def detailed_diff(str1, str2):
1198
1151
  matcher = SequenceMatcher(None, str1, str2)
1199
1152
  result = []
@@ -1211,14 +1164,6 @@ def detailed_diff(str1, str2):
1211
1164
  return "".join(result)
1212
1165
 
1213
1166
 
1214
- def clean(s) -> str:
1215
- clear = r"[^\w\s.,!?;:()\-]"
1216
- repeat = r"(.)\1{2,}"
1217
- s = re.sub(clear, "", s).lower()
1218
- s = re.sub(repeat, r"\1", s)
1219
- return s.replace("\n\n", "\n").replace(" ", " ").strip(" \n/.,!?-")
1220
-
1221
-
1222
1167
  def step_is_need(mad, cad) -> bool:
1223
1168
  # todo: пока не решен непонятный кейс, почему то конкурент по всем параметрам слабже, но в списке ранжируется выше.
1224
1169
  # текущая версия: recentExecuteRate округляется до целого, но на бэке байбита его дробная часть больше
@@ -1243,37 +1188,75 @@ class ExcCode(IntEnum):
1243
1188
  Timestamp = 10002
1244
1189
  IP = 10010
1245
1190
  Quantity = 912300019
1191
+ PayMethod = 912300013
1246
1192
  Unknown = 912300014
1247
1193
 
1248
1194
 
1249
- async def main():
1250
- logging.basicConfig(level=logging.INFO)
1251
- _ = await init_db(TORM)
1195
+ @post_save(models.Race)
1196
+ async def race_upserted(
1197
+ _cls: type[models.Race], race: models.Race, created: bool, _db: BaseDBAsyncClient, _updated: list[str]
1198
+ ):
1199
+ logging.warning(f"Race {race.id} is now upserted")
1200
+ asyncio.all_tasks()
1201
+ if created:
1202
+ ...
1203
+ else: # параметры гонки изменены
1204
+ ...
1252
1205
 
1253
- @post_save(models.Race)
1254
- async def race_upserted(
1255
- _cls: type[models.Race], race: models.Race, created: bool, _db: BaseDBAsyncClient, _updated: list[str]
1256
- ):
1257
- logging.warning(f"Race {race.id} is now upserted")
1258
- asyncio.all_tasks()
1259
- if created:
1260
- ...
1261
- else: # параметры гонки изменены
1262
- ...
1263
1206
 
1264
- actor = (
1265
- await models.Actor.filter(ex_id=4, agent__isnull=False).prefetch_related("ex", "agent", "person__user").first()
1207
+ async def main():
1208
+ logging.basicConfig(level=logging.INFO)
1209
+ cn = await init_db(TORM)
1210
+
1211
+ agent = (
1212
+ await models.Agent.filter(actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, id=8)
1213
+ .prefetch_related(
1214
+ "actor__ex",
1215
+ "actor__person__user__gmail",
1216
+ "actor__my_ads__my_ad__race",
1217
+ "actor__my_ads__pair_side__pair__cur",
1218
+ "actor__my_ads__pms",
1219
+ )
1220
+ .first()
1266
1221
  )
1267
1222
  filebot = FileClient(NET_TOKEN)
1268
- await filebot.start()
1223
+ # await filebot.start()
1269
1224
  # b.add_handler(MessageHandler(cond_start_handler, command("cond")))
1270
- cl: AgentClient = actor.client(filebot)
1225
+ ex = await models.Ex.get(name="Bybit")
1226
+ ecl: ExClient = ex.client(filebot)
1227
+ abot = XyncBot(PAY_TOKEN, cn)
1228
+ cl: AgentClient = agent.client(ecl, filebot, abot)
1229
+
1230
+ # req = TakeAdReq(ad_id=1955696985964089344, amount=504, pm_id=128)
1231
+ # await cl.take_ad(req)
1232
+
1233
+ # await cl.actual_cond()
1234
+ # cl.get_api_orders(), # 10, 1738357200000, 1742504399999
1271
1235
 
1272
1236
  # await cl.ex_client.set_pairs()
1273
1237
  # await cl.ex_client.set_pms()
1238
+
1274
1239
  # await cl.set_creds()
1275
1240
  # await cl.export_my_ads()
1276
1241
 
1242
+ ms = await models.Agent.filter(
1243
+ actor__ex_id=4, auth__isnull=False, status__gt=AgentStatus.off, actor__person__user__id__in=[2]
1244
+ ).prefetch_related(
1245
+ "actor__ex",
1246
+ "actor__person__user__gmail",
1247
+ "actor__my_ads__my_ad__race",
1248
+ "actor__my_ads__pair_side__pair__cur",
1249
+ "actor__my_ads__pms",
1250
+ )
1251
+ mcs = {m.actor.exid: m.client(ecl, filebot, abot) for m in ms}
1252
+
1253
+ await gather(
1254
+ create_task(cl.start(True)),
1255
+ create_task(cl.watch_payeer(mcs)),
1256
+ )
1257
+ # ensure_future(cl.start(True))
1258
+ # await cl.boost_acc()
1259
+
1277
1260
  # создание гонок по мои активным объявам:
1278
1261
  # for ma in cl.my_ads():
1279
1262
  # my_ad = await models.MyAd.get(ad__exid=ma.id).prefetch_related('ad__pms', 'ad__pair_side__pair')
@@ -1286,28 +1269,6 @@ async def main():
1286
1269
  # s, _ = await models.Synonym.update_or_create(typ=SynonymType.name, txt=name)
1287
1270
  # await s.curs.add(rub.cur)
1288
1271
 
1289
- # пока порешали рейс-кондишн, очищаем сиротские условия при каждом запуске
1290
- # [await c.delete() for c in await Cond.filter(ads__isnull=True)]
1291
- cl.all_conds = {
1292
- c.id: (c.raw_txt, {a.maker.exid for a in c.ads}) for c in await Cond.all().prefetch_related("ads__maker")
1293
- }
1294
- for curr, old in await CondSim.filter().values_list("cond_id", "cond_rel_id"):
1295
- cl.cond_sims[curr] = old
1296
- cl.rcond_sims[old] |= {curr}
1297
-
1298
- cl.build_tree()
1299
- a = set()
1300
-
1301
- def check_tree(tre):
1302
- for p, c in tre.items():
1303
- a.add(p)
1304
- check_tree(c)
1305
-
1306
- for pr, ch in cl.tree.items():
1307
- check_tree(ch)
1308
- if ct := set(cl.tree.keys()) & a:
1309
- logging.exception(f"cycle cids: {ct}")
1310
-
1311
1272
  pauth = (await models.PmAgent[1]).auth
1312
1273
  papi = PayeerAPI(pauth["email"], pauth["api_id"], pauth["api_sec"])
1313
1274
  hist: dict = papi.history(count=1000)
@@ -1320,32 +1281,6 @@ async def main():
1320
1281
  # )
1321
1282
  # await cl.get_api_orders() # 43, 1741294800000, 1749157199999)
1322
1283
 
1323
- races = await models.Race.filter(started=True).prefetch_related(
1324
- "road__ad__pair_side__pair__cur",
1325
- "road__ad__pms",
1326
- )
1327
- tasks = [asyncio.create_task(cl.racing(race), name=f"Rc{race.id}") for race in races]
1328
- # await cl.actual_cond()
1329
- try:
1330
- await gather(
1331
- *tasks
1332
- # cl.get_api_orders(), # 10, 1738357200000, 1742504399999
1333
- )
1334
- except Exception as e:
1335
- await filebot.send("🤬Bybit agent CRASHED!!!🤬", actor.person.user.username_id)
1336
- await filebot.send(e.__repr__(), actor.person.user.username_id)
1337
- raise e
1338
- # bor = BaseOrderReq(
1339
- # ad_id="1861440060199632896",
1340
- # # asset_amount=40,
1341
- # fiat_amount=3000,
1342
- # amount_is_fiat=True,
1343
- # is_sell=False,
1344
- # cur_exid=rub.exid,
1345
- # coin_exid=usdt.exid,
1346
- # coin_scale=usdt.coin.scale,
1347
- # )
1348
- # res: OrderResp = await cl.order_request(bor)
1349
1284
  # await cl.cancel_order(res.orderId)
1350
1285
  await filebot.stop()
1351
1286
  await cl.close()