xync-client 0.0.141__py3-none-any.whl → 0.0.156.dev18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. xync_client/Abc/AdLoader.py +5 -0
  2. xync_client/Abc/Agent.py +354 -8
  3. xync_client/Abc/Ex.py +432 -25
  4. xync_client/Abc/HasAbotUid.py +10 -0
  5. xync_client/Abc/InAgent.py +0 -11
  6. xync_client/Abc/PmAgent.py +34 -26
  7. xync_client/Abc/xtype.py +57 -3
  8. xync_client/Bybit/InAgent.py +233 -409
  9. xync_client/Bybit/agent.py +844 -777
  10. xync_client/Bybit/etype/__init__.py +0 -0
  11. xync_client/Bybit/etype/ad.py +54 -86
  12. xync_client/Bybit/etype/cred.py +29 -9
  13. xync_client/Bybit/etype/order.py +75 -103
  14. xync_client/Bybit/ex.py +35 -48
  15. xync_client/Gmail/__init__.py +119 -98
  16. xync_client/Htx/agent.py +213 -40
  17. xync_client/Htx/etype/ad.py +40 -16
  18. xync_client/Htx/etype/order.py +194 -0
  19. xync_client/Htx/ex.py +17 -19
  20. xync_client/Mexc/agent.py +268 -0
  21. xync_client/Mexc/api.py +1255 -0
  22. xync_client/Mexc/etype/ad.py +52 -1
  23. xync_client/Mexc/etype/order.py +354 -0
  24. xync_client/Mexc/ex.py +34 -22
  25. xync_client/Okx/1.py +14 -0
  26. xync_client/Okx/agent.py +39 -0
  27. xync_client/Okx/ex.py +8 -8
  28. xync_client/Pms/Payeer/agent.py +396 -0
  29. xync_client/Pms/Payeer/login.py +1 -59
  30. xync_client/Pms/Payeer/trade.py +58 -0
  31. xync_client/Pms/Volet/__init__.py +82 -63
  32. xync_client/Pms/Volet/api.py +5 -4
  33. xync_client/loader.py +2 -0
  34. xync_client/pm_unifier.py +1 -1
  35. {xync_client-0.0.141.dist-info → xync_client-0.0.156.dev18.dist-info}/METADATA +5 -1
  36. {xync_client-0.0.141.dist-info → xync_client-0.0.156.dev18.dist-info}/RECORD +38 -29
  37. xync_client/Pms/Payeer/__init__.py +0 -253
  38. xync_client/Pms/Payeer/api.py +0 -25
  39. {xync_client-0.0.141.dist-info → xync_client-0.0.156.dev18.dist-info}/WHEEL +0 -0
  40. {xync_client-0.0.141.dist-info → xync_client-0.0.156.dev18.dist-info}/top_level.txt +0 -0
@@ -1,48 +1,72 @@
1
1
  from typing import Literal
2
2
 
3
- from pydantic import BaseModel
3
+ from pydantic import BaseModel, RootModel
4
4
  from xync_schema.xtype import BaseAd
5
5
 
6
+ from xync_client.Abc.xtype import BaseAdUpdate
7
+
6
8
 
7
9
  class TradeRule(BaseModel):
8
10
  title: str
9
- titleValue: str
11
+ titleValue: str | None = None
10
12
  content: str
11
13
  inputType: int
12
14
  inputValue: str
13
15
  hint: str
14
- contentCode: str
16
+ contentCode: Literal["PAY", "MERCHANT"]
17
+ contentType: int | None = None
15
18
  sort: int
16
19
 
17
20
 
18
- class Req(BaseModel):
21
+ TradeRulesV2 = RootModel[list[TradeRule]]
22
+
23
+
24
+ class AdsUpd(BaseAdUpdate):
19
25
  tradeType: int
20
26
  coinId: int
21
27
  currency: int
22
28
  minTradeLimit: float
23
29
  maxTradeLimit: float
24
30
  tradeCount: float
25
- password: str
31
+ password: str = ""
26
32
  payTerm: int
