Skip to content

Commit

Permalink
2.1.15
Browse files Browse the repository at this point in the history
  • Loading branch information
DogsTailFarmer committed Jul 12, 2024
1 parent dd50515 commit b4cc366
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.1.15 2024-07-12
### Added for new features
* `Binance`: add method `transfer_to_sub()`. See use example in `example/exch_client.py`

## 2.1.14 2024-07-07
### Fix
* `Bybit`: `fetch_ledgers()` doubling of incoming transfers to a subaccount
Expand Down
35 changes: 34 additions & 1 deletion example/exch_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
FILE_CONFIG = 'ms_cfg.toml'
config = toml.load(FILE_CONFIG)
EXCHANGE = config.get('exchange')
SYMBOL = 'BTCUSDT'
SYMBOL = 'BNBUSDT'


async def main(_exchange, _symbol):
Expand Down Expand Up @@ -63,8 +63,10 @@ async def main(_exchange, _symbol):
# Subscribe to WSS
# First you want to create all WSS task
# Market stream
# noinspection PyAsyncCall
asyncio.create_task(on_ticker_update(stub, client_id, _symbol, trade_id))
# User Stream
# noinspection PyAsyncCall
asyncio.create_task(on_order_update(stub, client_id, _symbol, trade_id))
# Other market and user methods are used similarly: OnKlinesUpdate, OnFundsUpdate, OnOrderBookUpdate
# Start WSS
Expand Down Expand Up @@ -336,6 +338,37 @@ async def transfer2master(_stub, symbol: str, amount: str):
print(f"Not sent {amount} {symbol} to main account\n,{res.result}")


async def transfer2sub(_stub, email: str, symbol: str, amount: str):
"""
Send request to transfer asset from subaccount to subaccount
Binance sub to sub only
:param _stub:
:param email:
:param symbol:
:param amount:
:return:
"""
try:
res = await _stub.transfer_to_sub(
mr.MarketRequest,
symbol=symbol,
amount=amount,
data=email
)
except asyncio.CancelledError:
pass # Task cancellation should not be logged as an error
except GRPCError as ex:
status_code = ex.status
print(f"Exception transfer {symbol} to sub account: {status_code.name}, {ex.message}")
except Exception as _ex:
print(f"Exception transfer {symbol} to sub account: {_ex}")
else:
if res.success:
print(f"Sent {amount} {symbol} to sub account {email}")
else:
print(f"Not sent {amount} {symbol} to sub account {email}\n,{res.result}")


