SUIのDEX Bluefinで動かすbot
当方プログラミング初学者です。予期せぬエラーが出る可能性もある事、ご理解下さい。
Bluefinでもうbotを動かしていません。変数名やコードは汚いままですが、誰かの参考になればと思い公開しました。
公開に向けて見やすく若干の修正をしましたが、おかしい所がまだあるかと思います。
Bluefin uses USDC.e (bridged USDC) as the primary collateral for the positions on the exchange. This article explains the relationship between USD-denominated instruments and USDC.e margin on Bluefin and how the risk engine functions in the event of a USDC depeg from USD. Throughout the article, USDC is assumed to mean USDC.e.
- 注意
- 上記原文にある通り、USDC.eとUSDがデペグするリスク。又、取引所が閉鎖やハッキング等さまざまなリスクがあります。取引所固有の仕様を理解し、利用して下さい。
githubにインストール方法が記載されています。
ターミナル等でpipして下さい。
コードを実行する場合、全て自己責任でお願い致します。
本記事は教育目的での共有であり、投資を勧誘するものではありません。
利益が出るbotではありません。損失が出る事を理解して下さい。
以下コード
from config import TEST_ACCT_KEY, TEST_NETWORK
from bluefin_v2_client import BluefinClient, Networks, MARKET_SYMBOLS, ORDER_STATUS
from pprint import pprint
import asyncio
import sys
import time
from rich import print
from config import TEST_ACCT_KEY, TEST_NETWORK
from bluefin_v2_client import (
BluefinClient,
Networks,
MARKET_SYMBOLS,
ORDER_SIDE,
ORDER_TYPE,
OrderSignatureRequest,
ORDER_STATUS,
)
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
async def calculate_order_prices(
client: BluefinClient, multiplier: float
):
# マーケットの最終価格を取得
market_data = await client.get_market_data(MARKET_SYMBOLS.BTC)
last_price_wei = int(market_data["lastPrice"])
last_price_BTC = last_price_wei / 1e18
last_price_BTC_rounded = round(last_price_BTC, 1)
# 注文価格を計算
order_price = round(last_price_BTC_rounded * multiplier, 1)
return order_price
async def cancel_orders(client: BluefinClient, orders: list):
for order_hash in orders:
cancellation_request = client.create_signed_cancel_orders(
MARKET_SYMBOLS.BTC, [order_hash]
)
cancel_resp = await client.post_cancel_order(cancellation_request)
pprint({"msg": f"Cancelling order {order_hash}", "resp": cancel_resp})
async def check_position2(client: BluefinClient, symbol):
# BluefinClientを使用してポジション情報を取得
position = await client.get_user_position({"symbol": symbol})
print(f"Position: {position}")
# 初期化されたポジションデータ
default_position_data = {"side": "NONE", "size": 0, "pnl": 0, "avg_entry_price": 0}
# ポジション情報が存在する場合、実際のデータを使用
if position:
size = float(position.get("quantity", "0")) / 1e18
unrealized_pnl = float(position.get("unrealizedProfit", "0")) / 1e18
avg_entry_price = float(position.get("avgEntryPrice", "0")) / 1e18
side = position.get("side", "NONE") # レスポンスから直接 'side' の値を取得
return {
"side": side,
"size": size,
"pnl": unrealized_pnl,
"avg_entry_price": avg_entry_price,
}
else:
# ポジションが存在しない場合、初期化されたデータを返す
return default_position_data
async def buy_place_orders(client: BluefinClient, price: float, quantity: float):
adjusted_leverage = 20
signature_request = OrderSignatureRequest(
symbol=MARKET_SYMBOLS.BTC, # market symbol
price=price, # 使用する price 変数
quantity=quantity, # 使用する quantity 変数
side=ORDER_SIDE.BUY,
orderType=ORDER_TYPE.LIMIT,
leverage=adjusted_leverage,
expiration=int(
(time.time() + 864000) * 1000
), # expiry after 10 days, default expiry is a month
)
signed_order = client.create_signed_order(signature_request)
resp = await client.post_signed_order(signed_order)
pprint({"msg": "placing limit order", "resp": resp})
return
async def sell_place_orders(client: BluefinClient, price: float, quantity: float):
adjusted_leverage = 20
signature_request = OrderSignatureRequest(
symbol=MARKET_SYMBOLS.BTC, # market symbol
price=price, # 使用する price 変数
quantity=quantity, # 使用する quantity 変数
side=ORDER_SIDE.SELL,
orderType=ORDER_TYPE.LIMIT,
leverage=adjusted_leverage,
expiration=int(
(time.time() + 864000) * 1000
), # expiry after 10 days, default expiry is a month
)
signed_order = client.create_signed_order(signature_request)
resp = await client.post_signed_order(signed_order)
pprint({"msg": "placing limit order", "resp": resp})
return
async def main(client):
global order_price, symbol
multiplier = 0.9995
size_multiplier = 0.001
symbol = MARKET_SYMBOLS.BTC
# クライアントを初期化
client = BluefinClient(True, Networks[TEST_NETWORK], TEST_ACCT_KEY)
await client.init(True)
# オーダーのキャンセル
orders = await client.get_orders(
{
"symbol": MARKET_SYMBOLS.BTC,
"statuses": [ORDER_STATUS.OPEN, ORDER_STATUS.PENDING],
}
)
order_hashes = [order["hash"] for order in orders]
print("Cancelling orders:", order_hashes)
await cancel_orders(client, order_hashes)
# 注文価格の計算
order_price = await calculate_order_prices(client, multiplier)
print(f"Order Price 1: {order_price}")
# ポジションの確認
position_info = await check_position2(client, MARKET_SYMBOLS.BTC)
# BUYポジションがある場合の処理
if position_info["side"] == "BUY":
sell_size = abs(position_info["size"])
close_price_sell = order_price * 1.0001 # エントリー価格よりも1%高く設定
close_price_sell = round(close_price_sell, 1)
await sell_place_orders(client, close_price_sell, sell_size)
print(f"sell_size 1: {sell_size}))")
# SELLポジションがある場合の処理
if position_info["side"] == "SELL":
buy_size = abs(position_info["size"])
print(f"buy_size 1: {buy_size}))")
close_price = order_price * 0.99 # エントリー価格よりも1%低く設定
close_price = round(close_price, 1)
await buy_place_orders(client, close_price, buy_size)
print(f" Price 1: {close_price}))")
# ポジションがない場合の処理
if position_info["side"] == "NONE":
# ポジションがない場合の処理をここに記述
buy_size = abs(size_multiplier)
order_price1_ = order_price * 0.9996 # エントリー価格よりも1%低く設定
order_price1_ = round(order_price1_, 1)
await buy_place_orders(client, order_price1_, buy_size)
print(f"Order Price 1: {order_price1_}))")
pass
else:
print("No position info found")
async def main_loop():
client = BluefinClient(True, Networks[TEST_NETWORK], TEST_ACCT_KEY)
await client.init(True)
try:
while True:
await main(client)
await asyncio.sleep(6)
except KeyboardInterrupt:
print("Interrupted by user, exiting...")
finally:
await client.close() # ここでクライアントセッションを閉じる
if __name__ == "__main__":
asyncio.run(main_loop())
from config import TEST_ACCT_KEY, TEST_NETWORK
from bluefin_v2_client import BluefinClient, Networks, MARKET_SYMBOLS, ORDER_STATUS
from pprint import pprint
import asyncio
import sys
import time
from rich import print
from config import TEST_ACCT_KEY, TEST_NETWORK
from bluefin_v2_client import (
BluefinClient,
Networks,
MARKET_SYMBOLS,
ORDER_SIDE,
ORDER_TYPE,
OrderSignatureRequest,
ORDER_STATUS,
)
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
async def calculate_order_prices(client: BluefinClient, multiplier1: float, multiplier2: float):
# マーケットの最終価格を取得
market_data = await client.get_market_data(MARKET_SYMBOLS.ETH)
last_price_wei = int(market_data["lastPrice"])
last_price_ETH = last_price_wei / 1e18
last_price_ETH_rounded = round(last_price_ETH, 1)
# 注文価格を計算
order_price1 = round(last_price_ETH_rounded * multiplier1, 1)
order_price2 = round(last_price_ETH_rounded * multiplier2, 1)
return order_price1, order_price2
async def cancel_orders(client: BluefinClient, orders: list):
for order_hash in orders:
cancellation_request = client.create_signed_cancel_orders(
MARKET_SYMBOLS.ETH, [order_hash]
)
cancel_resp = await client.post_cancel_order(cancellation_request)
pprint({"msg": f"Cancelling order {order_hash}", "resp": cancel_resp})
async def check_position2(client: BluefinClient, symbol):
# BluefinClientを使用してポジション情報を取得
position = await client.get_user_position({"symbol": symbol})
print(f"Position: {position}")
# 初期化されたポジションデータ
default_position_data = {'side': 'NONE', 'size': 0, 'pnl': 0, 'avg_entry_price': 0}
# ポジション情報が存在する場合、実際のデータを使用
if position:
size = float(position.get("quantity", "0")) / 1e18
unrealized_pnl = float(position.get("unrealizedProfit", "0")) / 1e18
avg_entry_price = float(position.get("avgEntryPrice", "0")) / 1e18
side = position.get("side", "NONE") # レスポンスから直接 'side' の値を取得
return {'side': side, 'size': size, 'pnl': unrealized_pnl, 'avg_entry_price': avg_entry_price}
else:
# ポジションが存在しない場合、初期化されたデータを返す
return default_position_data
async def buy_place_orders(client: BluefinClient, price: float, quantity: float):
adjusted_leverage = 20
signature_request = OrderSignatureRequest(
symbol=MARKET_SYMBOLS.ETH, # market symbol
price=price, # 使用する price 変数
quantity=quantity, # 使用する quantity 変数
side=ORDER_SIDE.BUY,
orderType=ORDER_TYPE.LIMIT,
leverage=adjusted_leverage,
expiration=int(
(time.time() + 864000) * 1000
), # expiry after 10 days, default expiry is a month
)
signed_order = client.create_signed_order(signature_request)
resp = await client.post_signed_order(signed_order)
pprint({"msg": "placing limit order", "resp": resp})
return
async def sell_place_orders(client: BluefinClient, price: float, quantity: float):
adjusted_leverage = 20
signature_request = OrderSignatureRequest(
symbol=MARKET_SYMBOLS.ETH, # market symbol
price=price, # 使用する price 変数
quantity=quantity, # 使用する quantity 変数
side=ORDER_SIDE.SELL,
orderType=ORDER_TYPE.LIMIT,
leverage=adjusted_leverage,
expiration=int(
(time.time() + 864000) * 1000
), # expiry after 10 days, default expiry is a month
)
signed_order = client.create_signed_order(signature_request)
resp = await client.post_signed_order(signed_order)
pprint({"msg": "placing limit order", "resp": resp})
return
async def main(client):
global order_price1, order_price2, symbol
multiplier1 = 0.9995
multiplier2 = 0.995
size_multiplier = 0.02
symbol = MARKET_SYMBOLS.ETH
# クライアントを初期化
client = BluefinClient(True, Networks[TEST_NETWORK], TEST_ACCT_KEY)
await client.init(True)
# オーダーのキャンセル
orders = await client.get_orders(
{"symbol": MARKET_SYMBOLS.ETH, "statuses": [ORDER_STATUS.OPEN, ORDER_STATUS.PENDING]}
)
order_hashes = [order["hash"] for order in orders]
print("Cancelling orders:", order_hashes)
await cancel_orders(client, order_hashes)
# 注文価格の計算
order_price1, order_price2 = await calculate_order_prices(client, multiplier1, multiplier2)
print(f"Order Price 1: {order_price1}, Order Price 2: {order_price2}")
# ポジションの確認
position_info = await check_position2(client, MARKET_SYMBOLS.ETH)
avg_entry_price = position_info['avg_entry_price']
if position_info['side'] == "BUY":
sell_size = abs(position_info['size'])
close_price_sell = order_price2 * 1.01 # エントリー価格よりも1%高く設定
close_price_sell = round(close_price_sell, 2)
await sell_place_orders(client, close_price_sell, sell_size)
print(f"sell_size 1: {sell_size}))")
if position_info['side'] == "SELL":
buy_size = abs(position_info['size'])
print(f"buy_size 1: {buy_size}))")
close_price = order_price1 * 0.9995 # エントリー価格よりも1%低く設定
close_price = round(close_price, 2)
await buy_place_orders(client, close_price, buy_size)
print(f" Price 1: {close_price}))")
if position_info['side'] == "NONE":
# ポジションがない場合の処理をここに記述
buy_size = abs(size_multiplier)
order_price1_ = order_price1 * 0.9996 # エントリー価格よりも1%低く設定
order_price1_ = round(order_price1_, 2)
await buy_place_orders(client, order_price1_, buy_size)
print(f"Order Price 1: {order_price1_}))")
pass
else:
print("No position info found")
async def main_loop():
client = BluefinClient(True, Networks[TEST_NETWORK], TEST_ACCT_KEY)
await client.init(True)
try:
while True:
await main(client)
await asyncio.sleep(6)
except KeyboardInterrupt:
print("Interrupted by user, exiting...")
finally:
await client.close() # ここでクライアントセッションを閉じる
if __name__ == "__main__":
asyncio.run(main_loop())
from config import TEST_ACCT_KEY, TEST_NETWORK
from bluefin_v2_client import BluefinClient, Networks, MARKET_SYMBOLS, ORDER_STATUS
from pprint import pprint
import asyncio
import sys
import time
from rich import print
from config import TEST_ACCT_KEY, TEST_NETWORK
from bluefin_v2_client import (
BluefinClient,
Networks,
MARKET_SYMBOLS,
ORDER_SIDE,
ORDER_TYPE,
OrderSignatureRequest,
ORDER_STATUS,
)
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
async def calculate_order_prices(client: BluefinClient, multiplier1: float, multiplier2: float):
# マーケットの最終価格を取得
market_data = await client.get_market_data(MARKET_SYMBOLS.SUI)
last_price_wei = int(market_data["lastPrice"])
last_price_SUI = last_price_wei / 1e18
last_price_SUI_rounded = round(last_price_SUI, 1)
# 注文価格を計算
order_price1 = round(last_price_SUI_rounded * multiplier1, 1)
order_price2 = round(last_price_SUI_rounded * multiplier2, 1)
return order_price1, order_price2
async def cancel_orders(client: BluefinClient, orders: list):
for order_hash in orders:
cancellation_request = client.create_signed_cancel_orders(
MARKET_SYMBOLS.SUI, [order_hash]
)
cancel_resp = await client.post_cancel_order(cancellation_request)
pprint({"msg": f"Cancelling order {order_hash}", "resp": cancel_resp})
async def check_position2(client: BluefinClient, symbol):
# BluefinClientを使用してポジション情報を取得
position = await client.get_user_position({"symbol": symbol})
print(f"Position: {position}")
# 初期化されたポジションデータ
default_position_data = {'side': 'NONE', 'size': 0, 'pnl': 0, 'avg_entry_price': 0}
# ポジション情報が存在する場合、実際のデータを使用
if position:
size = float(position.get("quantity", "0")) / 1e18
unrealized_pnl = float(position.get("unrealizedProfit", "0")) / 1e18
avg_entry_price = float(position.get("avgEntryPrice", "0")) / 1e18
side = position.get("side", "NONE") # レスポンスから直接 'side' の値を取得
return {'side': side, 'size': size, 'pnl': unrealized_pnl, 'avg_entry_price': avg_entry_price}
else:
# ポジションが存在しない場合、初期化されたデータを返す
return default_position_data
async def buy_place_orders(client: BluefinClient, price: float, quantity: float):
adjusted_leverage = 10
signature_request = OrderSignatureRequest(
symbol=MARKET_SYMBOLS.SUI, # market symbol
price=price, # 使用する price 変数
quantity=quantity, # 使用する quantity 変数
side=ORDER_SIDE.BUY,
orderType=ORDER_TYPE.LIMIT,
leverage=adjusted_leverage,
expiration=int(
(time.time() + 864000) * 1000
), # expiry after 10 days, default expiry is a month
)
signed_order = client.create_signed_order(signature_request)
resp = await client.post_signed_order(signed_order)
pprint({"msg": "placing limit order", "resp": resp})
return
async def sell_place_orders(client: BluefinClient, price: float, quantity: float):
adjusted_leverage = 10
signature_request = OrderSignatureRequest(
symbol=MARKET_SYMBOLS.SUI, # market symbol
price=price, # 使用する price 変数
quantity=quantity, # 使用する quantity 変数
side=ORDER_SIDE.SELL,
orderType=ORDER_TYPE.LIMIT,
leverage=adjusted_leverage,
expiration=int(
(time.time() + 864000) * 1000
), # expiry after 10 days, default expiry is a month
)
signed_order = client.create_signed_order(signature_request)
resp = await client.post_signed_order(signed_order)
pprint({"msg": "placing limit order", "resp": resp})
return
async def main(client):
global order_price1, order_price2, symbol
multiplier1 = 0.9995
multiplier2 = 0.9
size_multiplier = 10
symbol = MARKET_SYMBOLS.SUI
# クライアントを初期化
client = BluefinClient(True, Networks[TEST_NETWORK], TEST_ACCT_KEY)
await client.init(True)
# オーダーのキャンセル
orders = await client.get_orders(
{"symbol": MARKET_SYMBOLS.SUI, "statuses": [ORDER_STATUS.OPEN, ORDER_STATUS.PENDING]}
)
order_hashes = [order["hash"] for order in orders]
print("Cancelling orders:", order_hashes)
await cancel_orders(client, order_hashes)
# 注文価格の計算
order_price1, order_price2 = await calculate_order_prices(client, multiplier1, multiplier2)
print(f"Order Price 1: {order_price1}, Order Price 2: {order_price2}")
# ポジションの確認
position_info = await check_position2(client, MARKET_SYMBOLS.SUI)
avg_entry_price = position_info['avg_entry_price']
if position_info['side'] == "BUY":
sell_size = abs(position_info['size'])
close_price_sell = order_price2 * 1.0001 # エントリー価格よりも1%高く設定
close_price_sell = round(close_price_sell, 4)
await sell_place_orders(client, close_price_sell, sell_size)
print(f"sell_size 1: {sell_size}))")
if position_info['side'] == "SELL":
buy_size = abs(position_info['size'])
print(f"buy_size 1: {buy_size}))")
close_price = order_price1 * 0.9995 # エントリー価格よりも1%低く設定
close_price = round(close_price, 4)
await buy_place_orders(client, close_price, buy_size)
print(f" Price 1: {close_price}))")
if position_info['side'] == "NONE":
# ポジションがない場合の処理をここに記述
buy_size = abs(size_multiplier)
order_price1_ = order_price1 * 0.9994 # エントリー価格よりも1%低く設定
order_price1_ = round(order_price1_, 4)
await buy_place_orders(client, order_price1_, buy_size)
print(f"Order Price 1: {order_price1_}))")
pass
else:
print("No position info found")
async def main_loop():
client = BluefinClient(True, Networks[TEST_NETWORK], TEST_ACCT_KEY)
await client.init(True)
try:
while True:
await main(client)
await asyncio.sleep(6)
except KeyboardInterrupt:
print("Interrupted by user, exiting...")
finally:
await client.close() # ここでクライアントセッションを閉じる
if __name__ == "__main__":
asyncio.run(main_loop())
import argparse
import subprocess
import time
from threading import Thread
stop_threads = False # スレッドを終了させるフラグ
def monitor_program(cmd, check_interval):
global stop_threads
while not stop_threads:
print(f"Starting {cmd}")
proc = subprocess.Popen(cmd, shell=True)
while proc.poll() is None and not stop_threads: # プロセスが実行中かつ停止フラグが立っていない間ループ
time.sleep(check_interval)
if not stop_threads:
print(f"{cmd} stopped unexpectedly. Restarting...")
proc.terminate() # プロセスを終了
def main(check_interval=60):
global stop_threads
cmds = [
f'C:/Users/Local/Programs/Python/Python39/python.exe btc.py', #BTC
f'C:/Users/Local/Programs/Python/Python39/python.exe eth.py', #ETH
f'C:/Users/Local/Programs/Python/Python39/python.exe sui.py', #SUI
#f'C:/Users/Local/Programs/Python/Python39/python.exe sol.py' #SOL
]
threads = [Thread(target=monitor_program, args=(cmd, check_interval)) for cmd in cmds]
for t in threads:
t.start()
try:
while any(t.is_alive() for t in threads):
time.sleep(0.1)
except KeyboardInterrupt:
print("\nKeyboardInterrupt detected. Stopping all threads...")
stop_threads = True
for t in threads:
t.join() # スレッドの終了を待つ
print("All threads have been stopped. Exiting program.")
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-ci', '--check_interval', type=int, default=10) # 監視間隔をデフォルトで10秒に設定
args = parser.parse_args()
main(args.check_interval)
TEST_ACCT_KEY = "この中にウォレットの秘密鍵" # public key
TEST_NETWORK = "SUI_PROD"
githubでDLしたbluefin-v2-client-pythonフォルダに各プログラムをコピペしてください。
一つの通貨だけで動かしたい時はcmdsの各プログラムをコメントアウトして起動出来ます。
botを動かしたい時はコマンドプロンプトでbluefin-v2-client-pythonに移動後
python main.pyを入力する事で起動できます。
python main.py
f'C:/Users/Local/Programs/Python/Python39/python.exe btc.py',
上記部分の
f'C:/Users/Local/Programs/Python/Python39/python.exe
この部分には仮想環境、またはローカルのPythonインストール先を指定してください。
ロジックは現在価格に0.99等かけた価格に買い指値を出し、1.001等かけた売り注文を出します。各自で好きな指値位置に微調整した方が良いです。コードが悪いorAPIの反映に遅延する事があり(自分のPCが原因かも)想定ポジションより多く持つ事があるので、少ない注文量にしてください。
Sui Walletの秘密鍵は作成時にしか見れない様なので注意してください。
基本的な動作確認しか出来ていません。何か分からない事があれば聞いて下さい。