27
33
  isFixed: Literal["off", "on"]
28
- premium: int
34
+ premium: float
29
35
  isAutoReply: Literal["off", "on"]
30
36
  takerAcceptOrder: int
31
37
  isPayCode: Literal["off", "on"]
32
- isVerifyCapital: bool
33
- receiveAccounts: int
38
+ verifyCapitalStatus: str = None # todo: check if int
39
+ receiveAccounts: str
34
40
  deviation: int
35
41
  isTakerLimit: Literal["off", "on"]
42
+ takerRealLevel: Literal["off", "on"]
43
+ takerIsPhoneBind: Literal["off", "on"]
44
+ takerIsMerchant: Literal["off", "on"]
45
+ takerIsPayment: Literal["off", "on"]
36
46
  blockType: int
37
47
  session: int
38
48
  chargeType: bool
39
49
  apiVersion: int
40
50
  channel: str
41
- tradeRulesV2: list[TradeRule]
51
+ tradeRulesV2: str # TradeRulesV2
42
52
  securityToken: str | None = ""
43
- fixedPrice: str | None = ""
53
+ fixedPrice: float | None = None
44
54
  autoReplyContent: str | None = ""
45
- tradeRule: str | None = ""
55
+
56
+
57
+ class AdsReq(BaseModel):
58
+ coinId: int
59
+ currency: int
60
+ tradeType: Literal["sell", "buy"]
61
+ payMethod: str
62
+ currPage: int = 1
63
+ acceptOrder: int = 0
64
+ blockType: Literal["general"] = "general"
65
+ online: int = 1
66
+ range: int = 0
67
+ amount: str = "" # float
68
+ onlyTradable: str = "false" # bool
69
+ isFollowed: str = "false" # bool
46
70
 
47
71
 
48
72
  class PayMethod(BaseModel):
@@ -69,21 +93,21 @@ class Resp(BaseAd):
69
93
  isOnline: bool
70
94
  isTrade: bool
71
95
  isVerifyCapital: bool
72
- maxTradeLimit: str
96
+ maxTradeLimit: float
73
97
  merchantLevel: int
74
- minTradeLimit: str
98
+ minTradeLimit: float
75
99
  orderCompleteRate: str
76
100
  payMethod: str
77
101
  payMethods: list[PayMethod]
78
- payName: str # list[PayName] # приходит массив объектов внутри строки
102
+ payName: str # list[PayName] # приходит массив объектов внутри строки
79
103
  payTerm: int
80
- price: str
104
+ price: float
81
105
  takerAcceptAmount: str
82
106
  takerAcceptOrder: int
83
107
  takerLimit: int
84
108
  thumbUp: int
85
109
  totalTradeOrderCount: int
86
- tradeCount: str
110
+ tradeCount: float
87
111
  tradeMonthTimes: int
88
112
  tradeType: int
89
113
  uid: int
