さくらのVPS for Windows Server と ngrokを活用したTradingView bot の構築

# さくらのVPS for WindowsServer と ngrokを活用したTradingView bot の構築方法


以前はCloud Functionsを利用してBYBITなどの海外取引所で自動売買botを容易に作成できました。しかし、最近BYBITをはじめとする海外取引所では、アメリカのIPアドレスからのAPIアクセスを制限するようになりました。この変更により、自動売買botの作成が困難になっています。他の方法で実現可能かもしれませんが、インターネット上での情報は見つかりません。

TradingViewの魅力は、仮想通貨だけでなく、指数、商品、株価など様々なマーケットを比較しやすく、簡単にバックテストができる点にあります。この便利さを活かして、TradingView botの構築に取り組み始めました。



https://www.oanda.jp/lab-education/api/usage12/rading_view_oanda_api_automated_trading_system/

上記のリンクは、参考にしたウェブサイトです。今回は、Bybitで使用できるように修正、またさくらのVPS for Windows Serverで動かす所までをやっていきます。


:::note alert
今回作成したmain.pyは最初にポジションを持っている前提のプログラムです。
初期実行時は手動でポジションを持って下さい。ヘッジモードでは正常に動作しません。

・環境構築確認用プログラム post_requests.pyを作成、実行
・TradingViewでストラテジーの設定、Webhook経由のアラート通知を設定する


上記２項目以外は全て、さくらのVPS for Windows Server上で操作を行い、ngrok.exeとmain.pyを常時起動する必要があります。
:::

# 必要な手順
1\. TradingViewのEssentialプランの契約
月額14.95＄ （年次契約12.95
2\. さくらのVPS for Windows Server の契約
月額1,210円 CPU	仮想2Core  メモリ	1GB  SSD	50GB  リージョン	大阪、石狩
3\. ngrokのアカウント作成とインストール
4\. ngrokを起動してWebhook用のURLを取得する
5\.Webhook経由のアラートを受信したら売買するプログラムを準備
6\.TradingViewでストラテジーの設定、Webhook経由のアラート通知を設定する

## 1\. TradingViewのEssentialプランの契約

https://jp.tradingview.com/

TradingViewに契約してない方は、月額で契約した方が良いと思います。毎年ブラックフライデー前後で格安になる時期があるので、それまで待った方が得策です。




## 2\. さくらのVPS for Windows Server の契約

https://vps.sakura.ad.jp/windows/

VPSにはサーバータイプとWindows環境の２つあります。
サーバータイプはOSがCentOS、AlmaLinux、Rocky Linux 、Ubuntu 、KUSANAGI、Debianと普段見かけないOSばかりです。初学者はWindows Serverがオススメです。環境設定等、余計な所でハマる事が多いのでお金で時間を買う感覚です。
![スクリーンショット 2024-03-02 162257.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/4ac7af83-b93c-cc7a-187c-060752d9b632.png)

自分は１Gの契約ですが、
TradingView bot と 他のbotの合計２つbotを動かしても割と余裕あります


## 3\. ngrokのアカウント作成とインストール
https://ngrok.com/

高頻度に取引を行う以外、フリープランで大丈夫です。
ここからはさくらのVPS for Windows Serverの画面上で行います。


登録が終わると画面左側のGetting Started項目の中
Setup＆installationに進みます
![スクリーンショット 2024-03-02 163841.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/1e36564f-47c8-79aa-8f04-2b2cdde11562.png)


さくらのVPS for Windows Serverにインストールしていきます。
![スクリーンショット 2024-03-02 162539.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/43f964e8-3c9e-0f13-17a3-3e48585d06e7.png)
インストールの仕方はサイトに分かりやすく記載されていますが、
```ruby:インストールするコマンド
choco install ngrok
```

上記コマンドはさくらのVPS for Windows Serverの初期状態では使えないので、DLしてインストールしましょう。
インストールしたら、DLしたファイルをダブルクリックしてngrok.exeを開きます

## 4\. ngrokを起動してWebhook用のURLを取得する





次にアカウントの認証を行います。
![スクリーンショット 2024-03-02 162802.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/9940868e-7e05-5e1b-41dc-cc74879fec5e.png)


ngrokの管理画面からYour Aauthtokenを確認することができます。

こちらを利用して認証作業を行います。

Command Lineのコマンドを、ngrok.exe画面に入力。
ここでngrokの準備は完了です

次にWebhook用のURLを取得します。


```ruby:Webhook用のURLを取得
ngrok http 40
```


上記コマンドを入力すると次のような画面が表示されます。


![スクリーンショット 2024-03-02 165658.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/bf195c81-a506-9636-59f4-074a477472b3.png)


Forwardingのhttps:~~~~~ngrok-free.appまでがTradingViewでwebhookに入力するURLです。freeプランだと起動する毎に~~~部分が変更になるので注意して下さい。

次にポートの開放をやっていきます

