SUIのDEX Bluefinで動かすbot

## SUIのDEX Bluefinで動かすbot
###### 当方プログラミング初学者です。予期せぬエラーが出る可能性もある事、ご理解下さい。
Bluefinでもうbotを動かしていません。変数名やコードは汚いままですが、誰かの参考になればと思い公開しました。

公開に向けて見やすく若干の修正をしましたが、おかしい所がまだあるかと思います。

:::note alert
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. 
:::

<dl>

</dl>

<dl>
  <dt>注意</dt>
  <dd> 上記原文にある通り、USDC.eとUSDがデペグするリスク。又、取引所が閉鎖やハッキング等さまざまなリスクがあります。取引所固有の仕様を理解し、利用して下さい。 </dd>
</dl>









https://trade.bluefin.io/


https://github.com/fireflyprotocol/bluefin-v2-client-python

githubにインストール方法が記載されています。
ターミナル等でpipして下さい。

:::note alert
コードを実行する場合、全て自己責任でお願い致します。
本記事は教育目的での共有であり、投資を勧誘するものではありません。
利益が出るbotではありません。損失が出る事を理解して下さい。
:::
以下コード


```ruby:btc.py
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())


```






```ruby:eth.py
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())


```



```ruby:sui.py
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())



```




```ruby:main.py

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)
```
```ruby:config.py
TEST_ACCT_KEY = "この中にウォレットの秘密鍵"  # public key 
TEST_NETWORK = "SUI_PROD"
```

githubでDLしたbluefin-v2-client-pythonフォルダに各プログラムをコピペしてください。
一つの通貨だけで動かしたい時はcmdsの各プログラムをコメントアウトして起動出来ます。
botを動かしたい時はコマンドプロンプトでbluefin-v2-client-pythonに移動後
python main.pyを入力する事で起動できます。



```ruby:
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の秘密鍵は作成時にしか見れない様なので注意してください。

基本的な動作確認しか出来ていません。何か分からない事があれば聞いて下さい。