@@ -0,0 +1,194 @@
1
+ from decimal import Decimal
2
+ from typing import Literal
3
+
4
+ from pydantic import BaseModel, NonNegativeInt, Field, PositiveInt
5
+
6
+
7
+ class C2COrder(BaseModel):
8
+ cancelCountDown: NonNegativeInt
9
+ consultCancelCountDown: NonNegativeInt
10
+
11
+
12
+ class OrderItem(BaseModel):
13
+ orderId: int
14
+ counterpartUid: PositiveInt
15
+ counterpartNickName: str
16
+ counterpartIsOnline: bool
17
+ counterpartOrderCount: NonNegativeInt
18
+ counterpartLastTradeTime: NonNegativeInt # timestamp in ms
19
+ counterpartMerchantLevel: NonNegativeInt
20
+ counterpartThumbUp: NonNegativeInt
21
+ counterpartRocketAmount: NonNegativeInt | None
22
+ counterpartPrimeLevel: str = Field(pattern=r"^Prime\d+$")
23
+ counterpartRecentWithdrawTime: PositiveInt | None # timestamp in ms
24
+ side: Literal[0, 1] # 0=buy, 1=sell
25
+ tradeMode: Literal[1, 2] # предполагаемые значения
26
+ runMode: Literal[1, 2] # предполагаемые значения
27
+ quoteAssetName: str = Field(min_length=1, max_length=10)
28
+ cryptoAssetName: str = Field(min_length=1, max_length=10)
29
+ amount: Decimal = Field(gt=0, decimal_places=2)
30
+ quantity: Decimal = Field(gt=0)
31
+ quote: Decimal = Field(gt=0, decimal_places=2)
32
+ orderStatus: NonNegativeInt
33
+ c2cOrder: C2COrder = None
34
+ inNegotiation: bool
35
+
36
+
37
+ class ModelField(BaseModel):
38
+ fieldId: str
39
+ name: str
40
+ fieldType: Literal["payee", "pay_account", "bank", "sub_bank", "qr_code"]
41
+ index: PositiveInt
42
+ maxLength: int | None = None
43
+ required: bool | None = None
44
+ copyable: bool
45
+ remindWord: str | None = None
46
+ valueType: str | None = None
47
+ value: str
48
+ nameList: list | None = None
49
+ remindWordList: list | None = None
50
+
51
+
52
+ class PaymentMethod(BaseModel):
53
+ id: PositiveInt
54
+ userName: str = Field(min_length=1)
55
+ bankType: PositiveInt
56
+ bankNumber: str
57
+ bankName: str | None = None
58
+ bankAddress: str | None = None
59
+ qrCode: str | None = None
60
+ color: str = Field(pattern=r"^#[0-9A-F]{6}$")
61
+ payMethodName: str = Field(min_length=1)
62
+ paymentStatus: Literal[1]
63
+ modelFieldsList: list[ModelField]
64
+
65
+
66
+ class OrderInfo(BaseModel):
67
+ orderId: PositiveInt
68
+ orderNo: PositiveInt
69
+ uid: PositiveInt
70
+ nickName: str = Field(min_length=1)
71
+ realName: str | None = None
72
+ roleName: Literal["maker", "taker"]
73
+ side: Literal[0, 1]
74
+ runMode: Literal[1]
75
+ tradeMode: Literal[1]
76
+ liquidDivision: Literal[3]
77
+ sideName: Literal["buy", "sell"]
78
+ quoteAssetId: PositiveInt
79
+ quoteAssetType: Literal[1]
80
+ quoteAssetName: str = Field(min_length=1, max_length=10)
81
+ quoteAssetSymbol: str
82
+ cryptoAssetId: PositiveInt
83
+ cryptoAssetType: Literal[2]
84
+ cryptoAssetName: str = Field(min_length=1, max_length=10)
85
+ cryptoAssetSymbol: str
86
+ amount: Decimal = Field(gt=0, decimal_places=2)
87
+ quantity: Decimal = Field(gt=0)
88
+ quote: Decimal = Field(gt=0, decimal_places=2)
89
+ orderStatus: NonNegativeInt
90
+ gmtCreate: PositiveInt
91
+ gmtModified: PositiveInt
92
+ areaType: Literal[1]
93
+ appealCountDown: NonNegativeInt
94
+
95
+
96
+ class OtherInfo(BaseModel):
97
+ uid: PositiveInt
98
+ nickName: str = Field(min_length=1)
99
+ realName: str | None = None
100
+ gmtCreate: PositiveInt
101
+ merchantLevel: NonNegativeInt
102
+ realTradeCountBuy: NonNegativeInt
103
+ realTradeCountSell: NonNegativeInt
104
+ registerTime: PositiveInt
105
+ isPhoneBind: bool
106
+ marginAssetId: NonNegativeInt
107
+ marginAssetName: str | None = None
108
+ marginAmount: NonNegativeInt
109
+ appealMonthTimes: NonNegativeInt
110
+ appealMonthWinTimes: NonNegativeInt
111
+ isOnline: bool
112
+ isSeniorAuth: bool
113
+ orderCompleteRate: Decimal = Field(ge=0, le=100, decimal_places=2)
114
+ tradeMonthCount: NonNegativeInt
115
+ tradeCount: NonNegativeInt
116
+ releaseTime: NonNegativeInt
117
+ buyCompleteRate: Decimal = Field(ge=0, le=100, decimal_places=2)
118
+ buyCancelTimeAvg: Decimal = Field(ge=0, decimal_places=2)
119
+ thumbUp: NonNegativeInt
120
+ merchantTags: str | None = None
121
+ totalUserTradeCount: NonNegativeInt
122
+ totalTradeOrderCount: NonNegativeInt
123
+ totalTradeOrderCancelCount: NonNegativeInt
124
+ orderBuyCompleteRate: Decimal = Field(ge=0, le=100, decimal_places=2)
125
+ orderSellCompleteRate: Decimal = Field(ge=0, le=100, decimal_places=2)
126
+ totalOrderCompleteRate: Decimal = Field(ge=0, le=100, decimal_places=2)
127
+ counterpartRocketAmount: int | None = None
128
+ counterpartPrimeLevel: str = Field(pattern=r"^Prime\d+$")
129
+ counterpartRecentWithdrawTime: PositiveInt | None = None
130
+ counterpartOrderCount: NonNegativeInt
131
+ counterpartLastTradeTime: NonNegativeInt
132
+
133
+
134
+ class Fee(BaseModel):
135
+ feeType: Literal[1]
136
+ feeStatus: Literal[1, 2]
137
+ fee: Decimal = Field(ge=0)
138
+ feeName: str = Field(min_length=1)
139
+
140
+
141
+ class FeeInfo(BaseModel):
142
+ totalFee: Decimal = Field(ge=0)
143
+ feeList: list[Fee]
144
+
145
+
146
+ class OrderTag(BaseModel):
147
+ isSoonLock: Literal[1, 2]
148
+ isPremature: Literal[1, 2]
149
+ isAppeal: Literal[1, 2]
150
+ specialCancelFlag: Literal[1, 2]
151
+ isPhone: Literal[1, 2]
152
+ now: PositiveInt
153
+ isAppealPremature: Literal[1, 2]
154
+ isFollowed: bool
155
+ isShield: bool
156
+ negotiationStatus: int | None = None
157
+
158
+
159
+ class C2COrderDetail(BaseModel):
160
+ matchPayId: int | None = None
161
+ payTerm: PositiveInt
162
+ payCode: str | None = None
163
+ quote: Decimal = Field(gt=0, decimal_places=2)
164
+ amount: Decimal = Field(gt=0, decimal_places=2)
165
+ quantity: Decimal = Field(gt=0)
166
+ quoteAssetName: str = Field(min_length=1)
167
+ cryptoAssetName: str = Field(min_length=1)
168
+ buyPayAccount: PositiveInt | None
169
+ gmtPay: PositiveInt | None
170
+ gmtResetCancel: PositiveInt | None = None
171
+ orderStatus: NonNegativeInt
172
+ cancelCountDown: NonNegativeInt
173
+ consultCancelCountDown: NonNegativeInt
174
+ waitCompletedCountDown: NonNegativeInt
175
+ areaType: Literal[1]
176
+ acceptStatus: Literal[0, 1]
177
+ appCountDown: NonNegativeInt
178
+ appMaxCountDown: NonNegativeInt
179
+
180
+
181
+ class OrderSnapshot(BaseModel):
182
+ tradeInstructionStatus: Literal["INIT", "COMPLETED"] # добавь другие статусы
183
+
184
+
185
+ class OrderFull(BaseModel):
186
+ orderInfo: OrderInfo
187
+ otherInfo: OtherInfo
188
+ paymentMethod: list[PaymentMethod]
189
+ feeInfo: FeeInfo
190
+ orderTag: OrderTag
191
+ c2cOrder: C2COrderDetail
192
+ inNegotiation: bool
193
+ takerEvaluateStatus: Literal[0, 1]
194
+ orderSnapshot: OrderSnapshot
xync_client/Htx/ex.py CHANGED
@@ -9,7 +9,7 @@ from xync_schema.models import Ex, Cur
9
9
  from xync_schema.enums import PmType
