# MOV做市商指南
# MOV-MMDK介绍
MOV Market Maker Develo pment Kit,基于MOV Server的RESTful API开发的Python SDK,提供获取市场深度、发送订单、取消订单、查询订单、查询账户余额等功能。
# MOV-MMDK安装
git clone https://github.com/Bytom/mov-mmdk
cd mov-mmdk/
pip3 install -r requirements.txt
python3 setup.py install
# 磁力兑换做市流程
磁力兑换是MOV协议下的核心产品之一,是将用户的资产通过合约的形式在链上完成挂单,撮合和成交的一种资产交易方式。通俗的说,磁力兑换实现的就是去中心化交易的功能,并且在去中心的同时具备了媲美中心化交易所的速度。
# 磁力做市架构
# 磁力做市原理
MOV Server只是提供订单与交易之间的相互转化,实际上所有的订单都是由用户签名后提交, 并最终由智能合约进行撮合的过程。 MOV Server提供了与中心化交易所接近一致的API接口,其交易时的API使用体验,基本与中心化交易所一致,但依然有一些不同,下面我们将会进行说明。
# 磁力做市教程
在代码中添加依赖,创建Api对象就可以调用MOV-MMDK提供的API参与磁力兑换做市商。
from mov_sdk.mov_api import MovApi
# 获取私钥并初始化
//通过助记词初始化
api = MovApi(secret_key="")
config = api.init_from_mnemonic("你的助记词")
print(api.main_address)
print(api.vapor_address)
print(api.public_key)
//通过私钥初始化
api = MovApi(secret_key="你的私钥")
print(api.main_address)
print(api.vapor_address)
print(api.public_key)
//以下两种方式会创建新地址并且初始化
api = MovApi("")
print(api.secret_key)
print(api.main_address)
print(api.vapor_address)
print(api.public_key)
api = MovApi("")
print(api.get_new_secret_key())
print(api.main_address)
print(api.vapor_address)
# 获得磁力交易对信息
返回MOV所有的交易对信息
>>> print(api.get_exchange_info())
{
'code': 200,
'msg': '',
'data': [{
'price_decimal': 4,
'amount_decimal': 2,
'base_asset': {
'asset_id': 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'decimals': 8,
'symbol': 'BTM'
},
'quote_asset': {
'asset_id': '184e1cc4ee4845023888810a79eed7a42c02c544cf2c61ceac05e176d575bd46',
'decimals': 6,
'symbol': 'USDT'
}
}, {
'price_decimal': 2,
'amount_decimal': 4,
'base_asset': {
'asset_id': '78de44ffa1bce37b757c9eae8925b5f199dc4621b412ef0f3f46168865284a93',
'decimals': 9,
'symbol': 'ETH'
},
'quote_asset': {
'asset_id': '184e1cc4ee4845023888810a79eed7a42c02c544cf2c61ceac05e176d575bd46',
'decimals': 6,
'symbol': 'USDT'
}
}, {
'price_decimal': 8,
'amount_decimal': 2,
'base_asset': {
'asset_id': 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'decimals': 8,
'symbol': 'BTM'
},
'quote_asset': {
'asset_id': '78de44ffa1bce37b757c9eae8925b5f199dc4621b412ef0f3f46168865284a93',
'decimals': 9,
'symbol': 'ETH'
}
}, {
'price_decimal': 8,
'amount_decimal': 2,
'base_asset': {
'asset_id': 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'decimals': 8,
'symbol': 'BTM'
},
'quote_asset': {
'asset_id': 'bda946b3110fa46fd94346ce3f05f0760f1b9de72e238835bc4d19f9d64f1742',
'decimals': 8,
'symbol': 'BTC'
}
}, {
'price_decimal': 2,
'amount_decimal': 4,
'base_asset': {
'asset_id': 'bda946b3110fa46fd94346ce3f05f0760f1b9de72e238835bc4d19f9d64f1742',
'decimals': 8,
'symbol': 'BTC'
},
'quote_asset': {
'asset_id': '184e1cc4ee4845023888810a79eed7a42c02c544cf2c61ceac05e176d575bd46',
'decimals': 6,
'symbol': 'USDT'
}
}, {
'price_decimal': 6,
'amount_decimal': 4,
'base_asset': {
'asset_id': '78de44ffa1bce37b757c9eae8925b5f199dc4621b412ef0f3f46168865284a93',
'decimals': 9,
'symbol': 'ETH'
},
'quote_asset': {
'asset_id': 'bda946b3110fa46fd94346ce3f05f0760f1b9de72e238835bc4d19f9d64f1742',
'decimals': 8,
'symbol': 'BTC'
}
}, {
'price_decimal': 2,
'amount_decimal': 8,
'base_asset': {
'asset_id': '47fcd4d7c22d1d38931a6cd7767156babbd5f05bbbb3f7d3900635b56eb1b67e',
'decimals': 8,
'symbol': 'SUP'
},
'quote_asset': {
'asset_id': 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'decimals': 8,
'symbol': 'BTM'
}
}, {
'price_decimal': 4,
'amount_decimal': 8,
'base_asset': {
'asset_id': '47fcd4d7c22d1d38931a6cd7767156babbd5f05bbbb3f7d3900635b56eb1b67e',
'decimals': 8,
'symbol': 'SUP'
},
'quote_asset': {
'asset_id': '184e1cc4ee4845023888810a79eed7a42c02c544cf2c61ceac05e176d575bd46',
'decimals': 6,
'symbol': 'USDT'
}
}]
}
# 获得磁力兑换深度
获取市场指定交易对的深度
>>> print(api.get_depth("BTC/USDT", 5))
{
'code': 200,
'msg': '',
'data': {
'symbol': 'BTC/USDT',
'fee_rate': '',
'asks': [
['17951.94', '0.0022'],
['17954.80', '0.2183'],
['17956.60', '0.0124'],
['17958.40', '0.0742'],
['17961.11', '0.1758']
],
'bids': [
['17908.50', '0.0317'],
['17903.69', '0.0428'],
['17901.90', '0.0184'],
['17900.11', '0.0166'],
['17898.32', '0.0011']
],
'timestamp': 1605754831534
}
}
# 发送磁力交易订单
调用SDK发送订单,send order封装了build和submit过程
>>> print(api.send_order(symbol="BTM/USDT", side="sell", price=888, volume=1))
{
'code': 200,
'msg': '',
'data': {
'tx_hash': '1335845f53c7e0f72b709b659c1db69f60a42adcdb3dd2b0c7bbedc2be1d73f7',
'order': {
'symbol': 'BTM/USDT',
'side': 'sell',
'order_id': 6688061,
'open_price': '888.00000000000000000000',
'deal_price': '0',
'amount': '1.00',
'filled_amount': '0.00',
'fee_amount': '0.000000',
'status': 'submitted',
'type': 'mov',
'client_id': '',
'order_txs': [{
'hash': '1335845f53c7e0f72b709b659c1db69f60a42adcdb3dd2b0c7bbedc2be1d73f7',
'type': 'mov_place_order',
'timestamp': 1605755145
}],
'order_timestamp': 1605755145,
'update_timestamp': 1605755145
}
}
}
# 查询所有未成交的磁力订单
调用SDK查询所有未成交的磁力订单
>>> print(api.query_open_orders("BTM/USDT"))
{
'code': 200,
'msg': '',
'data': [{
'symbol': 'BTM/USDT',
'side': 'sell',
'order_id': 6688061,
'open_price': '888.0000000000',
'deal_price': '0.0000000000',
'amount': '1.00',
'filled_amount': '0.00',
'fee_amount': '0.000000',
'status': 'open',
'type': 'mov',
'client_id': '',
'order_txs': [{
'hash': '1335845f53c7e0f72b709b659c1db69f60a42adcdb3dd2b0c7bbedc2be1d73f7',
'type': 'mov_place_order',
'timestamp': 1605755145
}],
'order_timestamp': 1605755145,
'update_timestamp': 1605755146
}],
'pagination': {
'start': 0,
'limit': 1000,
'_links': {}
}
}
# 通过订单号查询磁力订单
>>> print(api.query_list_orders([6688061]))
{
'code': 200,
'msg': '',
'data': [{
'symbol': 'BTM/USDT',
'side': 'sell',
'order_id': 6688061,
'open_price': '888.0000000000',
'deal_price': '0.0000000000',
'amount': '1.00',
'filled_amount': '0.00',
'fee_amount': '0.000000',
'status': 'open',
'type': 'mov',
'client_id': '',
'order_txs': [{
'hash': '1335845f53c7e0f72b709b659c1db69f60a42adcdb3dd2b0c7bbedc2be1d73f7',
'type': 'mov_place_order',
'timestamp': 1605755145
}],
'order_timestamp': 1605755145,
'update_timestamp': 1605755146
}],
'pagination': {
'start': 0,
'limit': 1000,
'_links': {}
}
}
# 磁力撤单
通过sdk撤销指定订单号的订单
>>> print(api.cancel_order(710924))
{
'code': 200,
'msg': '',
'data': {
'tx_hash': '2387e1ab0c462b212fb2252708a8792ad57c5b1ad5857cadf1cce200ce6ab5d3'
}
}
# 查询账户余额数据
>>> print(api.get_balance())
{
'code': 200,
'msg': '',
'data': {
'address': 'vp1quxu2u2m04stfc82v3ucgpr00vr0w8xuc2zqgu5',
'label': 'byone',
'balances': [{
'asset': {
'asset_id': 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'decimals': 0,
'symbol': 'BTM',
'name': 'Bytom',
'type': 'BTM'
},
'balance': '0.97555',
'available_balance': '0.97555',
'unconfirmed_balance': '0',
'total_received': '3.98355',
'total_sent': '3.008',
'in_usd': '0.06',
'in_cny': '0.37',
'in_btc': '0.000003'
}, {
'asset': {
'asset_id': 'bda946b3110fa46fd94346ce3f05f0760f1b9de72e238835bc4d19f9d64f1742',
'decimals': 0,
'symbol': 'BTC',
'name': 'Bitcoin',
'type': 'BTC'
},
'balance': '0',
'available_balance': '0',
'unconfirmed_balance': '0',
'total_received': '0',
'total_sent': '0',
'in_usd': '0.00',
'in_cny': '0.00',
'in_btc': '0.000000'
}, {
'asset': {
'asset_id': '47fcd4d7c22d1d38931a6cd7767156babbd5f05bbbb3f7d3900635b56eb1b67e',
'decimals': 0,
'symbol': 'SUP',
'name': 'SUP',
'type': 'BTM'
},
'balance': '0',
'available_balance': '0',
'unconfirmed_balance': '0',
'total_received': '0',
'total_sent': '0',
'in_usd': '0.00',
'in_cny': '0.00',
'in_btc': '0.000000'
}, {
'asset': {
'asset_id': '78de44ffa1bce37b757c9eae8925b5f199dc4621b412ef0f3f46168865284a93',
'decimals': 0,
'symbol': 'ETH',
'name': 'Ethereum',
'type': 'ETH'
},
'balance': '0',
'available_balance': '0',
'unconfirmed_balance': '0',
'total_received': '0',
'total_sent': '0',
'in_usd': '0.00',
'in_cny': '0.00',
'in_btc': '0.000000'
}, {
'asset': {
'asset_id': '184e1cc4ee4845023888810a79eed7a42c02c544cf2c61ceac05e176d575bd46',
'decimals': 0,
'symbol': 'USDT',
'name': 'USDT-ERC20',
'type': 'ETH'
},
'balance': '0',
'available_balance': '0',
'unconfirmed_balance': '0',
'total_received': '1.0593',
'total_sent': '1.0593',
'in_usd': '0.00',
'in_cny': '0.00',
'in_btc': '0.000000'
}, {
'asset': {
'asset_id': '25f2069140fa3ff4d6e0dc1d0fcaa11ace01eb721f115f0f1a5a3782db597fb1',
'decimals': 0,
'symbol': 'DAI',
'name': 'Dai Stablecoin',
'type': 'ETH'
},
'balance': '0',
'available_balance': '0',
'unconfirmed_balance': '0',
'total_received': '0',
'total_sent': '0',
'in_usd': '0.00',
'in_cny': '0.00',
'in_btc': '0.000000'
}, {
'asset': {
'asset_id': 'c4644dd6643475d57ed624f63129ab815f282b61f4bb07646d73423a6e1a1563',
'decimals': 0,
'symbol': 'USDC',
'name': 'USDC-ERC20',
'type': 'ETH'
},
'balance': '0',
'available_balance': '0',
'unconfirmed_balance': '0',
'total_received': '0',
'total_sent': '0',
'in_usd': '0.00',
'in_cny': '0.00',
'in_btc': '0.000000'
}, {
'asset': {
'asset_id': '011a24f9da7551d4cd9ae0f194aa1d1691e22a173edf7d81aabd9a97ca386252',
'decimals': 0,
'symbol': 'LTC',
'name': 'Litecoin',
'type': 'LTC'
},
'balance': '0',
'available_balance': '0',
'unconfirmed_balance': '0',
'total_received': '0',
'total_sent': '0',
'in_usd': '0.00',
'in_cny': '0.00',
'in_btc': '0.000000'
}],
'votes': []
}
}
# 闪电兑换做市流程
闪电兑换是通过一方公开报价,另一方快速成交的交易模式,链下撮合,链上结算。用户在闪电兑换上没有传统交易模式的对手盘,所有的交易深度由做市商者提供。
# 闪兑做市架构
# 闪兑做市原理
市商在本地启动Dealer,通过本地Dealer的RESTful API发送订单以及撤销订单。在这个过程中,做市商需要不断的轮询Mov Server服务器来获取账户资产余额的变化,变化部分即是闪电兑换成交的资产。
# 闪兑做市教程
# 准备Dealer配置文件
单签地址做市配置如下:
{
"port": 1024,
"bycoin_url": "https://ex.movapi.com",
"flash_swap_url": "47.101.190.112:50052",
"mnemonic": "你的助记词",
"derive_rule": {
"account_idx": 1,
"address_idx": 1
},
"network": "mainnet",
"logs": {
"rotate_time": "24h",
"max_age": "72h"
}
}
多签地址做市配置如下:
{
"port": 1024,
"bycoin_url": "https://ex.movapi.com",
"flash_swap_url": "47.101.190.112:50052",
"mnemonic": "你的助记词",
"derive_rule": {
"account_idx": 1,
"address_idx": 1
},
"quant_mode": {
"quant_delegation_url": "https://ex.movapi.com/delegation",
"funder_pubkey": 三方管理的合作伙伴公钥",
"attester_pubkey": "三方管理生成的公钥"
},
"network": "mainnet",
"logs": {
"rotate_time": "24h",
"max_age": "72h"
}
}
port是本地使用的端口号,默认使用1024,可以自行设置 derive_rule 是私钥的派生路径,原则上不用修改 flash_swap_url 闪兑服务器地址 quant_mode 是量化端口时要设置的配置
# 后台运行Dealer
使用mmdk关于闪兑的方法前,需要先启动Dealer并指定正确的配置文件dealer.conf 可以将两部分文件放在同一目录下,启动服务的命令行如下:
$ nohup ./linux_dealer_hz_test dealer.conf &
Dealer后台运行后,可以通过$ ps -aux | grep dealer
来查询程序Dealer进程是否成功运行。
# 调用MOV-MMDK提供的方法
目前针对闪电兑换,MMDK提供了以下几个方法 可供使用:
- get_depth(self, symbol)
- send_order(self, symbol, side, price, amount)
- cancel_order_by_id(self, order_id)
- query_list_orders(self, symbol, side)
在代码中添加依赖,创建FlashApi对象就可以调用MOV-MMDK提供的API参与闪电兑换做市商。
from mov_sdk.flash_api import FlashApi
FLASH_LOCAL_URL = "http://127.0.0.1:1024" //1024为dealer.conf中设置的端口号,此处需与dealer配置文件相同
client = FlashApi(_local_url=FLASH_LOCAL_URL)//创建对象
# 获取市场深度
# Params
名称 | 必选 | 类型 | 描述 |
---|---|---|---|
symbol | 是 | string | 交易对,如:btm_usdt |
# Response
名称 | 类型 | 描述 |
---|---|---|
symbol | String | 交易对 |
fee_rate | String | 交易手续费费率 |
bids | Array | 指定深度的买单的价格和数量 |
asks | Array | 指定深度的卖单的价格和数量 |
>>> print(client.get_depth("btm_usdt"))
>>>
{
"code": 200,
"msg": "",
"data": {
"symbol": "BTM/USDT",
"fee_rate": "0.003000",
"asks": [
[
"0.0597",
"47690.22"
],
[
"0.0598",
"140120.65"
]
],
"bids": [
[
"0.0596",
"37857.85"
],
[
"0.0595",
"158661.60"
]
]
}
}
# 提交订单
# Params
名称 | 必选 | 类型 | 描述 |
---|---|---|---|
symbol | 是 | string | 交易对,如:btm_usdt |
side | 是 | String | 买单/卖单,buy / sell |
price | 是 | String | 挂单价格 |
amount | 是 | String | 挂单的数量 |
# Response
名称 | 类型 | 描述 |
---|---|---|
code | Int | 状态码 |
msg | String | 返回的消息 |
result | Object | 返回的结果 |
data | Object | 订单数据 |
├── order_id | Int | 订单ID |
├── order | Object | 订单 |
├──── symbol | String | 交易对 |
├──── side | String | 交易方向 |
├──── price | String | 挂单价格 |
└──── amount | String | 挂单的数量 |
└── address | String | 发起订单的侧链地址 |
>>> print(client.send_order(symbol="btm_usdt", side="sell", price="5", amount="0.3"))
>>>
{
'code': 200,
'msg': '',
'result': {
'data': {
'order_id': 1340936,
'order': {
'symbol': 'BTM/USDT',
'side': 'sell',
'price': '5',
'amount': '0.3'
},
'address': 'vp1quxu2u2m04stfc82v3ucgpr00vr0w8xuc2zqgu5'
}
}
}
# 取消订单
# Params
名称 | 必选 | 类型 | 描述 |
---|---|---|---|
order_id | 是 | String | 订单ID |
# Response
名称 | 类型 | 描述 |
---|---|---|
code | Int | 状态码,200为成功 |
msg | String | 返回的消息 |
result | Object | 返回的结果 |
>>> print(client.cancel_order_by_id(order_id=1340936))
>>>
{
'code': 200,
'msg': '',
'result': {
'data': None
}
}
# 查询订单
# Params
名称 | 必选 | 类型 | 描述 |
---|---|---|---|
symbol | 是 | String | 需要查询的交易对 |
side | 是 | String | 交易方向,buy或sell |
# Response
名称 | 类型 | 描述 |
---|---|---|
code | Int | 状态码,200为成功 |
msg | String | 返回的消息 |
result | Object | 返回的结果 |
data | Object | 订单数据 |
├── order_id | Int | 订单ID |
├── order | Object | 订单 |
├──── symbol | String | 交易对 |
├──── side | String | 交易方向 |
├──── price | String | 挂单价格 |
└──── amount | String | 挂单的数量 |
└── address | String | 发起订单的侧链地址 |
>>> print(client.query_list_orders(symbol="btm_usdt", side="sell"))
>>>
{
'code': 200,
'msg': '',
'result': {
'data': [{
'order_id': 1340936,
'order': {
'symbol': 'BTM/USDT',
'side': 'sell',
'price': '5',
'amount': '0.3'
},
'address': 'vp1quxu2u2m04stfc82v3ucgpr00vr0w8xuc2zqgu5'
}]
}
}
# MMDK三方托管做市
在代码中添加依赖,使用MovApi对象就可以调用MOV-MMDK提供的API参与三方托管做市
from mov_sdk.mov_api import MovApi
# To use delegation submit
api = MovApi(secret_key="Your secretkey", network="mainnet", third_address="Your deletegation address", third_public_key="Your delegation public key")
print(api.get_exchange_info())
print(api.query_open_orders("BTM/USDT"))
print(api.query_list_orders([710941]))
print(api.cancel_order(710924))
print(api.get_depth("BTC/USDT", 5))
print(api.send_order(symbol="BTC/USDT", side="buy", price=6100, volume=0.01))
print(api.query_balance())
参数
名称 | 类型 | 必选 | 描述 |
---|---|---|---|
secret_key | String | 是 | 策略方私钥 |
network | String | 是 | 主网/测试网 |
third_address | String | 是 | 资金方提供的存币地址 |
third_public_key | String | 是 | 策略方与验证服务器共同生成的公钥 |