![スクリーンショット 2024-03-02 170310.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/96b30c38-1f10-df94-2627-a6d0b8ab5bf5.png)
Windows Defender ファイヤーウォールを開きます
詳細設定から
受信の規則 から 新しい規則を作成します。画像と同じ様に設定してください。
プロトコルの種類を”TCP"
ローカルポートを”特定のポート”
ポート番号を”4040”にして下さい。


ローカルPCだとngrok http 80 で作成して、ローカルポート80を開放で良かったんですが、何故かVPSだと上記設定じゃないとダメでした。良く分かりません。詳しい人教えてください

ここまでで、webhookを受信する設定は完了です。
これからテストプログラムを使って動作確認していきます。


次はpost_requests.pyを作成して正しく受信できるか確認します
下記プログラムをローカルPCで作成実行してください。
```ruby:post_requests.py
#ローカルPCで作成、実行
import requests
import time

url = "https:~~~~~ngrok-free.appまで入力/webhook"

requests.post(url=url, json={"contents": "test message"})
```
さくらのVPS for Windows Serverのngrok.exeで正しく受信できたら、画面上に出力されます
![スクリーンショット 2024-03-02 165658.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/bf195c81-a506-9636-59f4-074a477472b3.png)
200 OK!!
![スクリーンショット 2024-03-02 172111.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/51426671-92c4-4227-55e3-50b225c46ce8.png)

## 5\. Webhook経由のアラートを受信したら売買するプログラムを準備


```ruby:main.py
import http.server
import socketserver
import json
import time
from pybit.unified_trading import HTTP
import os
from os.path import join, dirname
from dotenv import load_dotenv

# .env ファイルのパスを設定
dotenv_path = join(dirname(__file__), '.env_bybit_key')
load_dotenv(dotenv_path, verbose=True)

api_key = os.getenv("BYBIT_API_KEY")
api_secret = os.getenv("BYBIT_API_SECRET")

# セッションの初期化
session = HTTP(
    testnet=False,  # 実際の環境での使用の場合
    api_key=api_key,
    api_secret=api_secret,
)



def position_entry(side):
    try:
        print(session.place_order(
            category="linear",
            symbol="BTCUSDT",
            side=side,
            orderType="Market",
            qty=0.01,
            positionIdx = 0
        ))
    except Exception as e:
        print(e)
def position_close(side):
    try:
        print(session.place_order(
            category="linear",
            symbol="BTCUSDT",
            side=side,
            orderType="Market",
            qty=0.01,
            positionIdx = 0,
            reduceOnly = True
        ))
    except Exception as e:
        print(e)
        

class WebhookHandler(http.server.BaseHTTPRequestHandler):
    def do_POST(self):
        
        # データの抽出
        content_length = int(self.headers['Content-Length'])
        req_body = self.rfile.read(content_length).decode("utf-8")
        
        try:
            # JSONとしてデータを解析
            data = json.loads(req_body)
            
            # 'side'キーの値に基づいて処理を分岐
            if 'side' in data:
                if data['side'] == 'buy':
                    print("side_buy")  # 買い注文の場合
                    position_close("Buy")#決済
                    time.sleep(0.1)#0.1秒待機
                    position_entry("Buy")#新規注文
                    
                if data['side'] == "sell":
                    print("side_seLL")  # 売り注文の場合
                    position_close("Sell")#決済
                    time.sleep(0.1)#0.1秒待機
                    position_entry("Sell")#新規注文
                    
        except json.JSONDecodeError:
            print("受信したデータは有効なJSON形式ではありません。")

        # ステータスコード200を返す
        self.send_response(200)
        self.end_headers()
        
with socketserver.TCPServer(("", 40), WebhookHandler) as httpd:
    print("サーバー起動中...")
    httpd.serve_forever()

```


```ruby:.env_bybit_key
BYBIT_API_KEY=************
BYBIT_API_SECRET=************
```


main.pyと.env_bybit_keyをさくらのVPS for Windows Serverに用意します
main.pyの
with socketserver.TCPServer(("", 40), WebhookHandler) as httpd:
上記部分は自分の開いたポートに変更してください

## 6\. TradingViewでストラテジーの設定、Webhook経由のアラート通知を設定する
![スクリーンショット 2024-03-02 175017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/77336fcb-df2e-bc8e-e73b-a28ee110fb46.png)

設定のメッセージ欄に設定します

```ruby:
{"side":"{{strategy.order.action}}"}
```


webhook URLを設定したら完了です。

全てが完了したら実際にポジションを持つか確認してみましょう

```動作確認.py

import requests

# WebhookのURL (ngrokなどでローカルサーバーを外部に公開)
url = "https://12c-12.121212.ngrok-free.app"

# 送信するデータ
data = {"side":"sell"} 
# POSTリクエストを送信
response = requests.post(url=url, json=data)

# レスポンスのステータスコードを出力
print(f"Status Code: {response.status_code}")

# レスポンスの内容を出力
print(f"Response: {response.text}")
```


## まとめ

初期ポジションは手動で持つ前提を忘れないでください。
今回説明にセキュリティ面は省いたので、各自で設定を忘れずに行って下さい。

webhook発動からポジションを持つまで5秒ほどかかります。

良いbotterライフを！！