10
10
 
11
11
  from xync_client.Abc.Ex import BaseExClient
12
- from xync_client.Abc.xtype import PmEx, MapOfIdsList
12
+ from xync_client.Abc.xtype import PmEx, MapOfIdsList, GetAds
13
13
  from xync_client.Htx.etype import pm, Country, ad
14
14
  from xync_client.loader import NET_TOKEN
15
15
  from xync_client.loader import TORM
@@ -42,24 +42,22 @@ class ExClient(BaseExClient):
42
42
  pair[rcurs[cur["name"]]] += [coen]
43
43
  return tuple(pairs.values())
44
44
 
45
- async def ads(
46
- self, coin_exid: int, cur_exid: int, is_sell: bool, pm_exids: list[str] = None, amount: int = None
47
- ) -> list[xtype.BaseAd]:
48
- params = {
49
- "coinId": coin_exid,
50
- "currency": cur_exid,
51
- "tradeType": "sell" if is_sell else "buy",
52
- "currPage": 1,
53
- "payMethod": ",".join(pm_exids) if pm_exids else 0,
54
- "acceptOrder": 0,
55
- "blockType": "general",
56
- "online": 1,
57
- "range": 0,
58
- "amount": amount or "",
59
- "onlyTradable": "false",
60
- "isFollowed": "false",
61
- }
62
- res = (await self._get("/-/x/otc/v1/data/trade-market", params))["data"]
45
+ async def x2e_req_ads(self, xreq: GetAds) -> ad.AdsReq:
46
+ coin_id, _ = await self.x2e_coin(xreq.coin_id)
47
+ cur_id, _, __ = await self.x2e_cur(xreq.cur_id)
48
+ ereq = ad.AdsReq(
49
+ coinId=int(coin_id),
50
+ currency=int(cur_id),
51
+ tradeType="sell" if xreq.is_sell else "buy",
52
+ payMethod=",".join([await self.x2e_pm(pm_id) for pm_id in xreq.pm_ids]),
53
+ )
54
+ if xreq.amount:
55
+ ereq.amount = str(xreq.amount)
56
+ # todo: all kwargs
57
+ return ereq
58
+
59
+ async def _ads(self, req: ad.AdsReq, lim: int = None, vm_filter: bool = False, **kwargs) -> list[ad.Resp]:
60
+ res = (await self._get("/-/x/otc/v1/data/trade-market", req.model_dump()))["data"]
63
61
  ads = [ad.Resp(**a) for a in res]
