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.
- xync_client/Abc/AdLoader.py +299 -0
- xync_client/Abc/Agent.py +94 -10
- xync_client/Abc/Ex.py +27 -22
- xync_client/Abc/HasAbotUid.py +10 -0
- xync_client/Abc/InAgent.py +0 -11
- xync_client/Abc/PmAgent.py +34 -26
- xync_client/Abc/xtype.py +24 -2
- xync_client/Bybit/InAgent.py +122 -87
- xync_client/Bybit/agent.py +477 -542
- xync_client/Bybit/etype/ad.py +11 -56
- xync_client/Bybit/etype/cred.py +29 -9
- xync_client/Bybit/etype/order.py +42 -55
- xync_client/Bybit/ex.py +15 -2
- xync_client/Gmail/__init__.py +119 -98
- xync_client/Htx/agent.py +162 -31
- xync_client/Htx/etype/ad.py +18 -11
- xync_client/Htx/ex.py +7 -9
- xync_client/Mexc/agent.py +85 -0
- xync_client/Mexc/api.py +636 -0
- xync_client/Mexc/etype/order.py +639 -0
- xync_client/Mexc/ex.py +10 -8
- xync_client/Pms/Payeer/__init__.py +38 -29
- xync_client/Pms/Payeer/login.py +6 -2
- xync_client/Pms/Volet/__init__.py +82 -63
- xync_client/Pms/Volet/api.py +5 -4
- xync_client/loader.py +1 -0
- xync_client/pm_unifier.py +1 -1
- {xync_client-0.0.141.dist-info → xync_client-0.0.155.dist-info}/METADATA +4 -1
- {xync_client-0.0.141.dist-info → xync_client-0.0.155.dist-info}/RECORD +31 -26
- {xync_client-0.0.141.dist-info → xync_client-0.0.155.dist-info}/WHEEL +0 -0
- {xync_client-0.0.141.dist-info → xync_client-0.0.155.dist-info}/top_level.txt +0 -0
xync_client/Mexc/api.py
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MEXC P2P OpenAPI v1.2 Async Client
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import hmac
|
|
6
|
+
import hashlib
|
|
7
|
+
import time
|
|
8
|
+
from typing import Optional, List, Literal
|
|
9
|
+
from decimal import Decimal
|
|
10
|
+
|
|
11
|
+
import aiohttp
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ============ Enums ============
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Side(str):
|
|
19
|
+
BUY = "BUY"
|
|
20
|
+
SELL = "SELL"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AdvStatus(str):
|
|
24
|
+
CLOSE = "CLOSE"
|
|
25
|
+
OPEN = "OPEN"
|
|
26
|
+
DELETE = "DELETE"
|
|
27
|
+
LOW_STOCK = "LOW_STOCK"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OrderDealState(str):
|
|
31
|
+
NOT_PAID = "NOT_PAID"
|
|
32
|
+
PAID = "PAID"
|
|
33
|
+
WAIT_PROCESS = "WAIT_PROCESS"
|
|
34
|
+
PROCESSING = "PROCESSING"
|
|
35
|
+
DONE = "DONE"
|
|
36
|
+
CANCEL = "CANCEL"
|
|
37
|
+
INVALID = "INVALID"
|
|
38
|
+
REFUSE = "REFUSE"
|
|
39
|
+
TIMEOUT = "TIMEOUT"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class NotifyType(str):
|
|
43
|
+
SMS = "SMS"
|
|
44
|
+
MAIL = "MAIL"
|
|
45
|
+
GA = "GA"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ============ Request Models ============
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class CreateUpdateAdRequest(BaseModel):
|
|
52
|
+
advNo: Optional[str] = None
|
|
53
|
+
payTimeLimit: int
|
|
54
|
+
initQuantity: Decimal
|
|
55
|
+
supplyQuantity: Optional[Decimal] = None
|
|
56
|
+
price: Decimal
|
|
57
|
+
coinId: str
|
|
58
|
+
countryCode: Optional[str] = None
|
|
59
|
+
side: str
|
|
60
|
+
advStatus: Optional[str] = None
|
|
61
|
+
allowSys: Optional[bool] = None
|
|
62
|
+
fiatUnit: str
|
|
63
|
+
payMethod: str
|
|
64
|
+
autoReplyMsg: Optional[str] = None
|
|
65
|
+
tradeTerms: Optional[str] = None
|
|
66
|
+
minSingleTransAmount: Decimal
|
|
67
|
+
maxSingleTransAmount: Decimal
|
|
68
|
+
kycLevel: Optional[str] = None
|
|
69
|
+
requireMobile: Optional[bool] = None
|
|
70
|
+
userAllTradeCountMin: int
|
|
71
|
+
userAllTradeCountMax: int
|
|
72
|
+
exchangeCount: Optional[int] = None
|
|
73
|
+
maxPayLimit: Optional[int] = None
|
|
74
|
+
buyerRegDaysLimit: Optional[int] = None
|
|
75
|
+
creditAmount: Optional[Decimal] = None
|
|
76
|
+
blockTrade: Optional[bool] = None
|
|
77
|
+
deviceId: Optional[str] = None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class CreateOrderRequest(BaseModel):
|
|
81
|
+
advNo: str
|
|
82
|
+
amount: Optional[Decimal] = None
|
|
83
|
+
tradableQuantity: Optional[Decimal] = None
|
|
84
|
+
userConfirmPaymentId: int
|
|
85
|
+
userConfirmPayMethodId: Optional[int] = None
|
|
86
|
+
deviceId: Optional[str] = None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class ConfirmPaidRequest(BaseModel):
|
|
90
|
+
advOrderNo: str
|
|
91
|
+
payId: int
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class ReleaseCoinRequest(BaseModel):
|
|
95
|
+
advOrderNo: str
|
|
96
|
+
notifyType: Optional[str] = None
|
|
97
|
+
notifyCode: Optional[str] = None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ServiceSwitchRequest(BaseModel):
|
|
101
|
+
open: bool
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ============ Response Models ============
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class BaseResponse(BaseModel):
|
|
108
|
+
code: int
|
|
109
|
+
msg: str
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class PaymentInfo(BaseModel):
|
|
113
|
+
id: int
|
|
114
|
+
payMethod: int
|
|
115
|
+
bankName: str
|
|
116
|
+
account: str
|
|
117
|
+
bankAddress: str
|
|
118
|
+
payee: str
|
|
119
|
+
extend: str
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class MerchantInfo(BaseModel):
|
|
123
|
+
nickName: str
|
|
124
|
+
imId: str
|
|
125
|
+
memberId: str
|
|
126
|
+
registry: int
|
|
127
|
+
vipLevel: int
|
|
128
|
+
greenDiamond: bool
|
|
129
|
+
emailAuthentication: bool
|
|
130
|
+
smsAuthentication: bool
|
|
131
|
+
identityVerification: bool
|
|
132
|
+
lastOnlineTime: int
|
|
133
|
+
badge: str
|
|
134
|
+
merchantType: str
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class MerchantStatistics(BaseModel):
|
|
138
|
+
totalBuyCount: int
|
|
139
|
+
totalSellCount: int
|
|
140
|
+
doneLastMonthCount: int
|
|
141
|
+
avgBuyHandleTime: int
|
|
142
|
+
avgSellHandleTime: int
|
|
143
|
+
lastMonthCompleteRate: str
|
|
144
|
+
completeRate: str
|
|
145
|
+
avgHandleTime: int
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class Advertisement(BaseModel):
|
|
149
|
+
advNo: str
|
|
150
|
+
payTimeLimit: int
|
|
151
|
+
quantity: int
|
|
152
|
+
price: Decimal
|
|
153
|
+
initAmount: Decimal
|
|
154
|
+
frozenQuantity: Decimal
|
|
155
|
+
availableQuantity: Decimal
|
|
156
|
+
coinId: str
|
|
157
|
+
coinName: str
|
|
158
|
+
countryCode: str
|
|
159
|
+
commissionRate: Decimal
|
|
160
|
+
advStatus: str
|
|
161
|
+
side: str
|
|
162
|
+
createTime: int
|
|
163
|
+
updateTime: int
|
|
164
|
+
fiatUnit: str
|
|
165
|
+
feeType: int
|
|
166
|
+
autoReplyMsg: str
|
|
167
|
+
tradeTerms: str
|
|
168
|
+
payMethod: str
|
|
169
|
+
paymentInfo: List[PaymentInfo]
|
|
170
|
+
minSingleTransAmount: Decimal
|
|
171
|
+
maxSingleTransAmount: Decimal
|
|
172
|
+
kycLevel: int
|
|
173
|
+
requireMobile: bool
|
|
174
|
+
userAllTradeCountMax: int
|
|
175
|
+
userAllTradeCountMin: int
|
|
176
|
+
exchangeCount: int
|
|
177
|
+
maxPayLimit: int
|
|
178
|
+
buyerRegDaysLimit: int
|
|
179
|
+
blockTrade: bool
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class MarketAdvertisement(Advertisement):
|
|
183
|
+
merchant: MerchantInfo
|
|
184
|
+
merchantStatistics: MerchantStatistics
|
|
185
|
+
orderPayCount: int
|
|
186
|
+
tags: str
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class PageInfo(BaseModel):
|
|
190
|
+
total: int
|
|
191
|
+
currPage: int
|
|
192
|
+
pageSize: int
|
|
193
|
+
totalPage: int
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class UserInfo(BaseModel):
|
|
197
|
+
nickName: str
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class Order(BaseModel):
|
|
201
|
+
advNo: str
|
|
202
|
+
advOrderNo: str
|
|
203
|
+
tradableQuantity: Decimal
|
|
204
|
+
price: Decimal
|
|
205
|
+
amount: Decimal
|
|
206
|
+
coinName: str
|
|
207
|
+
state: int
|
|
208
|
+
payTimeLimit: int
|
|
209
|
+
side: str
|
|
210
|
+
fiatUnit: str
|
|
211
|
+
createTime: int
|
|
212
|
+
updateTime: int
|
|
213
|
+
userInfo: UserInfo
|
|
214
|
+
complained: bool
|
|
215
|
+
blockUser: bool
|
|
216
|
+
unreadCount: int
|
|
217
|
+
complainId: Optional[str] = None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class OrderDetail(Order):
|
|
221
|
+
paymentInfo: List[PaymentInfo]
|
|
222
|
+
allowComplainTime: int
|
|
223
|
+
confirmPaymentInfo: PaymentInfo
|
|
224
|
+
userInfo: dict
|
|
225
|
+
userFiatStatistics: dict
|
|
226
|
+
spotCount: int
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class CreateAdResponse(BaseResponse):
|
|
230
|
+
data: str # advNo
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class AdListResponse(BaseResponse):
|
|
234
|
+
data: List[Advertisement]
|
|
235
|
+
page: PageInfo
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class MarketAdListResponse(BaseResponse):
|
|
239
|
+
data: List[MarketAdvertisement]
|
|
240
|
+
page: PageInfo
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class CreateOrderResponse(BaseResponse):
|
|
244
|
+
data: str # advOrderNo
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class OrderListResponse(BaseResponse):
|
|
248
|
+
data: List[Order]
|
|
249
|
+
page: PageInfo
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class OrderDetailResponse(BaseResponse):
|
|
253
|
+
data: OrderDetail
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class ListenKeyResponse(BaseResponse):
|
|
257
|
+
listenKey: str
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class ConversationResponse(BaseResponse):
|
|
261
|
+
data: dict
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class ChatMessage(BaseModel):
|
|
265
|
+
id: int
|
|
266
|
+
content: Optional[str] = None
|
|
267
|
+
createTime: str
|
|
268
|
+
fromNickName: str
|
|
269
|
+
fromUserId: str
|
|
270
|
+
type: int
|
|
271
|
+
imageUrl: Optional[str] = None
|
|
272
|
+
imageThumbUrl: Optional[str] = None
|
|
273
|
+
videoUrl: Optional[str] = None
|
|
274
|
+
fileUrl: Optional[str] = None
|
|
275
|
+
self_: bool = Field(alias="self")
|
|
276
|
+
conversationId: int
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class ChatMessagesResponse(BaseResponse):
|
|
280
|
+
data: dict
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class UploadFileResponse(BaseResponse):
|
|
284
|
+
data: dict
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# ============ Client ============
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class MEXCP2PClient:
|
|
291
|
+
"""Асинхронный клиент для MEXC P2P API v1.2"""
|
|
292
|
+
|
|
293
|
+
BASE_URL = "https://api.mexc.com"
|
|
294
|
+
|
|
295
|
+
def __init__(self, api_key: str, api_secret: str):
|
|
296
|
+
self.api_key = api_key
|
|
297
|
+
self.api_secret = api_secret
|
|
298
|
+
self.session: Optional[aiohttp.ClientSession] = None
|
|
299
|
+
|
|
300
|
+
async def __aenter__(self):
|
|
301
|
+
self.session = aiohttp.ClientSession()
|
|
302
|
+
return self
|
|
303
|
+
|
|
304
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
305
|
+
if self.session:
|
|
306
|
+
await self.session.close()
|
|
307
|
+
|
|
308
|
+
def _generate_signature(self, query_string: str) -> str:
|
|
309
|
+
"""Генерация HMAC SHA256 подписи"""
|
|
310
|
+
return hmac.new(self.api_secret.encode("utf-8"), query_string.encode("utf-8"), hashlib.sha256).hexdigest()
|
|
311
|
+
|
|
312
|
+
def _get_timestamp(self) -> int:
|
|
313
|
+
"""Получение текущего timestamp в миллисекундах"""
|
|
314
|
+
return int(time.time() * 1000)
|
|
315
|
+
|
|
316
|
+
async def _request(
|
|
317
|
+
self, method: str, endpoint: str, params: Optional[dict] = None, data: Optional[BaseModel] = None
|
|
318
|
+
) -> dict:
|
|
319
|
+
"""Базовый метод для HTTP запросов"""
|
|
320
|
+
if not self.session:
|
|
321
|
+
raise RuntimeError("Client not initialized. Use async context manager.")
|
|
322
|
+
|
|
323
|
+
timestamp = self._get_timestamp()
|
|
324
|
+
|
|
325
|
+
# Формирование query string для подписи
|
|
326
|
+
query_params = params.copy() if params else {}
|
|
327
|
+
query_params["timestamp"] = timestamp
|
|
328
|
+
|
|
329
|
+
query_string = "&".join(f"{k}={v}" for k, v in sorted(query_params.items()))
|
|
330
|
+
signature = self._generate_signature(query_string)
|
|
331
|
+
|
|
332
|
+
query_params["signature"] = signature
|
|
333
|
+
|
|
334
|
+
headers = {"X-MEXC-APIKEY": self.api_key, "Content-Type": "application/json"}
|
|
335
|
+
|
|
336
|
+
url = f"{self.BASE_URL}{endpoint}"
|
|
337
|
+
|
|
338
|
+
json_data = data.model_dump(exclude_none=True) if data else None
|
|
339
|
+
|
|
340
|
+
async with self.session.request(method, url, params=query_params, json=json_data, headers=headers) as response:
|
|
341
|
+
return await response.json()
|
|
342
|
+
|
|
343
|
+
# ============ Advertisement Methods ============
|
|
344
|
+
|
|
345
|
+
async def create_or_update_ad(self, request: CreateUpdateAdRequest) -> CreateAdResponse:
|
|
346
|
+
"""Создание или обновление объявления"""
|
|
347
|
+
result = await self._request("POST", "/api/v3/fiat/merchant/ads/save_or_update", data=request)
|
|
348
|
+
return CreateAdResponse(**result)
|
|
349
|
+
|
|
350
|
+
async def get_my_ads(
|
|
351
|
+
self,
|
|
352
|
+
coin_id: Optional[str] = None,
|
|
353
|
+
adv_status: Optional[str] = None,
|
|
354
|
+
merchant_id: Optional[str] = None,
|
|
355
|
+
fiat_unit: Optional[str] = None,
|
|
356
|
+
side: Optional[str] = None,
|
|
357
|
+
kyc_level: Optional[str] = None,
|
|
358
|
+
start_time: Optional[int] = None,
|
|
359
|
+
end_time: Optional[int] = None,
|
|
360
|
+
page: int = 1,
|
|
361
|
+
limit: int = 10,
|
|
362
|
+
) -> AdListResponse:
|
|
363
|
+
"""Получение списка моих объявлений с пагинацией"""
|
|
364
|
+
params = {"page": page, "limit": limit}
|
|
365
|
+
|
|
366
|
+
if coin_id:
|
|
367
|
+
params["coinId"] = coin_id
|
|
368
|
+
if adv_status:
|
|
369
|
+
params["advStatus"] = adv_status
|
|
370
|
+
if merchant_id:
|
|
371
|
+
params["merchantId"] = merchant_id
|
|
372
|
+
if fiat_unit:
|
|
373
|
+
params["fiatUnit"] = fiat_unit
|
|
374
|
+
if side:
|
|
375
|
+
params["side"] = side
|
|
376
|
+
if kyc_level:
|
|
377
|
+
params["kycLevel"] = kyc_level
|
|
378
|
+
if start_time:
|
|
379
|
+
params["startTime"] = start_time
|
|
380
|
+
if end_time:
|
|
381
|
+
params["endTime"] = end_time
|
|
382
|
+
|
|
383
|
+
result = await self._request("GET", "/api/v3/fiat/merchant/ads/pagination", params=params)
|
|
384
|
+
return AdListResponse(**result)
|
|
385
|
+
|
|
386
|
+
async def get_market_ads(
|
|
387
|
+
self,
|
|
388
|
+
fiat_unit: str,
|
|
389
|
+
coin_id: str,
|
|
390
|
+
country_code: Optional[str] = None,
|
|
391
|
+
side: Optional[str] = None,
|
|
392
|
+
amount: Optional[Decimal] = None,
|
|
393
|
+
quantity: Optional[Decimal] = None,
|
|
394
|
+
pay_method: Optional[str] = None,
|
|
395
|
+
block_trade: Optional[bool] = None,
|
|
396
|
+
allow_trade: Optional[bool] = None,
|
|
397
|
+
have_trade: Optional[bool] = None,
|
|
398
|
+
follow: Optional[bool] = None,
|
|
399
|
+
page: int = 1,
|
|
400
|
+
) -> MarketAdListResponse:
|
|
401
|
+
"""Получение рыночных объявлений"""
|
|
402
|
+
params = {"fiatUnit": fiat_unit, "coinId": coin_id, "page": page}
|
|
403
|
+
|
|
404
|
+
if country_code:
|
|
405
|
+
params["countryCode"] = country_code
|
|
406
|
+
if side:
|
|
407
|
+
params["side"] = side
|
|
408
|
+
if amount:
|
|
409
|
+
params["amount"] = str(amount)
|
|
410
|
+
if quantity:
|
|
411
|
+
params["quantity"] = str(quantity)
|
|
412
|
+
if pay_method:
|
|
413
|
+
params["payMethod"] = pay_method
|
|
414
|
+
if block_trade is not None:
|
|
415
|
+
params["blockTrade"] = block_trade
|
|
416
|
+
if allow_trade is not None:
|
|
417
|
+
params["allowTrade"] = allow_trade
|
|
418
|
+
if have_trade is not None:
|
|
419
|
+
params["haveTrade"] = have_trade
|
|
420
|
+
if follow is not None:
|
|
421
|
+
params["follow"] = follow
|
|
422
|
+
|
|
423
|
+
result = await self._request("GET", "/api/v3/fiat/market/ads/pagination", params=params)
|
|
424
|
+
return MarketAdListResponse(**result)
|
|
425
|
+
|
|
426
|
+
# ============ Order Methods ============
|
|
427
|
+
|
|
428
|
+
async def create_order(self, request: CreateOrderRequest) -> CreateOrderResponse:
|
|
429
|
+
"""Создание ордера (захват объявления)"""
|
|
430
|
+
result = await self._request("POST", "/api/v3/fiat/merchant/order/deal", data=request)
|
|
431
|
+
return CreateOrderResponse(**result)
|
|
432
|
+
|
|
433
|
+
async def get_my_orders(
|
|
434
|
+
self,
|
|
435
|
+
start_time: int,
|
|
436
|
+
end_time: int,
|
|
437
|
+
coin_id: Optional[str] = None,
|
|
438
|
+
adv_order_no: Optional[str] = None,
|
|
439
|
+
side: Optional[str] = None,
|
|
440
|
+
order_deal_state: Optional[str] = None,
|
|
441
|
+
page: int = 1,
|
|
442
|
+
limit: int = 10,
|
|
443
|
+
) -> OrderListResponse:
|
|
444
|
+
"""Получение моих ордеров (только как maker)"""
|
|
445
|
+
params = {"startTime": start_time, "endTime": end_time, "page": page, "limit": limit}
|
|
446
|
+
|
|
447
|
+
if coin_id:
|
|
448
|
+
params["coinId"] = coin_id
|
|
449
|
+
if adv_order_no:
|
|
450
|
+
params["advOrderNo"] = adv_order_no
|
|
451
|
+
if side:
|
|
452
|
+
params["side"] = side
|
|
453
|
+
if order_deal_state:
|
|
454
|
+
params["orderDealState"] = order_deal_state
|
|
455
|
+
|
|
456
|
+
result = await self._request("GET", "/api/v3/fiat/merchant/order/pagination", params=params)
|
|
457
|
+
return OrderListResponse(**result)
|
|
458
|
+
|
|
459
|
+
async def get_market_orders(
|
|
460
|
+
self,
|
|
461
|
+
coin_id: Optional[str] = None,
|
|
462
|
+
adv_order_no: Optional[str] = None,
|
|
463
|
+
side: Optional[str] = None,
|
|
464
|
+
order_deal_state: Optional[str] = None,
|
|
465
|
+
start_time: Optional[int] = None,
|
|
466
|
+
end_time: Optional[int] = None,
|
|
467
|
+
page: int = 1,
|
|
468
|
+
limit: int = 10,
|
|
469
|
+
) -> OrderListResponse:
|
|
470
|
+
"""Получение всех ордеров (как maker и taker)"""
|
|
471
|
+
params = {"page": page, "limit": limit}
|
|
472
|
+
|
|
473
|
+
if coin_id:
|
|
474
|
+
params["coinId"] = coin_id
|
|
475
|
+
if adv_order_no:
|
|
476
|
+
params["advOrderNo"] = adv_order_no
|
|
477
|
+
if side:
|
|
478
|
+
params["side"] = side
|
|
479
|
+
if order_deal_state:
|
|
480
|
+
params["orderDealState"] = order_deal_state
|
|
481
|
+
if start_time:
|
|
482
|
+
params["startTime"] = start_time
|
|
483
|
+
if end_time:
|
|
484
|
+
params["endTime"] = end_time
|
|
485
|
+
|
|
486
|
+
result = await self._request("GET", "/api/v3/fiat/market/order/pagination", params=params)
|
|
487
|
+
return OrderListResponse(**result)
|
|
488
|
+
|
|
489
|
+
async def confirm_paid(self, request: ConfirmPaidRequest) -> BaseResponse:
|
|
490
|
+
"""Подтверждение оплаты"""
|
|
491
|
+
result = await self._request("POST", "/api/v3/fiat/confirm_paid", data=request)
|
|
492
|
+
return BaseResponse(**result)
|
|
493
|
+
|
|
494
|
+
async def release_coin(self, request: ReleaseCoinRequest) -> BaseResponse:
|
|
495
|
+
"""Релиз криптовалюты"""
|
|
496
|
+
result = await self._request("POST", "/api/v3/fiat/release_coin", data=request)
|
|
497
|
+
return BaseResponse(**result)
|
|
498
|
+
|
|
499
|
+
async def get_order_detail(self, adv_order_no: str) -> OrderDetailResponse:
|
|
500
|
+
"""Получение деталей ордера"""
|
|
501
|
+
params = {"advOrderNo": adv_order_no}
|
|
502
|
+
|
|
503
|
+
result = await self._request("GET", "/api/v3/fiat/order/detail", params=params)
|
|
504
|
+
return OrderDetailResponse(**result)
|
|
505
|
+
|
|
506
|
+
# ============ Service Methods ============
|
|
507
|
+
|
|
508
|
+
async def switch_service(self, request: ServiceSwitchRequest) -> BaseResponse:
|
|
509
|
+
"""Открытие/закрытие торговли"""
|
|
510
|
+
result = await self._request("POST", "/api/v3/fiat/merchant/service/switch", data=request)
|
|
511
|
+
return BaseResponse(**result)
|
|
512
|
+
|
|
513
|
+
# ============ WebSocket Methods ============
|
|
514
|
+
|
|
515
|
+
async def generate_listen_key(self) -> ListenKeyResponse:
|
|
516
|
+
"""Генерация listenKey для WebSocket"""
|
|
517
|
+
result = await self._request("POST", "/api/v3/userDataStream")
|
|
518
|
+
return ListenKeyResponse(**result)
|
|
519
|
+
|
|
520
|
+
async def get_listen_key(self) -> ListenKeyResponse:
|
|
521
|
+
"""Получение существующего listenKey"""
|
|
522
|
+
result = await self._request("GET", "/api/v3/userDataStream")
|
|
523
|
+
return ListenKeyResponse(**result)
|
|
524
|
+
|
|
525
|
+
# ============ Chat Methods ============
|
|
526
|
+
async def get_chat_conversation(self, order_no: str) -> ConversationResponse:
|
|
527
|
+
"""Получение ID чат-сессии для ордера"""
|
|
528
|
+
params = {"orderNo": order_no}
|
|
529
|
+
|
|
530
|
+
result = await self._request("GET", "/api/v3/fiat/retrieveChatConversation", params=params)
|
|
531
|
+
return ConversationResponse(**result)
|
|
532
|
+
|
|
533
|
+
async def get_chat_messages(
|
|
534
|
+
self,
|
|
535
|
+
conversation_id: int,
|
|
536
|
+
page: int = 1,
|
|
537
|
+
limit: int = 20,
|
|
538
|
+
chat_message_type: Optional[str] = None,
|
|
539
|
+
message_id: Optional[int] = None,
|
|
540
|
+
sort: Literal["DESC", "ASC"] = "DESC",
|
|
541
|
+
) -> ChatMessagesResponse:
|
|
542
|
+
"""Получение истории чата с пагинацией"""
|
|
543
|
+
params = {"conversationId": conversation_id, "page": page, "limit": limit, "sort": sort}
|
|
544
|
+
|
|
545
|
+
if chat_message_type:
|
|
546
|
+
params["chatMessageType"] = chat_message_type
|
|
547
|
+
if message_id:
|
|
548
|
+
params["id"] = message_id
|
|
549
|
+
|
|
550
|
+
result = await self._request("GET", "/api/v3/fiat/retrieveChatMessageWithPagination", params=params)
|
|
551
|
+
return ChatMessagesResponse(**result)
|
|
552
|
+
|
|
553
|
+
async def upload_file(self, file_data: bytes, filename: str) -> UploadFileResponse:
|
|
554
|
+
"""Загрузка файла"""
|
|
555
|
+
if not self.session:
|
|
556
|
+
raise RuntimeError("Client not initialized.")
|
|
557
|
+
|
|
558
|
+
timestamp = self._get_timestamp()
|
|
559
|
+
query_string = f"timestamp={timestamp}"
|
|
560
|
+
signature = self._generate_signature(query_string)
|
|
561
|
+
|
|
562
|
+
url = f"{self.BASE_URL}/api/v3/fiat/uploadFile"
|
|
563
|
+
params = {"timestamp": timestamp, "signature": signature}
|
|
564
|
+
|
|
565
|
+
headers = {"X-MEXC-APIKEY": self.api_key}
|
|
566
|
+
|
|
567
|
+
form = aiohttp.FormData()
|
|
568
|
+
form.add_field("file", file_data, filename=filename)
|
|
569
|
+
|
|
570
|
+
async with self.session.post(url, params=params, data=form, headers=headers) as response:
|
|
571
|
+
result = await response.json()
|
|
572
|
+
|
|
573
|
+
return UploadFileResponse(**result)
|
|
574
|
+
|
|
575
|
+
async def download_file(self, file_id: str) -> dict:
|
|
576
|
+
"""Скачивание файла"""
|
|
577
|
+
params = {"fileId": file_id}
|
|
578
|
+
|
|
579
|
+
result = await self._request("GET", "/api/v3/fiat/downloadFile", params=params)
|
|
580
|
+
return result
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
# ============ Usage Example ============
|
|
584
|
+
async def main():
|
|
585
|
+
"""Пример использования клиента"""
|
|
586
|
+
|
|
587
|
+
api_key = "your_api_key"
|
|
588
|
+
api_secret = "your_api_secret"
|
|
589
|
+
|
|
590
|
+
async with MEXCP2PClient(api_key, api_secret) as client:
|
|
591
|
+
# Создание объявления
|
|
592
|
+
ad_request = CreateUpdateAdRequest(
|
|
593
|
+
payTimeLimit=15,
|
|
594
|
+
initQuantity=Decimal("100"),
|
|
595
|
+
price=Decimal("70000"),
|
|
596
|
+
coinId="5989b56ba96a43599dbeeca5bb053f43",
|
|
597
|
+
side=Side.BUY,
|
|
598
|
+
fiatUnit="USD",
|
|
599
|
+
payMethod="1",
|
|
600
|
+
minSingleTransAmount=Decimal("10"),
|
|
601
|
+
maxSingleTransAmount=Decimal("1000"),
|
|
602
|
+
userAllTradeCountMin=0,
|
|
603
|
+
userAllTradeCountMax=100,
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
ad_response = await client.create_or_update_ad(ad_request)
|
|
607
|
+
print(f"Created ad: {ad_response.data}")
|
|
608
|
+
|
|
609
|
+
# Получение рыночных объявлений
|
|
610
|
+
market_ads = await client.get_market_ads(
|
|
611
|
+
fiat_unit="USD", coin_id="5989b56ba96a43599dbeeca5bb053f43", side=Side.SELL, page=1
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
print(f"Found {len(market_ads.data)} ads")
|
|
615
|
+
|
|
616
|
+
# Создание ордера
|
|
617
|
+
if market_ads.data:
|
|
618
|
+
first_ad = market_ads.data[0]
|
|
619
|
+
order_request = CreateOrderRequest(advNo=first_ad.advNo, amount=Decimal("100"), userConfirmPaymentId=123)
|
|
620
|
+
|
|
621
|
+
order_response = await client.create_order(order_request)
|
|
622
|
+
print(f"Created order: {order_response.data}")
|
|
623
|
+
|
|
624
|
+
# Получение деталей ордера
|
|
625
|
+
order_detail = await client.get_order_detail("order_id_here")
|
|
626
|
+
print(f"Order state: {order_detail.data.state}")
|
|
627
|
+
|
|
628
|
+
# Генерация listenKey для WebSocket
|
|
629
|
+
listen_key = await client.generate_listen_key()
|
|
630
|
+
print(f"ListenKey: {listen_key.listenKey}")
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
if __name__ == "__main__":
|
|
634
|
+
import asyncio
|
|
635
|
+
|
|
636
|
+
asyncio.run(main())
|