github上にあるプライベートリポジトリをインポートする方法
github上にあるプライベートリポジトリをインポートするときの設定をいつも忘れてしまうため、備忘録として残す。
取り扱うもの
go module
git、github
githubに登録したSSHKeyを利用する方法
インポート先のローカルリポジトリにある.git/config
ファイルへ以下を追加する。
[url "ssh://git@github.com/"] insteadOf = https://github.com/
次に、GOENVファイルのパスを確認し、該当ファイルを開く。
go env GOENV
env
ファイルの設定を以下のとおり変更する。
GOPRIVATE=<インポートしたいプライベートリポジトリ, github.com/user/repo>
念のため、go moduleのキャッシュを削除してから、go mod tidy。
go clean --modcache go mod tidy
プライベートリポジトリをインポートできる。
参考
MOVE取引とは
FTXで取り扱われているMOVEについての備忘録とする。
取り扱うもの
MOVE Volatility
MOVEとは?
ストラドルのこと。
- MOVE価格上昇:通貨が上下どちらかへ価格変動があるとき
- MOVE価格下降:通貨が上下どちらかへも価格変動がないとき
こちらが詳しい。
期間
1日、1週間、四半期の3種類あるが、1日物を対象とする。
1日物の期間は、償還期限の前日に上場、翌23:59:59に償還されて市場がなくなる。
つまり、取引期間はyyyy-mm-(dd-1) 00:00:00 ~ yyyy-mm-dd 23:59:59 @UTC
となる。
償還時の価格決定
償還価格 = abs(開始1時間のBTC index TWAP価格 - 終了1時間のBTC index TWAP価格)
ただし、算出期間は以下となることに注意する。
- 開始1時間:償還日のyyyy-mm-dd 00:00:00 - 00:00:59
- 終了1時間:償還日のyyyy-mm-dd 23:00:00 - 23:59:59
また、算出に使われるのは、indexのTWAP価格となる。
TWAPについては、以前の記事を参照のこと。
inplied volatility(IV、予想変動率)算出
Put Option = [STRADDLE + STRIKE - FUTURE] / 2
Put Option | STRADDLE | STRIKE | FUTURE |
---|---|---|---|
PUT価格 | MOVE価格 | UIやAPIで提供されるstrikePrice | MOVEと同時期に満期となるBTC先物価格 |
※FUTUREについては、無期とも四半期とも読み取れるが、おそらく無期?
ブラックショールズ方程式
つぎに、名前しか聞いたことがないが、オプションでよく使われるらしい方程式を使う。
この式にあるPut Option
が上記で計算される価格となる。
よって、各変数を与えつつ、σ(インプライド・ボラティリティ)を求める手順となる。
S | X | r | T | N(x) | σ |
---|---|---|---|---|---|
原資産価格(BTC) | 行使価格 | 安全利子率 | 期間 | 標準正規分布の累積確率密度関数 | ボラティリティ(予想変動率) |
ticker | future stats | ? | future | - | - |
※下段はAPI名称
計算自体は難しく、あとは安全利子率を入れれば計算できるのだが、これがよくわからない。
とりあえず、MOVEがどのようなものかザックリ把握できたので、このあたりとする。
次回、もう少しオプションについて学んでみる。
また、GUI上にIV値が表示されているので逆算してみて、IVなどを売買へ応答できるのか検討してみる。
参考
WSL2 + docker導入について
WSL2導入の備忘録とする。
取り扱うもの
- wsl2.0
- docker for windows
環境
- windows10 Pro(ver1909、build18363.1256)
※build 18363.1049 以降であればよい
- windows terminal インストール済み
※インストールされてなくてもよい
- docker for windows インストール済み
※インストールされてなくてもよい
1. 機能追加
power shellで、ふたつの機能を有効にする。
ふたつとも有効にできたら、PCを再起動する。
1.1 ひとつめの機能追加
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
展開イメージのサービスと管理ツール バージョン: 10.0.18362.1139 イメージのバージョン: 10.0.18363.1256 機能を有効にしています [==========================100.0%==========================] 操作は正常に完了しました。
1.2 ふたつめの機能追加
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
展開イメージのサービスと管理ツール バージョン: 10.0.18362.1139 イメージのバージョン: 10.0.18363.1256 機能を有効にしています [==========================100.0%==========================] 操作は正常に完了しました。
2.WSL2 カーネル更新
以下から更新プログラムをダウンロードして実行する。
x64 マシン用 WSL2 Linux カーネル更新プログラム パッケージ
この画面が表示されたら、Finishを押して終了。
3.WSL2を規定へ設定
wsl --set-default-version 2
これが表示されたら、wsl2を規定にセットできている。
WSL 2 との主な違いについては、https://aka.ms/wsl2 を参照してください
4.Ubuntu20.04LTSインストール
以下のリンクからインストールする。
入手を押すと、Microsoft Storeに移行するので、そちらで再び入手を押す。
5.Ubuntu20.04LTS起動
WindowsスタートアップメニューからUbuntuを起動する。
このとき、1~2分経ってもubuntuターミナルに何も表示されなかったとき、PCを再起動してから再度試す。
5.1 ubuntuアカウント登録
Ubuntu用のアカウントとパスワードを登録する。
このアカウントは、Windowsアカウントとは別ものとなる。
Installing, this may take a few minutes... Please create a default UNIX user account. The username does not need to match your Windows username. For more information visit: https://aka.ms/wslusers Enter new UNIX username: // ユーザ入力 New password: // パスワード入力 Retype new password: passwd: password updated successfully
パスワード登録が完了すると、続けて以下が表示される。
To run a command as administrator (user "root"), use "sudo <command>". See "man sudo_root" for details. Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 4.19.128-microsoft-standard x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Fri Dec 25 11:34:37 JST 2020 System load: 0.34 Processes: 8 Usage of /: 0.4% of 250.98GB Users logged in: 0 Memory usage: 2% IPv4 address for eth0: <IP addr> Swap usage: 0% 1 update can be installed immediately. 0 of these updates are security updates. To see these additional updates run: apt list --upgradable The list of available updates is more than a week old. To check for new updates run: sudo apt update This message is shown once once a day. To disable it please create the /home/<user>/.hushlogin file. <user name>@<PC device name>:~$
5.2 ubuntuアップデート&アップグレード
sudo apt update
アップデートが正常に完了したら、アップグレードする。
sudo apt upgrade
5.3 その他セットアップ
必要に応じて、ubuntuをセットアップする。
6. windows terminal導入
bash(ubuntu)、power shell(windows)の切り替えが簡単になるので、windows terminalをこちらからインストールする。
また、windows terminalのカスタマイズは、必要に応じて実施する。
7. docker導入
dockerが利用できるようにする。
7.1 docker desktop for windowsインストール
以下からwindows版のdocker desktopをこちらからインストールする。
インストールについては、この記事を参考にして進める。 qiita.com
7.2 dockerとwsl2起動状況
power shellなどでwsl起動状況を確認する。
wsl -l -v
dockerインストール直後だと、以下となっている。
NAME STATE VERSION * docker-desktop-data Running 2 Ubuntu-20.04 Stopped 2 docker-desktop Running 2
wsl2のデフォルトLinuxディストリビューションをubuntuへ設定する。
wsl --set-default Ubuntu-20.04
すべてRunningステータスであれば、問題ない。
NAME STATE VERSION * Ubuntu-20.04 Running 2 docker-desktop Running 2 docker-desktop-data Running 2
7.3 dockerコンテナ起動
windows terminalを起動して、ubuntu20.04のタブを開く。
試しにdocker desktopのお試しコンテナ起動するため、ubuntuでdockerコマンドを入力し、コンテナを起動する。
docker run -d -p 80:80 docker/getting-started
docker imageをPullしてきているのが確認できる。
Unable to find image 'docker/getting-started:latest' locally latest: Pulling from docker/getting-started 188c0c94c7c5: Pull complete 617561f33ec6: Pull complete 7d856acdaa9c: Pull complete a0d3c6e28e6d: Pull complete af69a9b963c8: Pull complete 0739f3815ad8: Pull complete 7c7b75d0baf8: Pull complete Digest: sha256:b821569034e3b5fae03b40e64a866017067f3bf17effe185b782bdbf02179528 Status: Downloaded newer image for docker/getting-started:latest 4e673700023e91cbe1ede4463b29084aef96ca0d719f67d2a97e52f437b01dd0
コンテナ起動を確認する。
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4e673700023e docker/getting-started "/docker-entrypoint.…" 52 seconds ago Up 49 seconds 0.0.0.0:80->80/tcp cool_chebyshev
docker desktopでもコンテナ起動できていることを確認できる。
7.4 dockerコンテナ内へ
起動したコンテナ名 or IDをdocker ps
で調べる。
起動したコンテナには、bashがなかったので、ashとした。
docker exec --it <コンテナ名 or ID> ash // ここでは、ID:4e673700023e
コンテナ内に入れた。
/ #
試しにディレクトリを確認してみる。
ls -la
/ # ls -la total 80 drwxr-xr-x 1 root root 4096 Dec 25 04:52 . drwxr-xr-x 1 root root 4096 Dec 25 04:52 .. -rwxr-xr-x 1 root root 0 Dec 25 04:52 .dockerenv drwxr-xr-x 2 root root 4096 Oct 21 09:23 bin drwxr-xr-x 5 root root 340 Dec 25 04:52 dev drwxr-xr-x 1 root root 4096 Nov 25 00:31 docker-entrypoint.d -rwxrwxr-x 1 root root 1202 Nov 25 00:30 docker-entrypoint.sh drwxr-xr-x 1 root root 4096 Dec 25 04:52 etc drwxr-xr-x 2 root root 4096 Oct 21 09:23 home drwxr-xr-x 1 root root 4096 Oct 21 09:23 lib drwxr-xr-x 5 root root 4096 Oct 21 09:23 media drwxr-xr-x 2 root root 4096 Oct 21 09:23 mnt drwxr-xr-x 2 root root 4096 Oct 21 09:23 opt dr-xr-xr-x 163 root root 0 Dec 25 04:52 proc drwx------ 1 root root 4096 Dec 25 04:59 root drwxr-xr-x 1 root root 4096 Dec 25 04:52 run drwxr-xr-x 2 root root 4096 Oct 21 09:23 sbin drwxr-xr-x 2 root root 4096 Oct 21 09:23 srv dr-xr-xr-x 11 root root 0 Dec 25 04:52 sys drwxrwxrwt 1 root root 4096 Nov 25 00:30 tmp drwxr-xr-x 1 root root 4096 Oct 21 09:23 usr drwxr-xr-x 1 root root 4096 Oct 21 09:23 var
コンテナから抜けるときは、exitを入力する。
7.4 dockerコンテナ削除
コンテナを削除する。
docker rm -f <コンテナ名 or ID> // ここでは、ID:4e673700023e
削除されたか確認する。コンテナIDが表示されなければ、削除されている。
docker ps -a
これもdocker desktopで確認すると、先ほどのコンテナ表示が消えている。
7.5 docker image確認と削除
インストールされているイメージファイルを確認する。
docker images
docker images
で確認したイメージファイルIDを使ってイメージを削除する。
docker rmi <image ID>
参考
pywinautoハマったところ
pythonのアプリケーションからwindows上のGUIを自動化するpywinautoライブラリを利用した際に、ハマった点を備忘録とする。
取り扱うもの
- python
- pywinauto
ハマった点
window上で操作したいItemが分かり難い
操作方法が分かり難い
どのように操作するのか?
例えば、pythonからPDFファイルのプリンタ設定を操作したいとき、 印刷ダイアログを開き、詳細設定ボタンを押下して詳細画面を開いてから、各種設定を実行する。
このとき、詳細設定画面には、様々なアイテム(テキスト・ボタン・チェックボックスなど)が存在するため、それらを識別して操作する必要がある。
pywinautoを利用すると、自動的にそれらを識別してくれるが、どのようなデータ構造で識別されているか知る必要がある。
pywinautoでデータ構造表示する
操作対象であるウィンドウを"window"オブジェクトすると、以下コードで表示できる。
print(window.dump_tree())
// // 表示結果 // Dialog - '印刷' (L76, T16, R615, B493) ['印刷Dialog', 'Dialog', '印刷'] child_window(title="印刷", class_name="#32770") | ------------------------ 省略 ------------------------ | GroupBox - '' (L380, T295, R587, B432) | ['ページ範囲GroupBox2', 'GroupBox4'] | child_window(class_name="Button") | | Static - '部数(&C):' (L389, T319, R496, B338) | ['部数(&C):', '部数(&C):Static', 'Static8'] | child_window(title="部数(&C):", class_name="Static") | | Edit - '1' (L504, T316, R536, B339) | ['Edit5', '部数(&C):Edit'] | child_window(title="1", class_name="Edit") | | CheckBox - '部単位で印刷(&O)' (L393, T359, R509, B378) | ['部単位で印刷(&O)CheckBox', '部単位で印刷(&O)', 'CheckBox2'] | child_window(title="部単位で印刷(&O)", class_name="Button") | | Static - '' (L463, T379, R580, B418) | ['Static9', '部単位で印刷(&O)Static'] | child_window(class_name="Static") | | UpDown - '' (L534, T316, R552, B339) | ['部数(&C):UpDown', 'UpDown'] | child_window(class_name="msctls_updown32") | | Button - '印刷(&P)' (L329, T456, R417, B482) | ['印刷(&P)Button', '印刷(&P)', 'Button3'] | child_window(title="印刷(&P)", class_name="Button") | | Button - 'キャンセル' (L424, T456, R512, B482) | ['キャンセル', 'キャンセルButton', 'Button4'] | child_window(title="キャンセル", class_name="Button") | | Button - '適用(&A)' (L518, T456, R606, B482) | ['適用(&A)Button', 'Button5', '適用(&A)'] | child_window(title="適用(&A)", class_name="Button") | | Button - 'ヘルプ' (L613, T456, R701, B482) | ['ヘルプ', 'Button6', 'ヘルプButton'] | child_window(title="ヘルプ", class_name="Button") | | TabControl - '' (L86, T50, R605, B449) | ['状態:TabControl', 'TabControl全般', 'TabControl'] | child_window(class_name="SysTabControl32")
構造の操作方法
例えば、印刷部数を変更したいとき、テキストボックスへ部数を入力する。
このとき、以下で表示されるアイテムを操作する。
// // 表示結果(抜粋) // | Edit - '1' (L504, T316, R536, B339) | ['Edit5', '部数(&C):Edit'] | child_window(title="1", class_name="Edit")
部数1と入力したいとき
window["部数(&C):Edit"].set_edit_text("1")
これは
'部数(&C):Edit'
をKeyとしてアイテムへアクセスし、メソッド(set_edit_text()など)でアイテムを操作している。
仮に、ほかの表示内容でアクセスを試みると、エラーとなる場合がある。
これは、Edit
やButton
や1
は、固有な名称でないか、可変となる名称であるためで、
それらをKeyとして利用することは、避けた方がよい。
参考
【FTX Legend】クリアするために
FTXでキャンペーンが行われるので、その対策を備忘録とする。
今回は、ざっくりとした前提条件までとする。
取り扱うもの
FTX Legend (今回のキャンペーンのこと)
FTX Legendとは?
2〜5名でチームを組み、チーム合計取引量が既定以上となれば、賞金がもらえるキャンペーン。
しかも、規定を達成したチームすべてに賞金が与えられるので、キャンペーンという扱い。
キャンペーンと認識していいのか?
トレーディング大会(例えば、期間内の純利益で対戦する)ではなく、誰でも貰えるキャンペーンと捉えていいのか疑問に思った。
というのも、規定取引量を達成するという性質上、取引コストを下げるだけで純利益プラスで終了できる可能性が高くなる。当然、maker取引を考える参加者が増えれば、板の椅子取りゲームとなる。
となると、成行き出来高を奪い合うbot対戦となると思われるので、概ね大会と同じかと考えている。 (キャンペーンなくてもbot対戦は起きてるので、いつもどおりと言われれば・・・)
収支プラスとなるラインは?
クリア条件と取得金額
- キャンペーン条件1:取引量1000万ドルを達成すると、5000ドル取得
- キャンペーン条件2:取引量2000万ドルを達成すると、+5000ドル取得
- キャンペーン条件3:取引量上位5名を達成すると、+5000ドル取得
取引手数料
キャンペーン条件1をクリアするために、すべてtakerで取引したときの手数料を示す。
手数料割引ごとに表示する。
割引なし | リファラル5%割引 | リファラル5% + FTT3%割引 |
---|---|---|
手数料 0.07% | 手数料 0.0665% | 手数料 0.064505% |
7000ドル | 6650ドル | 6450.5ドル |
リファラルキャンペーン
ここからFTXへ登録すると、手数料5%割引される。
紹介した人は、登録者が取引したときの取引手数料30%を貰える。
FTTトークンの所有による割引
30FTT(現時点で約0.01BTC)保有で3%割引される。
さらに保有すれば割引率も上がるが、FTT変動リスクもあるので、30FTTとしておく。
(FTX Legendの効果なのか、FTT価格は上昇しているので、さらに保有していてもよいかも)
プラスライン
下記金額より、taker手数料の支払いを少なくすれば、プラスとなる。
パーセンデージは、1000万ドルに占めるmaker割合を表していて、この値よりmaker割合を増やせば、プラスとなる。
割引なし | リファラル5%割引 | リファラル5% + FTT3%割引 |
---|---|---|
2,857,143ドル | 2,481,203ドル | 2,248,663ドル |
29% | 25% | 23% |
市場ごとに取引量は?
上述のとおり、各ポジションの損益を考えない前提として、取引の20~30%をmaker取引すれば、このキャンペーンはプラスで終われそう。
では、数多くの強者がいる中でmakerを勝ち取るためには、どの市場を選ぶべきなのだろうか?
- botのいない市場を狙う
- 取引高の高い市場を狙う
競合botがいると、板の席取り合戦の勝率が下がるので、botがいない市場を狙いたい。 また、取引高が少なすぎると、そもそも約定しにくいので避けたい。
よって、うまくバランスのとれた市場で、いくつかbot稼働させるのが良いかもしれない。
最後に、市場ごとの24h出来高を見てみると、以下のようになる。
ひと昔前と違ってBCHとかXRPよりも、出来高の多い知らない通貨が多いことに驚いた。
縦軸のスケールに注意
参考
並行処理について(orDoneチャンネル編)
以前、teeチャンネルの備忘録を書いた。 goroumaru41gou.hatenablog.com
その中でorDoneチャンネルを使っているので、今回はその備忘録とする。
今回も参考にしたのはこちら。
取り扱うもの
- orDoneチャンネルについて
テストコード
github.com/goroumaru/test-code
orDoneチャンネルとは?
例えば、上位構成からdoneチャンネルやcontextで停止指示がきたとき、select ~ caseでゴルーチンを抜ける処理を書くケースが多くて煩雑になる。かといってselect ~ caseしないと、ゴルーチンリークしてしまう。
そんなときにorDoneチャンネルを使うと、可読性が向上する。
後述するcontextを使ったほうがよい
// OrDone : func OrDone(done, c <-chan interface{}) <-chan interface{} { valStream := make(chan interface{}) go func() { defer close(valStream) for { select { case <-done: return case v, ok := <-c: if !ok { return } select { case valStream <- v: case <-done: // <-doneがないとdoneチャネルが送信されてきても、 // valStream <- vが送信されてくるまでブロックされ続けてしまう。 // ここで<-doneとなると、1つ上のネストにおける<-doneで抜けられる。 } } } }() return valStream }
なぜorDoneを利用するのか?
実行したい処理について、可読性があがる。
以降、利用有無でコードの可読性を見てみる。
orDoneを利用しないとき
メインとなる処理が埋もれて、わかりづらい・・・
// 比較部分のみ抜粋 loop: for { select{ case <- done: break loop case val,ok := <-myChannel: if !ok { return // またはforからbreakとか } fmt.Printf("valに対して何かするところ: %v\n", val) // ここがメインなのに埋もれてしまう } }
orDoneを利用したとき
// 比較部分のみ抜粋 for val := range orDoneChannel.OrDone(done, myChannel) { fmt.Printf("valに対して何かするところ: %v\n", val) // メインがわかり易くなった }
全体はこんな感じ・・・
func TestMain(t *testing.T) { // ここでやっていること // 定時実行した結果をmychannelとして渡す。 // このとき、orDoneを利用する。 // 時限処置によりタイムアウトして、すべての処理を終える。 // doneチャンネルをクローズするために、コンテキストでタイムアウトする ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() // 定時実行 tick := time.NewTicker(1 * time.Second) defer tick.Stop() // メインゴルーチンが先に終了してしまうので、子ゴルーチンを待たせる wg := sync.WaitGroup{} wg.Add(1) myChannel := make(chan interface{}) defer close(myChannel) done := make(chan interface{}) go func() { defer close(done) // gorutineから抜けるとき、doneチャンネルも閉じられる defer wg.Done() for { select { case <-ctx.Done(): // 時限でcontextがクローズする fmt.Println("context is closed!") return case <-tick.C: // 定時実行 myChannel <- "my channel!" } } }() // orDoneのゴルーチンは、doneチャンネルが送信される(閉じれれる)まで終了しない。 for val := range orDoneChannel.OrDone(done, myChannel) { fmt.Printf("valに対して何かするところ: %v\n", val) } // 子ゴルーチンを待機する wg.Wait() }
doneチャンネルの代わりにcontextを利用する
便利な標準パッケージcontextを使う。
// OrDoneCtx : doneの代わりにcontextを使う func OrDoneCtx(ctx context.Context, c <-chan interface{}) <-chan interface{} { valStream := make(chan interface{}) go func() { defer close(valStream) for { select { case <-ctx.Done(): return case v, ok := <-c: if !ok { return } select { case valStream <- v: case <-ctx.Done(): } } } }() return valStream }
func TestOrDoneCtx(t *testing.T) { // ここでやっていること // 定時実行した結果をmychannelとして渡す。 // このとき、orDoneを利用する。 // 時限処置によりタイムアウトして、すべての処理を終える。 // doneチャンネルをクローズするために、コンテキストでタイムアウトする ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() // 定時実行 tick := time.NewTicker(1 * time.Second) defer tick.Stop() // メインゴルーチンが先に終了してしまうので、子ゴルーチンを待たせる wg := sync.WaitGroup{} wg.Add(1) myChannel := make(chan interface{}) defer close(myChannel) go func() { defer wg.Done() for { select { case <-ctx.Done(): // 時限でcontextがクローズする fmt.Println("context is closed!") return case <-tick.C: // 定時実行 myChannel <- "my channel!" } } }() // doneからctxへ変更 childCtx, childCancel := context.WithCancel(ctx) defer childCancel() // contextがキャンセルされるまで終了しない。 for val := range orDoneChannel.OrDoneCtx(childCtx, myChannel) { fmt.Printf("valに対して何かするところ: %v\n", val) } // 子ゴルーチンを待機する wg.Wait() }
参考
並行処理について(teeチャンネル編)
1つのチャンネル値を2つに別けて別の場所で使用したいとき、teeチャンネルを使うことについて、備忘録とする。
参考にしたのはこちら。
ネットで調べるのもいいけど、情報の正しさを判断できないなら、この本に記載されてるコードを見た方が良いと思う。
取り扱うもの
- teeチャンネルについて
- orDoneチャンネル goroumaru41gou.hatenablog.com
テストコード
github.com/goroumaru/test-code
teeチャンネルとは?
ひとつのチャンネルデータを、ふたつのチャンネルへ分割する。
だから、1入力2出力を"T"という文字で表して、teeチャンネル。
S/Wだけでなく、H/W分野でもteeチャンネルって言葉は使われてる。
teeチャンネル
// Tee : func Tee(ctx context.Context, in <-chan interface{}) (_, _ <-chan interface{}) { out1 := make(chan interface{}) out2 := make(chan interface{}) go func() { defer close(out1) defer close(out2) for val := range orDoneChannel.OrDoneCtx(ctx, in) { var out1, out2 = out1, out2 // コピーしてローカル変数用意 for i := 0; i < 2; i++ { // out1とout2を確実に選択するため。 select { case out1 <- val: out1 = nil // コピー側へnil代入し、out1チャンネルをブロックさせる。 // (=out2を選択させる) case out2 <- val: out2 = nil // コピー側へnil代入し、out2チャンネルをブロックさせる。 // (=out1を選択させる) } } // out1とout2の書き込みが終わると、inチャンネルが読み込み可能となる。 // for ~ rangeのイテレーションがひとつ進むから。 } }() return out1, out2 }
なぜteeチャンネルを利用するのか?
チャンネルブロックを避けて、2チャンネルへ分岐できる。
teeチャンネル使い方
func TestTeeChannel(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 定時実行 tick := time.NewTicker(1 * time.Second) defer tick.Stop() inData := make(chan interface{}) go func() { defer close(inData) var cnt int for { select { case <-tick.C: cnt++ inData <- cnt case <-ctx.Done(): return } } }() // teeチャンネルを利用してチャンネルを分岐する out1, out2 := teeChannel.Tee(ctx, inData) // teeChannelというパッケージ名にしたので・・・ for v1 := range out1 { v2 := <-out2 fmt.Printf("2つに分割:\n(out1, ok) = (%v)\n(out2, ok) = (%v)\n", v1, v2) } }