64
62
  return ads
65
63
 
@@ -0,0 +1,268 @@
1
+ import json
2
+ import logging
3
+ from asyncio import run, sleep, create_task
4
+ from hashlib import md5
5
+ from urllib.parse import quote
6
+ from uuid import uuid4
7
+
8
+ import websockets
9
+ from blackboxprotobuf import protobuf_to_json
10
+ from pyro_client.client.file import FileClient
11
+ from xync_bot import XyncBot
12
+
13
+ from xync_client.Mexc.api import MEXCP2PApiClient
14
+ from xync_client.Mexc.etype import ad
15
+
16
+ from xync_client.Abc.xtype import GetAds, AdUpd
17
+ from xync_client.Bybit.etype.order import TakeAdReq
18
+ from xync_client.Mexc.etype.order import OrderDetail
19
+
20
+ from xync_client.loader import PAY_TOKEN, NET_TOKEN
21
+ from xync_schema import models
22
+ from xync_schema.enums import UserStatus, AgentStatus
23
+
24
+ from xync_client.Abc.Agent import BaseAgentClient
25
+
26
+
27
+ class AgentClient(BaseAgentClient):
28
+ i: int = 5
29
+ headers = {
30
+ # "Accept-Encoding": "gzip, deflate, br, zstd",
31
+ "Accept-Language": "ru,en;q=0.9",
32
+ "Language:": "ru-RU",
33
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
34
+ }
35
+ api: MEXCP2PApiClient
36
+
37
+ @staticmethod
38
+ async def _heartbeat(ws):
39
+ """Фоновая задача для PING/PONG"""
40
+ while True:
41
+ await sleep(25)
42
+ await ws.send('{"method":"PING"}')
43
+
44
+ async def _ws_key(self):
45
+ self.i = 13 if self.i > 9999 else self.i + 1
46
+ hdrs = self.agent.auth["headers"] | {
47
+ "trochilus-trace-id": "c7831459-3bb2-4aa7-bf5d-9ff2adef0c08-0062",
48
+ "ucenter-token": self.agent.auth["cookies"]["u_id"],
49
+ }
50
+ # resp = get("https://www.mexc.com/ucenter/api/ws_token", headers=hdrs, cookies=self.agent.auth['cookies'])
51
+ resp = await self._get("/ucenter/api/ws_token", hdrs=hdrs)
52
+ self.wkk = resp["data"]["wsToken"]
53
+
54
+ async def ws_prv(self):
55
+ await self._ws_key()
56
+ url = f"wss://wbs.mexc.com/ws?wsToken={self.wkk}"
57
+ async with websockets.connect(url) as ws:
58
+ create_task(self._heartbeat(ws))
59
+ await ws.send('{"method":"SUBSCRIPTION","params":["otc@private.p2p.orders.pb"],"id":12}')
60
+ await ws.send('{"method":"SUBSCRIPTION","params":["common@private.risk.result.pb"],"id":11}')
61
+ while resp := await ws.recv():
62
+ try:
63
+ data: dict = json.loads(resp)
64
+ except UnicodeDecodeError:
65
+ msg, typedef = protobuf_to_json(resp)
66
+ data = json.loads(msg)
67
+ await self.recv(data)
68
+ if data.get("msg") == "PONG":
69
+ print(end="p")
70
+ else:
71
+ logging.warning(data)
72
+
73
+ async def recv(self, data: dict):
74
+ if data["1"] == "otc@private.p2p.orders.pb":
75
+ o = data["218"]["1"]
76
+ order: OrderDetail = (await self.api.get_order_detail(o["1"])).data
77
+ if order.side == "SELL":
78
+ if order.state == "NOT_PAID":
79
+ ...
80
+ elif order.side == "BUY":
81
+ if order.state == "PAID":
82
+ ...
83
+ ...
84
+
85
+ async def _take_ad(self, req: TakeAdReq):
86
+ self.i = 33 if self.i > 9998 else self.i + 2
87
+ hdrs = self.headers | {"trochilus-trace-id": f"{uuid4()}-{self.i:04d}"}
88
+ auth = {
89
+ "p0": self.actor.agent.auth["p0"],
90
+ "k0": self.actor.agent.auth["k0"],
91
+ "chash": self.actor.agent.auth["chash"],
92
+ "mtoken": self.actor.agent.auth["deviceId"],
93
+ "mhash": md5(self.actor.agent.auth["deviceId"].encode()).hexdigest(),
94
+ }
95
+ data = {
96
+ "scene": "TRADE_BUY",
97
+ "quantity": req.quantity,
98
+ "amount": req.amount,
99
+ "orderId": req.ad_id,
100
+ "authVersion": "v2",
101
+ "deviceId": auth["mtoken"],
102
+ }
103
+ res = await self._post("/api/platform/p2p/api/verify/second_auth/risk/scene", json=data, hdrs=hdrs)
104
+ data = {
105
+ "amount": req.amount,
106
+ "authVersion": "v2",
107
+ "orderId": req.ad_id,
108
+ "price": req.price,
109
+ "ts": int(1761155700.8372989 * 1000),
110
+ "userConfirmPaymentId" if req.is_sell else "userConfirmPayMethodId": req.pm_id,
111
+ }
112
+ self.i = 33 if self.i > 9999 else self.i + 1
113
+ hdrs = self.headers | {"trochilus-trace-id": f"{uuid4()}-{self.i:04d}"}
114
+ res = await self._post("/api/platform/p2p/api/order/deal?mhash=" + auth["mhash"], data=auth | data, hdrs=hdrs)
115
+ return res["data"]
116
+
117
+ async def x2e_req_ad_upd(self, xreq: AdUpd) -> ad.AdUpd:
118
+ coin_id, coin_scale = await self.ex_client.x2e_coin(xreq.coin_id)
119
+ cur_id, cur_scale, minimum = await self.ex_client.x2e_cur(xreq.cur_id)
120
+ ereq = ad.AdUpd(
121
+ id=xreq.id,
122
+ price=round(xreq.price, cur_scale),
123
+ coinId=coin_id,
124
+ currency=cur_id,
125
+ tradeType="SELL" if xreq.is_sell else "BUY",
126
+ deviceId=self.agent.auth["deviceId"],
127
+ payment=",".join([str(cdx.exid) for cdx in xreq.credexs]),
128
+ priceType=0,
129
+ minTradeLimit=minimum,
130
+ maxTradeLimit=round(xreq.max_amount or xreq.amount - 10**-cur_scale, cur_scale),
131
+ quantity=round(xreq.quantity or xreq.amount / xreq.price, coin_scale),
132
+ )
133
+ if xreq.cond:
134
+ ereq.autoResponse = quote(xreq.cond)
135
+ # todo: all kwargs
136
+ return ereq
137
+
138
+ async def _ad_upd(self, req: ad.AdUpd):
139
+ self.i = 33 if self.i > 9999 else self.i + 1
140
+ hdrs = self.headers | {"trochilus-trace-id": f"{uuid4()}-{self.i:04d}"}
141
+ res = await self._put("/api/platform/p2p/api/merchant/order", form_data=req.model_dump(), hdrs=hdrs)
142
+ return res["code"]
143
+
144
+
145
+ async def main():
146
+ from x_model import init_db
147
+ from xync_client.loader import TORM
148
+
149
+ cn = await init_db(TORM, True)
150
+
151
+ ex = await models.Ex[12]
152
+ agent = (
153
+ await models.Agent.filter(
154
+ actor__ex=ex,
155
+ status__gte=AgentStatus.race,
156
+ auth__isnull=False,
157
+ actor__person__user__status=UserStatus.ACTIVE,
158
+ actor__person__user__pm_agents__isnull=False,
159
+ )
160
+ .prefetch_related("actor__ex", "actor__person__user__gmail")
161
+ .first()
162
+ )
163
+ bbot = XyncBot(PAY_TOKEN, cn)
164
+ fbot = FileClient(NET_TOKEN)
165
+ ecl = ex.client(fbot)
166
+ cl: AgentClient = agent.client(ecl, fbot, bbot)
167
+ cl.api = MEXCP2PApiClient(agent.auth["key"], agent.auth["sec"])
168
+ create_task(cl.ws_prv())
169
+
170
+ while True:
171
+ bceil = 106
172
+ sceil = 124.98
173
+ breq = GetAds(coin_id=1, cur_id=1, is_sell=False, pm_ids=[366])
174
+ sreq = GetAds(coin_id=1, cur_id=1, is_sell=True, pm_ids=[366])
175
+ breq_upd = AdUpd(
176
+ id="a1574183931501582340", price=87, **{**breq.model_dump(), "amount": 11000.01}, max_amount=4370
177
+ ) # + 1 cent
178
+ sreq_upd = AdUpd(id="a1594624084590445568", price=150, **{**sreq.model_dump(), "amount": 30000.01})
179
+
180
+ await sleep(5)
181
+ bads: list[ad.Ad] = await cl.ex_client.ads(breq)
182
+ if bads[0].price >= sceil:
183
+ bad: ad.Ad = bads.pop(0)
184
+ await cl.bbot.send(
185
+ 193017646,
186
+ f"price: {bad.price}\nnick: {bad.merchant.nickName}\nmax:{bad.maxPayLimit}"
187
+ f"\nqty: {bad.availableQuantity} [{bad.minTradeLimit}-{bad.maxTradeLimit}]",
188
+ )
189
+ # am = min(bad.maxTradeLimit, max(10000.0, bad.minTradeLimit))
190
+ # req = TakeAdReq(
191
+ # ad_id=bad.exid,
192
+ # amount=am,
193
+ # pm_id=366,
194
+ # is_sell=False,
195
+ # coin_id=1,
196
+ # cur_id=1,
197
+ # )
198
+ # ord_resp: OrderResp = await cl.take_ad(req)
199
+
200
+ bads = [
201
+ a
202
+ for a in bads
203
+ if a.price <= bceil and a.availableQuantity > 20 and (a.maxTradeLimit - a.minTradeLimit > 1000)
204
+ ]
205
+ if len(bads) > 1:
206
+ if bads[0].merchant.nickName == cl.actor.name:
207
+ if round(bads[0].price - bads[1].price, 2) > 0.01:
208
+ breq_upd.price = bads[1].price + 0.01
209
+ if _ := await cl.ad_upd(breq_upd):
210
+ await sleep(5, print(_, flush=True))
211
+ print(end="!", flush=True)
212
+ elif bads[0].price != (trgt_price := bads[0].price + 0.01):
213
+ breq_upd.price = trgt_price
214
+ if _ := await cl.ad_upd(breq_upd):
215
+ await sleep(5, print(_, flush=True))
216
+ print(end="!", flush=True)
217
+
218
+ await sleep(5)
219
+ sads: list[ad.Ad] = await cl.ex_client.ads(sreq)
220
+ if sads[0].price <= bceil:
221
+ sad: ad.Ad = sads.pop(0)
222
+ await cl.bbot.send(
223
+ 193017646,
224
+ f"price: {sad.price}\nnick: {sad.merchant.nickName}\nmax:{sad.maxPayLimit}"
225
+ f"\nqty: {sad.availableQuantity} [{sad.minTradeLimit}-{sad.maxTradeLimit}]",
226
+ )
227
+ # am = min(sad.maxTradeLimit, max(10000.0, sad.minTradeLimit))
228
+ # req = TakeAdReq(
229
+ # ad_id=sad.exid,
230
+ # amount=am,
231
+ # pm_id=366,
232
+ # is_sell=False,
233
+ # coin_id=1,
234
+ # cur_id=1,
235
+ # )
236
+ # ord_resp: OrderResp = await cl.take_ad(req)
237
+
238
+ sads = [
239
+ a
240
+ for a in sads
241
+ if a.price >= sceil and a.availableQuantity > 20 and (a.maxTradeLimit - a.minTradeLimit > 1000)
242
+ ]
243
+ if len(sads) > 1:
244
+ if sads[0].merchant.nickName == cl.actor.name:
245
+ if round(sads[1].price - sads[0].price, 2) > 0.01:
246
+ sreq_upd.price = sads[1].price - 0.01
247
+ if _ := await cl.ad_upd(sreq_upd):
248
+ await sleep(15, print(_, flush=True))
249
+ continue
250
+ print(end="!", flush=True)
251
+ continue
252
+ elif sads[0].price > sceil:
253
+ sreq_upd.price = sads[0].price - 0.01
254
+ if _ := await cl.ad_upd(sreq_upd):
255
+ await sleep(15, print(_, flush=True))
256
+ continue
257
+ print(end="!", flush=True)
258
+ continue
259
+
260
+ print(end=".", flush=True)
261
+
262
+ req = TakeAdReq(ad_id="a1574088909645125632", amount=500, pm_id=366, cur_id=1, price=85.8, is_sell=True)
263
+ res = await cl.take_ad(req)
264
+ print(res)
265
+
266
+
267
+ if __name__ == "__main__":
268
+ run(main())