# Server exception handling example for methods where it's realized
async def create_limit_order(_stub, _client_id, _symbol, _id: int, buy: bool, amount: str, price: str):
"""
Expand Down
2 changes: 1 addition & 1 deletion example/ms_cfg.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Example parameters for exchanges_wrapper/exch_client.py
# Accounts name wold be identically accounts.name from exch_srv_cfg.toml
# Accounts name would be identically accounts.name from exch_srv_cfg.toml
exchange = "Demo - Binance"
# exchange = "Demo - Bitfinex"
# exchange = "Demo - OKX"
2 changes: 1 addition & 1 deletion exchanges_wrapper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
__contact__ = "https://github.com/DogsTailFarmer"
__email__ = "[email protected]"
__credits__ = ["https://github.com/DanyaSWorlD"]
__version__ = "2.1.14"
__version__ = "2.1.15"

from pathlib import Path
import shutil
Expand Down
46 changes: 28 additions & 18 deletions exchanges_wrapper/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,8 @@ async def fetch_ticker_price_change_statistics(self, symbol=None):
# https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#symbol-price-ticker
async def fetch_symbol_price_ticker(self, symbol=None):
if symbol:
self.assert_symbol_exists(symbol)
if not (self.exchange == 'binance' and symbol == 'BNBUSDT'):
self.assert_symbol_exists(symbol)
binance_res = {}
elif self.exchange in ('bitfinex', 'huobi'):
raise ValueError('For fetch_symbol_price_ticker() symbol parameter required')
Expand Down Expand Up @@ -1603,25 +1604,34 @@ async def fetch_funding_wallet(self, asset=None, need_btc_valuation=None, receiv
binance_res = bbt.funding_wallet(res["balance"])
return binance_res

# https://developers.binance.com/docs/sub_account/asset-management/Transfer-to-Sub-account-of-Same-Master
async def transfer_to_sub(self, email, symbol, quantity, receive_window=None):
if self.exchange == 'binance':
quantity = any2str(Decimal(quantity).quantize(Decimal('0.01234567'), rounding=ROUND_HALF_DOWN))
params = {"toEmail": email, "asset": symbol, "amount": quantity}
if receive_window:
params["recvWindow"] = receive_window
return await self.http.send_api_call(
"/sapi/v1/sub-account/transfer/subToSub",
"POST",
signed=True,
params=params
)
else:
raise ValueError(f"Can't implemented for {self.exchange}")

# https://binance-docs.github.io/apidocs/spot/en/#transfer-to-master-for-sub-account
async def transfer_to_master(self, symbol, quantity, receive_window=None):
quantity = any2str(Decimal(quantity).quantize(Decimal('0.01234567'), rounding=ROUND_HALF_DOWN))

_quantity = any2str(Decimal(quantity).quantize(Decimal('0.01234567'), rounding=ROUND_HALF_DOWN))
binance_res = {}
if self.exchange == 'binance':
params = {"asset": symbol, "amount": quantity}
if receive_window:
params["recvWindow"] = receive_window
if self.master_email:
logger.info(f"Collect {quantity}{symbol} to {self.master_email} sub-account")
params["toEmail"] = self.master_email
binance_res = await self.http.send_api_call(
"/sapi/v1/sub-account/transfer/subToSub",
"POST",
signed=True,
params=params
)
logger.info(f"Collect {_quantity}{symbol} to {self.master_email} sub-account")
binance_res = await self.transfer_to_sub(self.master_email, symbol, quantity, receive_window)
else:
params = {"asset": symbol, "amount": _quantity}
if receive_window:
params["recvWindow"] = receive_window
binance_res = await self.http.send_api_call(
"/sapi/v1/sub-account/transfer/subToMaster",
"POST",
Expand All @@ -1636,7 +1646,7 @@ async def transfer_to_master(self, symbol, quantity, receive_window=None):
"from": "exchange",
"to": "exchange",
"currency": symbol,
"amount": quantity,
"amount": _quantity,
"email_dst": self.master_email,
"tfaToken": {"method": "otp", "token": totp.now()}
}
Expand All @@ -1658,7 +1668,7 @@ async def transfer_to_master(self, symbol, quantity, receive_window=None):
'to-account-type': "spot",
'to-account': self.main_account_id,
'currency': symbol.lower(),
'amount': quantity
'amount': _quantity
}
res = await self.http.send_api_call(
"v1/account/transfer",
Expand All @@ -1670,7 +1680,7 @@ async def transfer_to_master(self, symbol, quantity, receive_window=None):
elif self.exchange == 'okx':
params = {
"ccy": symbol,
"amt": quantity,
"amt": _quantity,
"from": '18',
"to": '18',
"type": '3'
Expand All @@ -1692,7 +1702,7 @@ async def transfer_to_master(self, symbol, quantity, receive_window=None):
params = {
'transferId': str(uuid.uuid4()),
'coin': symbol,
'amount': str(math.floor(float(quantity) * 10 ** n) / 10 ** n),
'amount': str(math.floor(float(_quantity) * 10 ** n) / 10 ** n),
'fromMemberId': self.account_uid,
'toMemberId': self.main_account_uid,
'fromAccountType': 'UNIFIED',
Expand Down
18 changes: 18 additions & 0 deletions exchanges_wrapper/exch_srv.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,24 @@ async def cancel_order(self, request: mr.CancelOrderRequest) -> mr.CancelOrderRe
response.from_pydict(res)
return response

async def transfer_to_sub(self, request: mr.MarketRequest) -> mr.SimpleResponse:
response = mr.SimpleResponse()
response.success = False

res, _, _ = await self.send_request(
'transfer_to_sub',
request,
rate_limit=True,
email=request.data,
symbol=request.symbol,
quantity=request.amount
)

if res and res.get("txnId"):
response.success = True
response.result = json.dumps(res)
return response

async def transfer_to_master(self, request: mr.MarketRequest) -> mr.SimpleResponse:
response = mr.SimpleResponse()
response.success = False
Expand Down
36 changes: 36 additions & 0 deletions exchanges_wrapper/martin/__init__.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions exchanges_wrapper/proto/martin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ service Martin {
rpc StartStream (StartStreamRequest) returns (SimpleResponse) {}
rpc StopStream (MarketRequest) returns (SimpleResponse) {}
rpc TransferToMaster(MarketRequest) returns (SimpleResponse) {}
rpc TransferToSub(MarketRequest) returns (SimpleResponse) {}
}

message JSONResponse {
Expand Down Expand Up @@ -368,6 +369,7 @@ message MarketRequest {
string trade_id = 2;
string symbol = 3;
string amount = 4;
string data = 5;
}

message StartStreamRequest {
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ dependencies = [
"pyotp~=2.9.0",
"simplejson==3.19.2",
"aiohttp==3.9.5",
"Pympler~=1.0.1",
"websockets~=12.0",
"expiringdict~=1.2.2",
"ujson~=5.10.0",
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pyotp==2.9.0
simplejson==3.19.2
toml~=0.10.2
aiohttp~=3.9.5
Pympler~=1.0.1
websockets==12.0
expiringdict~=1.2.2
ujson~=5.10.0
Expand Down

0 comments on commit b4cc366

Please sign in to comment.