xync-client 0.0.25.dev11__tar.gz → 0.0.25.dev19__tar.gz
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-0.0.25.dev11/xync_client.egg-info → xync_client-0.0.25.dev19}/PKG-INFO +1 -1
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Abc/Agent.py +28 -11
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Abc/AuthTrait.py +1 -3
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Abc/Base.py +3 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Abc/Ex.py +9 -9
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/agent.py +167 -119
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/auth.py +9 -3
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/ex.py +60 -40
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/pyd.py +66 -91
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19/xync_client.egg-info}/PKG-INFO +1 -1
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/.env.sample +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/.gitignore +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/.pre-commit-config.yaml +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/README.md +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/makefile +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/pyproject.toml +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/setup.cfg +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/TestAgent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/TestAsset.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/TestEx.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/TestOrder.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Binance/test_binance.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Gate/test_gate.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Htx/test_htx_p2p.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Wallet/test_agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Wallet/test_ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/_test_ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Abc/Asset.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Abc/BaseTest.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Abc/InAgent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Abc/Order.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Binance/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Binance/binance_async.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Binance/earn_api.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Binance/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Binance/exceptions.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Binance/sapi.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Binance/web_c2c.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BingX/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BingX/agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BingX/base.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BingX/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BingX/req.mjs +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BingX/sign.js +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BingX/test/main.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BitGet/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BitGet/agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BitGet/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/BitGet/req.mjs +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Bybit/agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Bybit/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Bybit/web_earn.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Bybit/web_p2p.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Gate/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Gate/premarket.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Htx/agent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Htx/earn.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Htx/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/KuCoin/pub.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/KuCoin/web.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/Okx/ex.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/asset.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/inAgent.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/order.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/pyro.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/TgWallet/web.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/__init__.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client/loader.py +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client.egg-info/SOURCES.txt +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client.egg-info/dependency_links.txt +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client.egg-info/requires.txt +0 -0
- {xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client.egg-info/top_level.txt +0 -0
|
@@ -3,20 +3,18 @@ from abc import abstractmethod
|
|
|
3
3
|
from pydantic import BaseModel
|
|
4
4
|
from tortoise.exceptions import IntegrityError
|
|
5
5
|
from x_model import HTTPException, FailReason
|
|
6
|
-
from
|
|
6
|
+
from xync_schema.models import OrderStatus, Coin, Cur, Ad, AdStatus, Fiat, Agent, Pmex, Pmcur, Cred, PmexBank
|
|
7
|
+
from xync_schema.pydantic import FiatNew, FiatUpd, BaseAd, FiatPydIn, AdSalePydIn, AdBuyPydIn, CredPydIn, BaseOrder
|
|
7
8
|
|
|
9
|
+
from xync_client.Abc.Ex import BaseExClient
|
|
8
10
|
from xync_client.Abc.Base import BaseClient
|
|
9
11
|
|
|
10
|
-
from xync_client.Abc.AuthTrait import BaseAuthTrait
|
|
11
|
-
from xync_schema.models import OrderStatus, Coin, Cur, Ad, AdStatus, Fiat, Agent, Pmex, Pmcur
|
|
12
|
-
from xync_schema.pydantic import FiatNew, FiatUpd, BaseAd, FiatPydIn
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
class BaseAgentClient(BaseClient, BaseAuthTrait):
|
|
13
|
+
class BaseAgentClient(BaseClient):
|
|
16
14
|
def __init__(self, agent: Agent):
|
|
17
15
|
self.agent: Agent = agent
|
|
18
|
-
self.
|
|
19
|
-
|
|
16
|
+
super().__init__(self.agent.contragent.ex) # , "host_p2p"
|
|
17
|
+
self.ex_client: BaseExClient = self.ex.client()
|
|
20
18
|
|
|
21
19
|
@abstractmethod
|
|
22
20
|
async def start_listen(self) -> bool: ...
|
|
@@ -33,7 +31,7 @@ class BaseAgentClient(BaseClient, BaseAuthTrait):
|
|
|
33
31
|
|
|
34
32
|
# 1: [T] Запрос на старт сделки
|
|
35
33
|
@abstractmethod
|
|
36
|
-
async def order_request(self,
|
|
34
|
+
async def order_request(self, order: BaseOrder) -> dict: ...
|
|
37
35
|
|
|
38
36
|
# async def start_order(self, order: Order) -> OrderOutClient:
|
|
39
37
|
# return OrderOutClient(self, order)
|
|
@@ -79,7 +77,7 @@ class BaseAgentClient(BaseClient, BaseAuthTrait):
|
|
|
79
77
|
|
|
80
78
|
# 25: Список реквизитов моих платежных методов
|
|
81
79
|
@abstractmethod
|
|
82
|
-
async def
|
|
80
|
+
async def creds(self) -> list: ... # {fiat.exid: {fiat}}
|
|
83
81
|
|
|
84
82
|
@staticmethod
|
|
85
83
|
async def fiat_pyd2db(fiat_pyd: FiatNew | FiatUpd, uid: int, fid: int = None) -> tuple[Fiat, bool]:
|
|
@@ -97,7 +95,7 @@ class BaseAgentClient(BaseClient, BaseAuthTrait):
|
|
|
97
95
|
# 26: Создание реквизита моего платежного метода
|
|
98
96
|
@abstractmethod
|
|
99
97
|
async def fiat_f2in(self, fiat_new: FiatNew) -> FiatPydIn:
|
|
100
|
-
if not (
|
|
98
|
+
if not (_pmcur := await Pmcur.get_or_none(cur_id=fiat_new.cur_id, pm_id=fiat_new.pm_id)):
|
|
101
99
|
raise HTTPException(FailReason.body, f"No Pmcur with cur#{fiat_new.cur_id} and pm#{fiat_new.pm_id}", 404)
|
|
102
100
|
# cred = await Cred.create({"exid": }, pmcur=pmcur, contragent=self.agent.contragent)
|
|
103
101
|
# df = {"detail": fiat_pyd.detail, "name": fiat_pyd.name, "amount": fiat_pyd.amount, "target": fiat_pyd.target}
|
|
@@ -182,3 +180,22 @@ class BaseAgentClient(BaseClient, BaseAuthTrait):
|
|
|
182
180
|
# 39: Балансы моих монет
|
|
183
181
|
@abstractmethod
|
|
184
182
|
async def my_assets(self) -> dict: ...
|
|
183
|
+
|
|
184
|
+
# Сохранение объявления (с Pm/Cred-ами) в бд
|
|
185
|
+
async def ad_pydin2db(self, ad_pydin: AdSalePydIn | AdBuyPydIn) -> Ad:
|
|
186
|
+
ad_db = await self.ex_client.ad_pydin2db(ad_pydin)
|
|
187
|
+
await ad_db.creds.add(*getattr(ad_pydin, "creds_", []))
|
|
188
|
+
return ad_db
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
async def cred_pydin2db(cred: CredPydIn) -> Cred:
|
|
192
|
+
df, unq = cred.args()
|
|
193
|
+
cred_db, _ = await Cred.update_or_create(df, **unq)
|
|
194
|
+
if cred.banks:
|
|
195
|
+
await cred_db.banks.add(*[await PmexBank.get(exid=b) for b in cred.banks])
|
|
196
|
+
return cred_db
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
async def fiat_cred2db(cred: Cred) -> Fiat:
|
|
200
|
+
fiat, _ = await Fiat.get_or_create(cred=cred)
|
|
201
|
+
return fiat
|
|
@@ -6,11 +6,9 @@ from aiohttp.http_exceptions import HttpProcessingError
|
|
|
6
6
|
from x_client.aiohttp import Client
|
|
7
7
|
from xync_schema.models import Agent
|
|
8
8
|
|
|
9
|
-
# from x_client.aiohttp import Client
|
|
10
|
-
|
|
11
9
|
|
|
12
10
|
class BaseAuthTrait(Client):
|
|
13
|
-
agent: Agent
|
|
11
|
+
agent: Agent = None
|
|
14
12
|
|
|
15
13
|
@abstractmethod
|
|
16
14
|
async def _get_auth_hdrs(self) -> dict[str, str]: ...
|
|
@@ -2,8 +2,8 @@ import logging
|
|
|
2
2
|
import re
|
|
3
3
|
from abc import abstractmethod
|
|
4
4
|
|
|
5
|
-
from xync_schema.pydantic import
|
|
6
|
-
from xync_schema.models import Ex, Coin, Cur, Pm, Pmex, Curex, Pmcur, Coinex, PmexBank, Ad
|
|
5
|
+
from xync_schema.pydantic import BaseAdPydIn, PmPyd, BaseAd, CurEpyd, CoinEpyd
|
|
6
|
+
from xync_schema.models import Ex, Coin, Cur, Pm, Pmex, Curex, Pmcur, Coinex, PmexBank, Ad
|
|
7
7
|
|
|
8
8
|
from xync_client.Abc.Base import BaseClient, DictOfDicts, MapOfIdsList
|
|
9
9
|
|
|
@@ -21,8 +21,7 @@ class BaseExClient(BaseClient):
|
|
|
21
21
|
"GTB Bank (Guarantee Trust Bank)": "GTBank",
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
def __init__(self, ex: Ex
|
|
25
|
-
self.ex: Ex = ex
|
|
24
|
+
def __init__(self, ex: Ex):
|
|
26
25
|
self.acronyms: dict[str, str] = {} # for pm norm
|
|
27
26
|
super().__init__(ex) # , "host_p2p"
|
|
28
27
|
|
|
@@ -66,7 +65,7 @@ class BaseExClient(BaseClient):
|
|
|
66
65
|
|
|
67
66
|
# Преобразрование объекта объявления из формата биржи в формат xync
|
|
68
67
|
@abstractmethod
|
|
69
|
-
async def ad_epyd2pydin(self, ad: BaseAd) ->
|
|
68
|
+
async def ad_epyd2pydin(self, ad: BaseAd) -> BaseAdPydIn: ... # my_uid: for MyAd
|
|
70
69
|
|
|
71
70
|
def _pmnorm(self, s: str) -> str:
|
|
72
71
|
def get_and_remove_acro(title: str) -> str:
|
|
@@ -196,8 +195,9 @@ class BaseExClient(BaseClient):
|
|
|
196
195
|
await Coinex.bulk_create(coinexs, update_fields=["minimum"], on_conflict=["coin_id", "ex_id"])
|
|
197
196
|
|
|
198
197
|
# Сохранение чужого объявления (с Pm-ами) в бд
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
ad_db, _ = await Ad.update_or_create(
|
|
202
|
-
|
|
198
|
+
async def ad_pydin2db(self, ad_pydin: BaseAdPydIn) -> Ad:
|
|
199
|
+
df, unq = ad_pydin.args()
|
|
200
|
+
ad_db, _ = await Ad.update_or_create(df, **unq)
|
|
201
|
+
if getattr(ad_pydin, "pms_", None): # if it ListItem, not Full One # todo: remove?
|
|
202
|
+
await ad_db.pms.add(*ad_pydin.pms_)
|
|
203
203
|
return ad_db
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from asyncio import run
|
|
3
3
|
from enum import StrEnum
|
|
4
|
-
|
|
4
|
+
from tg_auth import UserStatus
|
|
5
5
|
from x_model import init_db, HTTPException, FailReason
|
|
6
|
+
from xync_client.TgWallet.ex import ExClient
|
|
6
7
|
from xync_schema import models
|
|
7
8
|
|
|
8
9
|
from xync_client.TgWallet.pyd import (
|
|
9
|
-
|
|
10
|
-
FiatEpyd,
|
|
10
|
+
CredEpyd,
|
|
11
11
|
Attrs,
|
|
12
12
|
AttrsV2,
|
|
13
13
|
OneAdTakerMakerSale,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
OneAdMakerBuy,
|
|
15
|
+
AdMakerBuy,
|
|
16
16
|
AdMakerSale,
|
|
17
17
|
AdMakerNewSale,
|
|
18
|
-
_TakerOne,
|
|
19
18
|
_AdNew,
|
|
20
19
|
FiatEpydIn,
|
|
20
|
+
_PmsTrait,
|
|
21
|
+
OneAdTakerBuy,
|
|
22
|
+
OrderEpyd,
|
|
23
|
+
AdMakerNewBuy,
|
|
24
|
+
OrderEpydIn,
|
|
25
|
+
AvailableVolume,
|
|
21
26
|
)
|
|
22
27
|
from xync_client.loader import PG_DSN
|
|
23
28
|
from xync_schema.enums import AdStatus
|
|
@@ -33,20 +38,25 @@ from xync_schema.models import (
|
|
|
33
38
|
Ad,
|
|
34
39
|
Pmcur,
|
|
35
40
|
Cred,
|
|
36
|
-
PmexBank,
|
|
37
41
|
Agent,
|
|
38
42
|
Curex,
|
|
39
43
|
Coinex,
|
|
44
|
+
Pm,
|
|
45
|
+
Order,
|
|
46
|
+
Contragent,
|
|
40
47
|
)
|
|
41
|
-
from xync_schema.pydantic import FiatNew,
|
|
48
|
+
from xync_schema.pydantic import FiatNew, BaseAdPydIn, AdBuyPydIn, AdSalePydIn, CredPydIn, OrderPydIn
|
|
42
49
|
|
|
43
50
|
from xync_client.Abc.Agent import BaseAgentClient
|
|
44
51
|
|
|
45
52
|
|
|
46
53
|
class Exceptions(StrEnum):
|
|
54
|
+
ORDER_KYC = "FIAT_COUNTRY_NOT_SUPPORTED_BY_USER_KYC_COUNTRY"
|
|
47
55
|
PM_KYC = "OFFER_FIAT_COUNTRY_NOT_SUPPORTED_BY_USER_KYC_COUNTRY"
|
|
48
56
|
ROUND = "ROUNDING_IS_NOT_SUPPORTED"
|
|
49
57
|
CUR = "CURRENCY_IS_NOT_SUPPORTED_BY_PAYMENT_METHOD"
|
|
58
|
+
MAX_ADS = "ACTIVE_OFFER_COUNT_LIMIT_REACHED"
|
|
59
|
+
INACTIVE_AD = "OFFER_ILLEGAL_STATE"
|
|
50
60
|
|
|
51
61
|
|
|
52
62
|
# class Status(IntEnum):
|
|
@@ -54,6 +64,8 @@ class Exceptions(StrEnum):
|
|
|
54
64
|
|
|
55
65
|
|
|
56
66
|
class AgentClient(BaseAgentClient, AuthClient):
|
|
67
|
+
ex_client: ExClient
|
|
68
|
+
|
|
57
69
|
# 0: Получение ордеров в статусе status, по монете coin, в валюте coin, в направлении is_sell
|
|
58
70
|
async def orders(
|
|
59
71
|
self, status: OrderStatus = OrderStatus.created, coin: Coin = None, cur: Cur = None, is_sell: bool = None
|
|
@@ -69,48 +81,64 @@ class AgentClient(BaseAgentClient, AuthClient):
|
|
|
69
81
|
orders = await self._post("/p2p/public-api/v2/offer/order/get", {"orderId": oid})
|
|
70
82
|
return orders["data"]
|
|
71
83
|
|
|
84
|
+
@staticmethod
|
|
85
|
+
async def order_ad2epydin(ad: Ad, amount: float, cred_id: int) -> OrderEpydIn:
|
|
86
|
+
await ad.fetch_related("direction__pairex__pair__cur")
|
|
87
|
+
return OrderEpydIn(
|
|
88
|
+
offerId=ad.exid,
|
|
89
|
+
amount=AvailableVolume(currencyCode=ad.direction.pairex.pair.cur.ticker, amount=str(int(amount))),
|
|
90
|
+
type="SALE" if ad.direction.sell else "PURCHASE",
|
|
91
|
+
paymentDetailsId=cred_id,
|
|
92
|
+
)
|
|
93
|
+
|
|
72
94
|
# 1: [T] Запрос на старт сделки
|
|
73
|
-
async def order_request(self,
|
|
74
|
-
await self.
|
|
75
|
-
ad: _TakerOne = await self.ex_client.ad(ad_id=ad_id)
|
|
76
|
-
fiats: list[FiatEpyd] = await self.fiats()
|
|
77
|
-
fiats_pms: dict[str, int] = {fiat.paymentMethod.code: fiat.id for fiat in fiats}
|
|
78
|
-
pmfs: dict[str, int]
|
|
79
|
-
if ad.is_sell:
|
|
80
|
-
pmfs = {pd.paymentMethod.code: pd.id for pd in ad.paymentDetails if pd.paymentMethod.code in fiats_pms}
|
|
81
|
-
fiatex = (
|
|
82
|
-
await Cred.filter(
|
|
83
|
-
fiat__user_id=self.agent.user_id,
|
|
84
|
-
ex=self.ex_client.ex,
|
|
85
|
-
fiat__pmcur__pm__pmexs__exid__in=pmfs.keys(),
|
|
86
|
-
)
|
|
87
|
-
.order_by("-fiat__amount")
|
|
88
|
-
.first()
|
|
89
|
-
.prefetch_related("fiat__pmcur")
|
|
90
|
-
)
|
|
91
|
-
pmex = await Pmex.get(ex=self.ex_client.ex, pm_id=fiatex.fiat.pmcur.pm_id)
|
|
92
|
-
# else:
|
|
93
|
-
# pmfs = {pm.code: fid for pm in ad.paymentMethods if (fid := fiats_pms.get(pm.code))}
|
|
94
|
-
# f = await Cred.filter(
|
|
95
|
-
# fiat__user_id=self.agent.user_id, ex_id=self.ex_client.ex_id, fiat__pmcur__pm__pmexs__exid__in=pmfs.keys()
|
|
96
|
-
# ).order_by("-fiat__amount").first()
|
|
97
|
-
# p = max(pmfs, key=lambda x: fiats_pms)
|
|
98
|
-
req_body = {
|
|
99
|
-
"offerId": ad_id,
|
|
100
|
-
"paymentDetailsId": pmfs[pmex.exid], # fiats_pms[pid],
|
|
101
|
-
"amount": {"currencyCode": ad.orderAmountLimits.currencyCode, "amount": amount},
|
|
102
|
-
"type": ad.type,
|
|
103
|
-
}
|
|
104
|
-
request = await self._post("/p2p/public-api/v2/offer/order/create-by-amount", req_body)
|
|
95
|
+
async def order_request(self, req: OrderEpydIn) -> OrderEpyd | dict:
|
|
96
|
+
request = await self._post("/p2p/public-api/v2/offer/order/create-by-amount", req.model_dump(exclude_none=True))
|
|
105
97
|
if r := request.get("data"):
|
|
106
|
-
|
|
98
|
+
res = await self._post("/p2p/public-api/v2/offer/order/confirm", {"orderId": r["id"], "type": req.type})
|
|
99
|
+
if res.get("status") == "SUCCESS":
|
|
100
|
+
return OrderEpyd(**r)
|
|
107
101
|
logging.error(request)
|
|
108
102
|
return request
|
|
109
103
|
|
|
104
|
+
async def order_epyd2pydin(self, order: OrderEpyd) -> OrderPydIn:
|
|
105
|
+
ad = await Ad.get(exid=order.offerId, direction__pairex__ex=self.ex)
|
|
106
|
+
cred = await Cred.get(exid=order.paymentDetails.id, contragent__ex=self.ex)
|
|
107
|
+
iam_maker = self.agent.contragent == ad.maker
|
|
108
|
+
taker = (
|
|
109
|
+
(
|
|
110
|
+
await Contragent.get(
|
|
111
|
+
exid=(order.seller if order.is_sell == iam_maker else order.buyer).userId, ex=self.ex
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
if iam_maker
|
|
115
|
+
else self.agent.contragent
|
|
116
|
+
)
|
|
117
|
+
return OrderPydIn(
|
|
118
|
+
exid=order.id,
|
|
119
|
+
amount=order.amount.amount,
|
|
120
|
+
maker_topic=None,
|
|
121
|
+
taker_topic=None,
|
|
122
|
+
status=OrderStatus.created,
|
|
123
|
+
created_at=order.createDateTime,
|
|
124
|
+
payed_at=None,
|
|
125
|
+
confirmed_at=None,
|
|
126
|
+
appealed_at=None,
|
|
127
|
+
ad=ad,
|
|
128
|
+
cred=cred,
|
|
129
|
+
taker=taker,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
async def order_pydin2db(order: OrderPydIn) -> Order:
|
|
134
|
+
df, unq = order.args()
|
|
135
|
+
order_db, _ = await Order.update_or_create(df, **unq)
|
|
136
|
+
return order_db
|
|
137
|
+
|
|
110
138
|
# # # FIATS # # #
|
|
111
139
|
def fiat_args2ex_pyd(
|
|
112
140
|
self, exid: int | str, cur: str, detail: str, name: str, fid: int, typ: str, extra=None
|
|
113
|
-
) ->
|
|
141
|
+
) -> CredEpyd:
|
|
114
142
|
fiat = self.fiat_pyd(
|
|
115
143
|
paymentMethodCode=exid,
|
|
116
144
|
currencyCode=cur,
|
|
@@ -141,11 +169,11 @@ class AgentClient(BaseAgentClient, AuthClient):
|
|
|
141
169
|
}
|
|
142
170
|
|
|
143
171
|
# 25: Список реквизитов моих платежных методов
|
|
144
|
-
async def
|
|
172
|
+
async def creds(self) -> list[CredEpyd]:
|
|
145
173
|
resp = await self._post("/p2p/public-api/v3/payment-details/get/by-user-id")
|
|
146
|
-
return [
|
|
174
|
+
return [CredEpyd(**fiat) for fiat in resp["data"]]
|
|
147
175
|
|
|
148
|
-
async def
|
|
176
|
+
async def cred_epyd2pydin(self, fiat: CredEpyd) -> CredPydIn:
|
|
149
177
|
if not (pmex := await Pmex.get_or_none(exid=fiat.paymentMethod.code, ex=self.ex_client.ex)):
|
|
150
178
|
raise HTTPException(
|
|
151
179
|
FailReason.body, f"No Pmex {fiat.paymentMethod.code} on ex#{self.ex_client.ex.name}", 404
|
|
@@ -154,36 +182,26 @@ class AgentClient(BaseAgentClient, AuthClient):
|
|
|
154
182
|
raise HTTPException(
|
|
155
183
|
FailReason.body, f"No Pmcur with cur#{fiat.currency} and pm#{fiat.paymentMethod.code}", 404
|
|
156
184
|
)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
detail="",
|
|
160
|
-
name=fiat.name,
|
|
161
|
-
amount=0,
|
|
162
|
-
user_id=self.agent.user_id,
|
|
185
|
+
cred_pin = CredPydIn(
|
|
186
|
+
exid=fiat.id,
|
|
163
187
|
pmcur=pmcur,
|
|
188
|
+
contragent=self.agent.contragent,
|
|
189
|
+
name=fiat.name,
|
|
164
190
|
)
|
|
165
191
|
for val in fiat.attributes.values:
|
|
166
|
-
if val.name
|
|
167
|
-
|
|
192
|
+
if val.name == "BANKS":
|
|
193
|
+
cred_pin.banks = [b.code for b in val.value]
|
|
168
194
|
else:
|
|
169
|
-
val
|
|
170
|
-
|
|
171
|
-
return ftx
|
|
195
|
+
cred_pin.detail = val.value
|
|
196
|
+
return cred_pin
|
|
172
197
|
|
|
173
198
|
# 25: Список реквизитов моих платежных методов
|
|
174
|
-
async def
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
for
|
|
179
|
-
|
|
180
|
-
cred_db, _ = await Cred.update_or_create(
|
|
181
|
-
{"name": fx.cred.name, "detail": fx.cred.detail}, exid=fx.id, ex=self.ex_client.ex
|
|
182
|
-
)
|
|
183
|
-
fiat_db, _ = await Fiat.update_or_create(df, **unq)
|
|
184
|
-
banks = [await PmexBank.get(exid=bn) for bn in fx.banks]
|
|
185
|
-
await cred_db.banks.add(*banks)
|
|
186
|
-
return fxs
|
|
199
|
+
async def set_fiats(self) -> list[Fiat]:
|
|
200
|
+
creds_epyd: list[CredEpyd] = await self.creds()
|
|
201
|
+
creds_pyd_in: list[CredPydIn] = [await self.cred_epyd2pydin(f) for f in creds_epyd]
|
|
202
|
+
creds_db = [await self.cred_pydin2db(crd) for crd in creds_pyd_in]
|
|
203
|
+
fiats_db = [await self.fiat_cred2db(cd) for cd in creds_db]
|
|
204
|
+
return fiats_db
|
|
187
205
|
|
|
188
206
|
# 26: Создание реквизита моего платежного метода
|
|
189
207
|
async def fiat_new(self, fiat: FiatNew) -> Fiat:
|
|
@@ -201,7 +219,7 @@ class AgentClient(BaseAgentClient, AuthClient):
|
|
|
201
219
|
attributes={"values": vals},
|
|
202
220
|
)
|
|
203
221
|
add_fiat = await self._post("/p2p/public-api/v3/payment-details/create", fiat_in.model_dump())
|
|
204
|
-
|
|
222
|
+
CredEpyd(**add_fiat["data"])
|
|
205
223
|
cred, _ = await Cred.update_or_create(
|
|
206
224
|
{"name": fiat.name, "detail": fiat.detail}, ex=self.ex_client.ex, exid=add_fiat["data"]["id"]
|
|
207
225
|
)
|
|
@@ -246,10 +264,22 @@ class AgentClient(BaseAgentClient, AuthClient):
|
|
|
246
264
|
del_fiat = await self._post("/p2p/public-api/v3/payment-details/delete", {"id": fiat_id})
|
|
247
265
|
return del_fiat
|
|
248
266
|
|
|
267
|
+
async def ad_epyd2pydin(self, ad_: OneAdTakerMakerSale | OneAdMakerBuy | OneAdTakerBuy) -> AdBuyPydIn | AdSalePydIn:
|
|
268
|
+
ad_in: BaseAdPydIn = await self.ex_client.ad_common_epyd2pydin(ad_)
|
|
269
|
+
ad_in.maker = self.agent.contragent
|
|
270
|
+
if isinstance(ad_, _PmsTrait):
|
|
271
|
+
return AdBuyPydIn(
|
|
272
|
+
**ad_in.model_dump(),
|
|
273
|
+
pms_=await Pm.filter(pmexs__ex=self.ex_client.ex, pmexs__exid__in=[p.code for p in ad_.paymentMethods]),
|
|
274
|
+
)
|
|
275
|
+
creds_pin: list[CredPydIn] = [await self.cred_epyd2pydin(c) for c in ad_.paymentDetails]
|
|
276
|
+
creds_ = [(await Cred.update_or_create((a := cp.args())[0], **a[1]))[0] for cp in creds_pin]
|
|
277
|
+
return AdSalePydIn(**ad_in.model_dump(), creds_=creds_)
|
|
278
|
+
|
|
249
279
|
# 29: Список моих объявлений
|
|
250
|
-
async def my_ads(self, status: AdStatus = None) -> list[
|
|
251
|
-
def model(ad: dict) -> (
|
|
252
|
-
return AdMakerSale if ad["type"] == "SALE" else
|
|
280
|
+
async def my_ads(self, status: AdStatus = None) -> list[AdMakerBuy | AdMakerSale]:
|
|
281
|
+
def model(ad: dict) -> (AdMakerBuy | AdMakerSale).__class__:
|
|
282
|
+
return AdMakerSale if ad["type"] == "SALE" else AdMakerBuy
|
|
253
283
|
|
|
254
284
|
mapping = {AdStatus.defActive: "INACTIVE", AdStatus.active: "ACTIVE"}
|
|
255
285
|
ads = await self._post(
|
|
@@ -259,18 +289,46 @@ class AgentClient(BaseAgentClient, AuthClient):
|
|
|
259
289
|
return [model(ad)(**ad) for ad in ads["data"] if not status or (status and ad["status"] == mapping[status])]
|
|
260
290
|
|
|
261
291
|
# 43: Моя объява по id
|
|
262
|
-
async def my_ad(self, ad_id: int) ->
|
|
292
|
+
async def my_ad(self, ad_id: int) -> OneAdMakerBuy | OneAdTakerMakerSale:
|
|
263
293
|
ad = await self._post("/p2p/public-api/v2/offer/get-user-own", {"offerId": ad_id})
|
|
264
294
|
ad: dict = ad["data"]
|
|
265
295
|
assert ad["user"]["userId"] == self.agent.contragent.exid, "Not your ad"
|
|
266
|
-
model = OneAdTakerMakerSale if ad["type"] == "SALE" else
|
|
296
|
+
model = OneAdTakerMakerSale if ad["type"] == "SALE" else OneAdMakerBuy
|
|
267
297
|
return model(**ad)
|
|
268
298
|
|
|
299
|
+
async def ad_f2ein(
|
|
300
|
+
self, coin: Coin, cur: Cur, is_sell: bool, vol: int = None, cur_min: int = None
|
|
301
|
+
) -> AdMakerNewSale | AdMakerNewBuy:
|
|
302
|
+
coinex = await Coinex.get(coin=coin, ex=self.ex)
|
|
303
|
+
curex = await Curex.get(ex=self.ex, cur=cur)
|
|
304
|
+
creds = await Cred.filter(contragent__agent__user_id=self.agent.user_id, pmcur__cur=cur).limit(5)
|
|
305
|
+
# todo: ordering and filtering by fiat.amount-target
|
|
306
|
+
ad_ein = _AdNew(
|
|
307
|
+
type="SALE" if is_sell else "PURCHASE",
|
|
308
|
+
initVolume=AvailableVolume(currencyCode=coinex.exid, amount=str(vol or coinex.minimum)),
|
|
309
|
+
orderRoundingRequired=curex.rounding_scale is not None,
|
|
310
|
+
price={
|
|
311
|
+
"type": "FLOATING",
|
|
312
|
+
"baseCurrencyCode": coinex.exid,
|
|
313
|
+
"quoteCurrencyCode": curex.exid,
|
|
314
|
+
"value": "120" if is_sell else "80",
|
|
315
|
+
},
|
|
316
|
+
orderAmountLimits={"min": str(cur_min or curex.minimum)},
|
|
317
|
+
paymentConfirmTimeout="PT15M",
|
|
318
|
+
comment="tst",
|
|
319
|
+
)
|
|
320
|
+
if ad_ein.type == "SALE":
|
|
321
|
+
ad_ein = AdMakerNewSale(**ad_ein.model_dump(exclude_none=True), paymentDetailsIds=[c.exid for c in creds])
|
|
322
|
+
else:
|
|
323
|
+
pmexs = await Pmex.filter(ex=self.agent.contragent.ex, pm__pmcurs__id__in=[c.pmcur_id for c in creds])
|
|
324
|
+
ad_ein = AdMakerNewBuy(**ad_ein.model_dump(exclude_none=True), paymentMethodCodes=[p.exid for p in pmexs])
|
|
325
|
+
return ad_ein
|
|
326
|
+
|
|
269
327
|
# 30: Создание объявления
|
|
270
|
-
async def ad_new(self, ad: _AdNew) ->
|
|
328
|
+
async def ad_new(self, ad: _AdNew) -> OneAdMakerBuy | OneAdTakerMakerSale:
|
|
271
329
|
create = await self._post("/p2p/public-api/v2/offer/create", ad.model_dump())
|
|
272
330
|
if res := create.get("data"):
|
|
273
|
-
return OneAdTakerMakerSale(**res) if res["type"] == "SALE" else
|
|
331
|
+
return OneAdTakerMakerSale(**res) if res["type"] == "SALE" else OneAdMakerBuy(**res)
|
|
274
332
|
raise Exception(create)
|
|
275
333
|
|
|
276
334
|
# 31: Редактирование объявления
|
|
@@ -312,7 +370,7 @@ class AgentClient(BaseAgentClient, AuthClient):
|
|
|
312
370
|
|
|
313
371
|
# 33: Вкл/выкл объявления
|
|
314
372
|
async def ad_switch(self, ad_id: int, active: bool) -> bool:
|
|
315
|
-
ad:
|
|
373
|
+
ad: OneAdMakerBuy | OneAdTakerMakerSale = await self.my_ad(ad_id)
|
|
316
374
|
pre = "" if active else "de"
|
|
317
375
|
switch = await self._post(f"/p2p/public-api/v2/offer/{pre}activate", {"type": ad.type, "offerId": ad_id})
|
|
318
376
|
return switch["status"] == "SUCCESS"
|
|
@@ -353,50 +411,40 @@ class AgentClient(BaseAgentClient, AuthClient):
|
|
|
353
411
|
|
|
354
412
|
async def main():
|
|
355
413
|
await init_db(PG_DSN, models, True)
|
|
356
|
-
|
|
357
|
-
|
|
414
|
+
maker: Agent
|
|
415
|
+
taker: Agent
|
|
416
|
+
maker, taker = (
|
|
417
|
+
await Agent.filter(contragent__ex_id=34, auth__isnull=False, user__status__gte=UserStatus.MEMBER)
|
|
418
|
+
.order_by("user_id")
|
|
419
|
+
.limit(2)
|
|
420
|
+
.prefetch_related("contragent__ex")
|
|
358
421
|
)
|
|
359
|
-
maker, taker = agents
|
|
360
422
|
mcl: AgentClient = maker.client()
|
|
361
423
|
tcl: AgentClient = taker.client()
|
|
362
|
-
#
|
|
363
|
-
#
|
|
364
|
-
#
|
|
365
|
-
#
|
|
366
|
-
# await mcl.
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
)
|
|
387
|
-
if ad_ein.type == "SALE":
|
|
388
|
-
mfiatexids = [(await Cred.get(ex=maker.ex, fiat=mf)).exid for mf in mfs]
|
|
389
|
-
ad_ein.paymentDetailsIds = mfiatexids[:1]
|
|
390
|
-
else:
|
|
391
|
-
pmexsids = [(await Pmex.get(ex=maker.ex, pm__pmcurs__id=mf.pmcur_id)).exid for mf in mfs]
|
|
392
|
-
ad_ein.paymentMethodCodes = pmexsids[:1]
|
|
393
|
-
|
|
394
|
-
mad = await mcl.ad_new(ad_ein)
|
|
395
|
-
await mcl.ad_switch(mad.id, True)
|
|
396
|
-
await tcl.order_request(mad.id, float(mad.orderAmountLimits.min))
|
|
397
|
-
ad_in = await mcl.ex_client.ad_epyd2pydin(mad)
|
|
398
|
-
ad_db = await Ad.create(**ad_in.model_dump(exclude_none=True))
|
|
399
|
-
print(ad_db)
|
|
424
|
+
# fiats = await tcl.set_fiats()
|
|
425
|
+
# fiats = await mcl.set_fiats()
|
|
426
|
+
# my_ads = await mcl.my_ads()
|
|
427
|
+
# my_ads_in = [await mcl.ad_epyd2pydin(ma) for ma in my_ads]
|
|
428
|
+
# _my_ads_db = [await mcl.ad_pydin2db(ma) for ma in my_ads_in]
|
|
429
|
+
|
|
430
|
+
coin = await Coin.get(ticker="USDT")
|
|
431
|
+
cur = await Cur.get(ticker="RUB")
|
|
432
|
+
ad_sell_ein = await tcl.ad_f2ein(coin, cur, True)
|
|
433
|
+
ad_buy_ein = await mcl.ad_f2ein(coin, cur, False)
|
|
434
|
+
sad = await tcl.ad_new(ad_sell_ein)
|
|
435
|
+
bad = await mcl.ad_new(ad_buy_ein)
|
|
436
|
+
await tcl.ad_switch(sad.id, True)
|
|
437
|
+
await mcl.ad_switch(bad.id, True)
|
|
438
|
+
sad_in = await tcl.ad_epyd2pydin(sad)
|
|
439
|
+
await Ad.create(**sad_in.model_dump(exclude_none=True))
|
|
440
|
+
bad_in = await mcl.ad_epyd2pydin(bad)
|
|
441
|
+
await Ad.create(**bad_in.model_dump(exclude_none=True))
|
|
442
|
+
|
|
443
|
+
# order_epin: OrderEpydIn = await tcl.order_ad2epydin(ad_db, float(mad.orderAmountLimits.min), cred_ids[0])
|
|
444
|
+
# new_order: OrderEpyd = await tcl.order_request(order_epin)
|
|
445
|
+
# order_pin: OrderPydIn = await tcl.order_epyd2pydin(new_order)
|
|
446
|
+
# _order_db = await tcl.order_pydin2db(order_pin)
|
|
447
|
+
|
|
400
448
|
await tcl.close(), await mcl.close()
|
|
401
449
|
|
|
402
450
|
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
from x_client.http import Client as HttpClient
|
|
2
|
+
from xync_schema.models import Agent
|
|
2
3
|
|
|
3
4
|
from xync_client.Abc.AuthTrait import BaseAuthTrait
|
|
5
|
+
from xync_client.Abc.Base import BaseClient
|
|
4
6
|
from xync_client.TgWallet.pyro import PyroClient
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
class AuthClient(BaseAuthTrait):
|
|
9
|
+
class AuthClient(BaseAuthTrait, BaseClient):
|
|
8
10
|
async def _get_auth_hdrs(self) -> dict[str, str]:
|
|
11
|
+
if not self.agent:
|
|
12
|
+
self.agent = (
|
|
13
|
+
await Agent.filter(contragent__ex=self.ex, auth__isnull=False).prefetch_related("contragent").first()
|
|
14
|
+
)
|
|
9
15
|
pyro = PyroClient(self.agent)
|
|
10
16
|
init_data = await pyro.get_init_data()
|
|
11
17
|
tokens = HttpClient("walletbot.me")._post("/api/v1/users/auth/", init_data)
|
|
12
|
-
self.agent.exid = tokens["user_id"]
|
|
13
|
-
await self.agent.save()
|
|
18
|
+
self.agent.contragent.exid = tokens["user_id"]
|
|
19
|
+
await self.agent.contragent.save()
|
|
14
20
|
pref = "" if self.__class__.__name__ == "AssetClient" else "Bearer "
|
|
15
21
|
return {"Wallet-Authorization": tokens["jwt"], "Authorization": pref + tokens["value"]}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
from asyncio import run
|
|
2
2
|
|
|
3
3
|
from x_model import init_db
|
|
4
|
-
from xync_schema.pydantic import
|
|
4
|
+
from xync_schema.pydantic import PmPyd, PmexBankPyd, CurEpyd, CoinEpyd, BaseAdPydIn, AdBuyPydIn
|
|
5
5
|
|
|
6
6
|
from xync_schema import models
|
|
7
|
-
from xync_schema.models import Ex,
|
|
7
|
+
from xync_schema.models import Ex, Direction, Pair, Coin, Cur, Pm, Contragent, PairEx
|
|
8
8
|
|
|
9
9
|
from xync_client.TgWallet.pyd import (
|
|
10
10
|
PmEpydRoot,
|
|
11
|
-
_BaseAd,
|
|
12
11
|
OneAdTakerMakerSale,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
_UserTrait,
|
|
12
|
+
OneAdTakerBuy,
|
|
13
|
+
AdTakerSaleBuy,
|
|
16
14
|
_TakerOne,
|
|
15
|
+
_PmsTrait,
|
|
16
|
+
_BaseAd,
|
|
17
17
|
)
|
|
18
18
|
from xync_client.loader import PG_DSN
|
|
19
19
|
from xync_client.Abc.Ex import BaseExClient
|
|
@@ -22,12 +22,8 @@ from xync_client.TgWallet.auth import AuthClient
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class ExClient(BaseExClient, AuthClient):
|
|
25
|
-
def __init__(self, ex: Ex
|
|
26
|
-
|
|
27
|
-
# ex should be with fetched .agents
|
|
28
|
-
agent = [ctg for ctg in ex.contragents if ctg.agent and ctg.agent.auth][0].agent
|
|
29
|
-
self.agent: Agent = agent # need for AuthTrait
|
|
30
|
-
super().__init__(ex) # , "host_p2p"
|
|
25
|
+
def __init__(self, ex: Ex):
|
|
26
|
+
super().__init__(ex) # BaseExClient
|
|
31
27
|
|
|
32
28
|
def pm_type_map(self, pm: Pm) -> str:
|
|
33
29
|
return "V2" if pm.name.startswith("SBP") else "V1"
|
|
@@ -92,13 +88,13 @@ class ExClient(BaseExClient, AuthClient):
|
|
|
92
88
|
async def ad(self, ad_id: int) -> _TakerOne:
|
|
93
89
|
ad = await self._post("/p2p/public-api/v2/offer/get", {"offerId": ad_id})
|
|
94
90
|
ad: dict = ad["data"]
|
|
95
|
-
model = OneAdTakerMakerSale if ad["type"] == "SALE" else
|
|
91
|
+
model = OneAdTakerMakerSale if ad["type"] == "SALE" else OneAdTakerBuy
|
|
96
92
|
return model(**ad)
|
|
97
93
|
|
|
98
94
|
# 24: Список объяв по (buy/sell, cur, coin, pm)
|
|
99
95
|
async def ads(
|
|
100
96
|
self, coin_exid: str, cur_exid: str, is_sell: bool, pm_exids: list[str | int] = None, amount: int = None
|
|
101
|
-
) -> list[
|
|
97
|
+
) -> list[AdTakerSaleBuy]:
|
|
102
98
|
params = {
|
|
103
99
|
"baseCurrencyCode": coin_exid,
|
|
104
100
|
"quoteCurrencyCode": cur_exid,
|
|
@@ -108,46 +104,70 @@ class ExClient(BaseExClient, AuthClient):
|
|
|
108
104
|
# "merchantVerified": "TRUSTED"
|
|
109
105
|
}
|
|
110
106
|
ads = await self._post("/p2p/public-api/v2/offer/depth-of-market/", params, "data")
|
|
111
|
-
return [
|
|
107
|
+
return [AdTakerSaleBuy(**ad) for ad in ads]
|
|
112
108
|
|
|
113
|
-
async def
|
|
109
|
+
async def ad_common_epyd2pydin(self, ad: _BaseAd) -> BaseAdPydIn:
|
|
114
110
|
coin = await Coin.get_or_create_by_name(ad.price.baseCurrencyCode)
|
|
115
111
|
cur = await Cur.get_or_create_by_name(ad.price.quoteCurrencyCode)
|
|
116
|
-
pair, _ = await Pair.get_or_create(coin=coin, cur=cur
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
pair, _ = await Pair.get_or_create(coin=coin, cur=cur)
|
|
113
|
+
pairex, _ = await PairEx.get_or_create(pair=pair, ex=self.ex)
|
|
114
|
+
dr, _ = await Direction.get_or_create(pairex=pairex, sell=ad.is_sell)
|
|
115
|
+
return BaseAdPydIn(
|
|
116
|
+
exid=ad.id,
|
|
121
117
|
price=ad.price.value,
|
|
122
118
|
min_fiat=ad.orderAmountLimits.min,
|
|
123
119
|
max_fiat=ad.orderAmountLimits.max,
|
|
124
120
|
direction=dr,
|
|
125
121
|
detail=getattr(ad, "comment", None),
|
|
126
|
-
maker=maker if isinstance(ad, _UserTrait) else self.agent.contragent,
|
|
127
|
-
# todo: maybe later adpm_banks
|
|
128
122
|
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
123
|
+
|
|
124
|
+
async def ad_taker_epyd2pydin(self, ad: _TakerOne) -> AdBuyPydIn:
|
|
125
|
+
adx: BaseAdPydIn = await self.ad_common_epyd2pydin(ad)
|
|
126
|
+
adx.maker = (await Contragent.update_or_create({"name": ad.user.nickname}, ex=self.ex, exid=ad.user.userId))[0]
|
|
127
|
+
pms = ad.paymentMethods if isinstance(ad, _PmsTrait) else [pd.paymentMethod for pd in ad.paymentDetails]
|
|
128
|
+
return AdBuyPydIn(
|
|
129
|
+
**adx.model_dump(), pms_=await Pm.filter(pmexs__ex=self.ex, pmexs__exid__in=[p.code for p in pms])
|
|
130
|
+
)
|
|
134
131
|
|
|
135
132
|
|
|
136
133
|
async def _test():
|
|
137
134
|
await init_db(PG_DSN, models, True)
|
|
138
|
-
tgex = await Ex.get(name="TgWallet")
|
|
135
|
+
tgex = await Ex.get(name="TgWallet")
|
|
139
136
|
cl: ExClient = tgex.client()
|
|
140
|
-
|
|
141
|
-
await cl.set_pmcurexs()
|
|
142
|
-
await cl.set_coinexs()
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
await cl.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
await cl.ad_pydin2db(
|
|
137
|
+
|
|
138
|
+
# await cl.set_pmcurexs()
|
|
139
|
+
# await cl.set_coinexs()
|
|
140
|
+
|
|
141
|
+
# # # SALE # # #
|
|
142
|
+
# get ads list
|
|
143
|
+
ads: list[AdTakerSaleBuy] = await cl.ads("TON", "RUB", True)
|
|
144
|
+
# prepare ad list items for saving
|
|
145
|
+
ads_pydin: list[BaseAdPydIn] = [await cl.ad_taker_epyd2pydin(adp) for adp in ads]
|
|
146
|
+
# list items save
|
|
147
|
+
_ads_db = [await cl.ad_pydin2db(adi) for adi in ads_pydin]
|
|
148
|
+
|
|
149
|
+
# # get ad fulls
|
|
150
|
+
# ads_pyd: list[_TakerOne] = [await cl.ad(ad.id) for ad in ads]
|
|
151
|
+
# # prepare ad fulls for saving
|
|
152
|
+
# ads_pydin: list[AdBuyPydIn] = [await cl.ad_taker_epyd2pydin(adp) for adp in ads_pyd]
|
|
153
|
+
# # full ones save
|
|
154
|
+
# _ads_db = [await cl.ad_pydin2db(adi) for adi in ads_pydin]
|
|
155
|
+
|
|
156
|
+
# # # BUY # # #
|
|
157
|
+
# get ads list
|
|
158
|
+
ads: list[AdTakerSaleBuy] = await cl.ads("TON", "RUB", False)
|
|
159
|
+
# prepare ad list items for saving
|
|
160
|
+
ads_pydin: list[BaseAdPydIn] = [await cl.ad_taker_epyd2pydin(adp) for adp in ads]
|
|
161
|
+
# list items save
|
|
162
|
+
_ads_db = [await cl.ad_pydin2db(adi) for adi in ads_pydin]
|
|
163
|
+
|
|
164
|
+
# # get ad fulls
|
|
165
|
+
# ads_pyd = [await cl.ad(ad.id) for ad in ads]
|
|
166
|
+
# # prepare ad fulls for saving
|
|
167
|
+
# ads_pydin: list[AdBuyPydIn] = [await cl.ad_taker_epyd2pydin(adp) for adp in ads_pyd]
|
|
168
|
+
# # full ones save
|
|
169
|
+
# _ads_db = [await cl.ad_pydin2db(adi) for adi in ads_pydin]
|
|
170
|
+
|
|
151
171
|
await cl.close()
|
|
152
172
|
|
|
153
173
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Literal
|
|
2
2
|
from pydantic import BaseModel, computed_field
|
|
3
|
-
from xync_schema.pydantic import BaseAd
|
|
3
|
+
from xync_schema.pydantic import BaseAd, BaseOrder
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
# Модели для вложенных структур
|
|
@@ -44,7 +44,7 @@ class OrderLimits(OrderLimitsIn):
|
|
|
44
44
|
approximate: bool
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
Status = Literal["ACTIVE", "INACTIVE", "ACTIVATING", "DEACTIVATING"]
|
|
47
|
+
Status = Literal["ACTIVE", "INACTIVE", "ACTIVATING", "DEACTIVATING", "UPDATING"]
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
class ChangeLogItem(BaseModel):
|
|
@@ -120,7 +120,7 @@ class FiatEpydIn(BaseModel):
|
|
|
120
120
|
attributes: Attrs | AttrsV2In
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
class
|
|
123
|
+
class CredEpyd(BaseModel):
|
|
124
124
|
id: int
|
|
125
125
|
userId: int
|
|
126
126
|
paymentMethod: PmEpydRoot
|
|
@@ -134,16 +134,19 @@ class InitVolume(BaseModel):
|
|
|
134
134
|
amount: str # of asset
|
|
135
135
|
|
|
136
136
|
|
|
137
|
+
AdType = Literal["PURCHASE", "SALE"]
|
|
138
|
+
|
|
139
|
+
|
|
137
140
|
# Основные модели
|
|
138
141
|
class _BaseInOutAd(BaseAd):
|
|
139
|
-
type:
|
|
142
|
+
type: AdType
|
|
140
143
|
price: Price
|
|
141
144
|
|
|
142
145
|
|
|
143
146
|
class _BaseAd(_BaseInOutAd):
|
|
144
147
|
number: str
|
|
145
|
-
orderAmountLimits: OrderLimits
|
|
146
|
-
orderVolumeLimits: OrderLimits
|
|
148
|
+
orderAmountLimits: OrderLimits # fiat
|
|
149
|
+
orderVolumeLimits: OrderLimits # asset
|
|
147
150
|
availableVolume: AvailableVolume
|
|
148
151
|
|
|
149
152
|
@computed_field
|
|
@@ -165,38 +168,41 @@ class _UserTrait:
|
|
|
165
168
|
user: User # taker | One
|
|
166
169
|
|
|
167
170
|
|
|
171
|
+
PaymentConfirmTimeout = Literal["PT3M", "PT15M", "PT30M", "PT45M", "PT1H", "PT2H", "PT3H"]
|
|
172
|
+
|
|
173
|
+
|
|
168
174
|
class _OneTrait(_StatusTrait, _UserTrait):
|
|
169
175
|
comment: str
|
|
170
176
|
changeLog: ChangeLog
|
|
171
177
|
createDateTime: str
|
|
172
|
-
paymentConfirmTimeout:
|
|
178
|
+
paymentConfirmTimeout: PaymentConfirmTimeout
|
|
173
179
|
|
|
174
180
|
|
|
175
181
|
class _TakerOne(_BaseAd, _OneTrait):
|
|
176
|
-
orderConfirmationTimeout: Literal["PT3M", "PT15M"]
|
|
182
|
+
orderConfirmationTimeout: Literal["PT1M30S", "PT3M", "PT15M"]
|
|
177
183
|
orderAcceptTimeout: Literal["PT10M"]
|
|
178
184
|
|
|
179
185
|
|
|
180
|
-
class
|
|
186
|
+
class AdTakerSaleBuy(_BaseAd, _PmsTrait, _UserTrait):
|
|
181
187
|
availableVolume: str
|
|
182
188
|
|
|
183
189
|
|
|
184
190
|
class OneAdTakerMakerSale(_TakerOne):
|
|
185
|
-
paymentDetails: list[
|
|
191
|
+
paymentDetails: list[CredEpyd]
|
|
186
192
|
fee: Fee
|
|
187
193
|
|
|
188
194
|
|
|
189
|
-
class
|
|
195
|
+
class OneAdTakerBuy(_TakerOne, _PmsTrait): ...
|
|
190
196
|
|
|
191
197
|
|
|
192
|
-
class
|
|
198
|
+
class AdMakerBuy(_BaseAd, _PmsTrait, _StatusTrait): ...
|
|
193
199
|
|
|
194
200
|
|
|
195
|
-
class
|
|
201
|
+
class OneAdMakerBuy(_BaseAd, _OneTrait, _PmsTrait): ...
|
|
196
202
|
|
|
197
203
|
|
|
198
204
|
class AdMakerSale(_BaseAd, _StatusTrait):
|
|
199
|
-
paymentDetails: list[
|
|
205
|
+
paymentDetails: list[CredEpyd]
|
|
200
206
|
|
|
201
207
|
|
|
202
208
|
# # #
|
|
@@ -206,9 +212,9 @@ class AdMakerSale(_BaseAd, _StatusTrait):
|
|
|
206
212
|
class _BaseIn(_BaseInOutAd):
|
|
207
213
|
orderRoundingRequired: bool
|
|
208
214
|
orderAmountLimits: OrderLimitsIn
|
|
209
|
-
paymentConfirmTimeout:
|
|
215
|
+
paymentConfirmTimeout: PaymentConfirmTimeout
|
|
210
216
|
comment: str
|
|
211
|
-
takerFilter: TakerFilter
|
|
217
|
+
takerFilter: TakerFilter | None = None
|
|
212
218
|
|
|
213
219
|
|
|
214
220
|
class _AdNew(_BaseIn):
|
|
@@ -224,7 +230,7 @@ class _SaleInTrait:
|
|
|
224
230
|
paymentDetailsIds: list[int]
|
|
225
231
|
|
|
226
232
|
|
|
227
|
-
class
|
|
233
|
+
class _BuyInTrait:
|
|
228
234
|
paymentMethodCodes: list[str]
|
|
229
235
|
|
|
230
236
|
|
|
@@ -234,77 +240,46 @@ class AdMakerNewSale(_AdNew, _SaleInTrait): ...
|
|
|
234
240
|
class AdMakerUpdSale(_AdUpd, _SaleInTrait): ...
|
|
235
241
|
|
|
236
242
|
|
|
237
|
-
class
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
class
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
# user: User
|
|
281
|
-
# orderConfirmationTimeout: Literal["PT3M", "PT15M"] | None = None # purchase?
|
|
282
|
-
# orderAcceptTimeout: Literal["PT10M"] | None = None # TAKER
|
|
283
|
-
#
|
|
284
|
-
# @classmethod
|
|
285
|
-
# @model_validator(mode="before")
|
|
286
|
-
# def check_at_least_one_field(cls, values):
|
|
287
|
-
# if values.get("paymentMethods") or values.get("paymentDetails"):
|
|
288
|
-
# return values
|
|
289
|
-
# raise ValueError("paymentMethods or paymentDetails is required")
|
|
290
|
-
#
|
|
291
|
-
#
|
|
292
|
-
# class MyAdEpyd(_BaseAd):
|
|
293
|
-
# availableVolume: AvailableVolume # of asset
|
|
294
|
-
# status: Status
|
|
295
|
-
#
|
|
296
|
-
#
|
|
297
|
-
# class _AllAdFullTrait:
|
|
298
|
-
# createDateTime: str
|
|
299
|
-
# changeLog: ChangeLog
|
|
300
|
-
# takerFilter: TakerFilter
|
|
301
|
-
# fee: Fee | None = None
|
|
302
|
-
# comment: str
|
|
303
|
-
#
|
|
304
|
-
#
|
|
305
|
-
# class MyAdFullEpyd(MyAdEpyd, _AllAdFullTrait): ...
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
# class AdFullEpyd(AdEpyd, _AllAdFullTrait):
|
|
309
|
-
# availableVolume: AvailableVolume # of asset
|
|
310
|
-
# status: Status
|
|
243
|
+
class AdMakerNewBuy(_AdNew, _BuyInTrait): ...
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class AdMakerUpdBuy(_AdUpd, _BuyInTrait): ...
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
OrderStatus = Literal["ACTIVE", "DRAFT"]
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class OrderEpydIn(BaseOrder):
|
|
253
|
+
offerId: int
|
|
254
|
+
type: AdType
|
|
255
|
+
paymentDetailsId: int
|
|
256
|
+
amount: AvailableVolume
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class OrderEpyd(BaseModel):
|
|
260
|
+
id: int
|
|
261
|
+
number: str
|
|
262
|
+
seller: User
|
|
263
|
+
buyer: User
|
|
264
|
+
offerId: int
|
|
265
|
+
offerType: AdType
|
|
266
|
+
offerComment: str = ""
|
|
267
|
+
isExpress: bool
|
|
268
|
+
price: Price
|
|
269
|
+
paymentDetails: CredEpyd
|
|
270
|
+
volume: AvailableVolume
|
|
271
|
+
amount: AvailableVolume
|
|
272
|
+
feeVolume: AvailableVolume
|
|
273
|
+
paymentConfirmTimeout: PaymentConfirmTimeout
|
|
274
|
+
buyerSendingPaymentConfirmationTimeout: Literal["PT15M"]
|
|
275
|
+
createDateTime: str
|
|
276
|
+
statusUpdateDateTime: str
|
|
277
|
+
holdRestrictionsWillBeApplied: bool
|
|
278
|
+
status: OrderStatus
|
|
279
|
+
changeLog: ChangeLog
|
|
280
|
+
isAutoAccept: bool
|
|
281
|
+
|
|
282
|
+
@computed_field
|
|
283
|
+
@property
|
|
284
|
+
def is_sell(self) -> bool:
|
|
285
|
+
return self.offerType == "SALE"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Binance/test_binance.py
RENAMED
|
File without changes
|
{xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Bybit/test_bybit.py
RENAMED
|
File without changes
|
{xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Bybit/test_bybit_p2p.py
RENAMED
|
File without changes
|
|
File without changes
|
{xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Htx/test_htx_p2p.py
RENAMED
|
File without changes
|
{xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/tests/_todo_refact/Wallet/test_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xync_client-0.0.25.dev11 → xync_client-0.0.25.dev19}/xync_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|