Skip to content

Commit 1279243

Browse files
committed
add demo application
1 parent 0037310 commit 1279243

8 files changed

Lines changed: 422 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ except ApiException as e:
9898

9999
```
100100

101+
For a more complete API usage example, refer to the demo application in [example](example) directory
102+
101103
## Documentation for API Endpoints
102104

103105
All URIs are relative to *https://api.gateio.ws/api/v4*

example/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Example Application
2+
3+
This is a demo application using `gate-api` to show how Gate APIv4 works.
4+
Instead of running it, it is recommended to read the source code to get a general idea of
5+
how this SDK is used. However, you can modify this code directly to implement your own logic.
6+
7+
## Build
8+
9+
1. Clone this project and make sure it is named with `gateapi-python`
10+
2. Run `./build.sh`, then your demo application will be created beside `gateapi-python`
11+
12+
## Run
13+
14+
**READ THIS BEFORE YOU RUN ANYTHING**
15+
16+
**This application is shown for demo only. It will try to use your input API key and secret to
17+
trade, lend and borrow, etc. Make sure you know exactly what it does before running it.**
18+
19+
> The build.sh script will try to initiate a virtualenv environment if it can find virtualenv
20+
> executable. Follow what the script prints before running the demo application
21+
22+
```bash
23+
# run futures demo against TestNet
24+
python app.py futures -k <YOUR_TESTNET_API_KEY> -s <YOUR_TESTNET_API_SECRET> -u fx-api-testnet.gateio.ws
25+
26+
# run futures demo against real trading
27+
python app.py futures -k <YOUR_API_KEY> -s <YOUR_API_SECRET>
28+
29+
# run spot demo
30+
python app.py spot -k <YOUR_API_KEY> -s <YOUR_API_SECRET>
31+
32+
# run margin demo
33+
python app.py margin -k <YOUR_API_KEY> -s <YOUR_API_SECRET>
34+
```

example/app.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# !/usr/bin/env python
2+
# coding: utf-8
3+
import logging
4+
from argparse import ArgumentParser
5+
6+
from config import RunConfig
7+
from futures import futures_demo
8+
from margin import margin_demo
9+
from spot import spot_demo
10+
11+
logging.basicConfig(format="%(asctime)s: %(message)s", level=logging.DEBUG)
12+
logger = logging.getLogger(__name__)
13+
14+
15+
def main():
16+
parser = ArgumentParser(description="Run Gate APIv4 demo application")
17+
parser.add_argument("-k", "--key", required=True, help="Gate APIv4 Key")
18+
parser.add_argument("-s", "--secret", required=True, help="Gate APIv4 Secret")
19+
parser.add_argument("-u", "--url", required=False, help="API base URL used to test")
20+
parser.add_argument("tests", nargs='+', help="tests to run")
21+
options = parser.parse_args()
22+
23+
host_used = options.url
24+
if not host_used:
25+
host_used = "https://api.gateio.ws/api/v4"
26+
if not host_used.startswith("http"):
27+
host_used = "https://" + host_used
28+
host_used = host_used.rstrip("/")
29+
if not host_used.endswith("/api/v4"):
30+
host_used += '/api/v4'
31+
32+
run_config = RunConfig(options.key, options.secret, host_used)
33+
for t in options.tests:
34+
logger.info("run %s API demo", t)
35+
if t == 'spot':
36+
spot_demo(run_config)
37+
elif t == 'margin':
38+
margin_demo(run_config)
39+
elif t == 'futures':
40+
futures_demo(run_config)
41+
else:
42+
logger.warning("ignore unknown test %s", t)
43+
44+
45+
if __name__ == '__main__':
46+
main()

example/build.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env sh
2+
3+
set -e
4+
5+
WORKDIR="${PWD}/gateapi-demo"
6+
VENV_DIR="$WORKDIR/.venv"
7+
: "${GATEAPI_SOURCE_DIR:=gateapi-python}"
8+
9+
# determine python environment
10+
python=$(command -v python3 || true)
11+
12+
if [ -z "$python" ]; then
13+
python=$(command -v python2 || true)
14+
if [ -z "$python" ]; then
15+
echo >&2 "No python executable found."
16+
exit 1
17+
fi
18+
fi
19+
20+
mkdir -p "$WORKDIR"
21+
22+
virtualenv=$(command -v virtualenv || true)
23+
if [ -z "$virtualenv" ]; then
24+
echo "No virtualenv found. Native python environment will be used"
25+
LOCAL_INSTALL="--user"
26+
else
27+
if [ -n "$VIRTUAL_ENV" ]; then
28+
# find original python path, osx compatible
29+
while true; do
30+
orig=$(readlink "$python")
31+
if [ -z "$orig" ]; then
32+
break
33+
elif [ "$orig" != "${orig#/}" ]; then
34+
python=$orig
35+
break
36+
else
37+
python="$(dirname "${python}")"/"$orig"
38+
fi
39+
done
40+
fi
41+
if [ ! -d "$VENV_DIR" ]; then
42+
"$virtualenv" -p "$python" "$VENV_DIR"
43+
fi
44+
python="$VENV_DIR/bin/python"
45+
fi
46+
47+
echo "Python used: $python"
48+
49+
cd "$GATEAPI_SOURCE_DIR" && "$python" setup.py install ${LOCAL_INSTALL+"$LOCAL_INSTALL"} && cd -
50+
cp "$GATEAPI_SOURCE_DIR"/example/*.py "$WORKDIR"
51+
52+
if [ -n "$virtualenv" ]; then
53+
echo "run \`source $VENV_DIR/bin/activate \` and then "
54+
fi
55+
echo "run \`cd $WORKDIR && $python app.py -h\` for help"

example/config.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# !/usr/bin/env python
2+
# coding: utf-8
3+
4+
from six.moves.urllib.parse import urlparse
5+
6+
7+
class RunConfig(object):
8+
9+
def __init__(self, api_key=None, api_secret=None, host_used=None):
10+
# type: (str, str, str) -> None
11+
self.api_key = api_key
12+
self.api_secret = api_secret
13+
self.host_used = host_used
14+
self.use_test = urlparse(host_used).hostname == "fx-api-testnet.gateio.ws"

example/futures.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# !/usr/bin/env python
2+
# coding: utf-8
3+
import logging
4+
import time
5+
from decimal import Decimal as D, ROUND_UP, getcontext
6+
7+
from gate_api import ApiClient, Configuration, FuturesApi, FuturesOrder, Transfer, WalletApi
8+
from gate_api.exceptions import GateApiException
9+
10+
from config import RunConfig
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
def futures_demo(run_config):
16+
# type: (RunConfig) -> None
17+
settle = "usdt"
18+
contract = "BTC_USDT"
19+
20+
# Initialize API client
21+
# Setting host is optional. It defaults to https://api.gateio.ws/api/v4
22+
config = Configuration(key=run_config.api_key, secret=run_config.api_secret, host=run_config.host_used)
23+
futures_api = FuturesApi(ApiClient(config))
24+
25+
# update position leverage
26+
leverage = "3"
27+
futures_api.update_position_leverage(settle, contract, leverage)
28+
29+
# retrieve position size
30+
position_size = 0
31+
try:
32+
position = futures_api.get_position(settle, contract)
33+
position_size = position.size
34+
except GateApiException as ex:
35+
if ex.label != "POSITION_NOT_FOUND":
36+
raise ex
37+
38+
# set order size
39+
futures_contract = futures_api.get_futures_contract(settle, contract)
40+
order_size = 10
41+
if futures_contract.order_size_min and futures_contract.order_size_min > order_size:
42+
order_size = futures_contract.order_size_min
43+
if position_size < 0:
44+
order_size = 0 - order_size
45+
46+
# example to update risk limit
47+
assert futures_contract.risk_limit_base
48+
assert futures_contract.risk_limit_step
49+
risk_limit = D(futures_contract.risk_limit_base) + D(futures_contract.risk_limit_step)
50+
futures_api.update_position_risk_limit(settle, contract, str(risk_limit))
51+
52+
# retrieve last price to calculate margin needed
53+
tickers = futures_api.list_futures_tickers(settle, contract=contract)
54+
assert len(tickers) == 1
55+
last_price = tickers[0].last
56+
logger.info("last price of contract %s: %s", contract, last_price)
57+
58+
getcontext().prec = 8
59+
getcontext().rounding = ROUND_UP
60+
61+
assert futures_contract.quanto_multiplier
62+
margin = order_size * D(last_price) * D(futures_contract.quanto_multiplier) / D(leverage) * D("1.1")
63+
logger.info("needs margin amount: %s", str(margin))
64+
65+
# if balance is not enough, transfer from spot account
66+
available = "0"
67+
try:
68+
futures_account = futures_api.list_futures_accounts(settle)
69+
available = futures_account.available
70+
except GateApiException as ex:
71+
if ex.label != "USER_NOT_FOUND":
72+
raise ex
73+
logger.info("futures account available: %s %s", available, settle.upper())
74+
if D(available) < margin:
75+
if run_config.use_test:
76+
logger.warning("testnet account balance not enough. make a transferal on web")
77+
return
78+
transfer = Transfer(amount=str(margin), currency=settle.upper(), _from='spot', to='futures')
79+
wallet_api = WalletApi(ApiClient(config))
80+
wallet_api.transfer(transfer)
81+
82+
# example to cancel all open orders in contract
83+
futures_api.cancel_futures_orders(settle, contract)
84+
85+
# order using market price
86+
order = FuturesOrder(contract=contract, size=order_size, price="0", tif='ioc')
87+
try:
88+
order_response = futures_api.create_futures_order(settle, order)
89+
except GateApiException as ex:
90+
logger.error("error encountered creating futures order: %s", ex)
91+
return
92+
logger.info("order %s created with status: %s", order_response.id, order_response.status)
93+
94+
if order_response.status == 'open':
95+
futures_order = futures_api.get_futures_order(settle, str(order_response.id))
96+
logger.info("order %s status %s, total size %s, left %s", futures_order.id, futures_order.status,
97+
futures_order.size, futures_order.left)
98+
futures_api.cancel_futures_order(settle, str(futures_order.id))
99+
logger.info("order %s cancelled", futures_order.id)
100+
else:
101+
time.sleep(0.2)
102+
order_trades = futures_api.get_my_trades(settle, contract=contract, order=order_response.id)
103+
assert len(order_trades) > 0
104+
trade_size = 0
105+
for t in order_trades:
106+
assert t.order_id == str(order_response.id)
107+
trade_size += t.size
108+
logger.info("order %s filled size %s with price %s", t.order_id, t.size, t.price)
109+
assert trade_size == order_size
110+
111+
# example to update position margin
112+
futures_api.update_position_margin(settle, contract, "0.01")

example/margin.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# !/usr/bin/env python
2+
# coding: utf-8
3+
import logging
4+
import random
5+
from decimal import Decimal as D, ROUND_UP, getcontext
6+
7+
from gate_api import ApiClient, Configuration, Loan, MarginApi, Order, RepayRequest, SpotApi, Transfer, WalletApi
8+
from gate_api.exceptions import GateApiException
9+
10+
from config import RunConfig
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
def margin_demo(run_config):
16+
# type: (RunConfig) -> None
17+
currency_pair = 'BTC_USDT'
18+
currency = currency_pair.split("_")[1]
19+
20+
# Initialize API client
21+
# Setting host is optional. It defaults to https://api.gateio.ws/api/v4
22+
config = Configuration(key=run_config.api_key, secret=run_config.api_secret, host=run_config.host_used)
23+
spot_api = SpotApi(ApiClient(config))
24+
margin_api = MarginApi(ApiClient(config))
25+
wallet_api = WalletApi(ApiClient(config))
26+
27+
# retrieve currency pair last price
28+
tickers = spot_api.list_tickers(currency_pair=currency_pair)
29+
assert len(tickers) == 1
30+
last_price = tickers[0].last
31+
logger.info("currency pair %s last price %s", currency_pair, last_price)
32+
33+
pairs = margin_api.list_margin_currency_pairs()
34+
pair = next(p for p in pairs if p.id == currency_pair)
35+
loan_amount = D("0") if not pair.min_quote_amount else D(pair.min_quote_amount)
36+
37+
if pair.min_base_amount:
38+
min_loan_amount = D(pair.min_base_amount) * D(last_price)
39+
if loan_amount < min_loan_amount:
40+
loan_amount = min_loan_amount
41+
logger.info("minimum loan amount in currency pair %s: %s %s", currency_pair, str(loan_amount), currency)
42+
43+
getcontext().prec = 8
44+
getcontext().rounding = ROUND_UP
45+
46+
# example to lend
47+
funding_accounts = margin_api.list_funding_accounts(currency=currency)
48+
lend_amount = loan_amount + D(random.random())
49+
if len(funding_accounts) == 1 and D(funding_accounts[0].available) >= lend_amount:
50+
lending_loan = Loan(amount=str(lend_amount), auto_renew=False, days=10, currency=currency, rate="0.002",
51+
side='lend')
52+
created_loan = margin_api.create_loan(lending_loan)
53+
logger.info("place a lending loan %s with currency %s, rate %s, amount %s", created_loan.id,
54+
created_loan.currency, created_loan.rate, created_loan.amount)
55+
loan_result = margin_api.get_loan(created_loan.id, 'lend')
56+
if loan_result.status == 'loaned':
57+
records = margin_api.list_loan_records(loan_result.id)
58+
for r in records:
59+
logger.info("loan %s is borrowed with record id %s, amount %s, status: %s", r.loan_id, r.id, r.amount,
60+
r.status)
61+
else:
62+
margin_api.cancel_loan(created_loan.id, currency)
63+
64+
assert pair.leverage
65+
margin = loan_amount / (pair.leverage - 1)
66+
accounts = margin_api.list_margin_accounts(currency_pair=currency_pair)
67+
assert len(accounts) == 1
68+
available = D(accounts[0].quote.available)
69+
logger.info("available margin balance of currency %s in currency pair %s: %s", currency, currency_pair,
70+
str(available))
71+
if margin > available:
72+
transfer = Transfer(currency_pair=currency_pair, currency=currency, amount=str(margin - available),
73+
_from='spot', to='margin')
74+
wallet_api.transfer(transfer)
75+
logger.info("transferred %s %s to margin account", transfer.amount, transfer.currency)
76+
77+
# borrow with minimum rate
78+
borrow_amount = loan_amount + D(random.random())
79+
min_rate_item = min(
80+
filter(lambda x: x.rate and D(x.amount) > borrow_amount, margin_api.list_funding_book(currency)),
81+
key=lambda x: D(x.rate)
82+
)
83+
loan = Loan(side='borrow', currency=currency, rate=min_rate_item.rate, amount=str(borrow_amount),
84+
days=min_rate_item.days, currency_pair=currency_pair)
85+
borrowed = margin_api.create_loan(loan)
86+
logger.info("borrowed %s %s in currency pair %s with rate %s, id %s", borrowed.amount, borrowed.currency,
87+
borrowed.currency_pair, borrowed.rate, borrowed.id)
88+
assert borrowed.status == 'loaned'
89+
90+
# create margin order
91+
order_amount = spot_api.get_currency_pair(currency_pair).min_quote_amount
92+
order = Order(account='margin', currency_pair=currency_pair, price=last_price, amount=order_amount or "1",
93+
side='sell')
94+
try:
95+
created_order = spot_api.create_order(order)
96+
logger.info("margin order created with id %s, status %s", created_order.id, created_order.status)
97+
except GateApiException as ex:
98+
logger.error("failed to create margin order: %s", ex)
99+
100+
repay_request = RepayRequest(mode='all', currency=currency, currency_pair=currency_pair)
101+
margin_api.repay_loan(borrowed.id, repay_request)
102+
for r in margin_api.list_loan_repayments(borrowed.id):
103+
logger.info("loan %s repaid %s with interest %s", borrowed.id, r.principal, r.interest)

0 commit comments

Comments
 (0)