patorashのブログ

方向性はまだない

配列に同じ要素が何個あるかを数える

先日、会社のチャットワークでそういう話題が出て、いろんな方法があることを知った。

1. array.count(item)

l = [1,2,2,1,3]
l.uniq.map{|item| [item, l.count(item)]}.to_h

配列の変数のcountメソッドの引数に値を渡したら同じ値をカウントしてくれるっていうのは知らなかった。

2. array.group_by(&:itself)を使ってからmap

l = [1,2,2,1,3]
l.group_by(&:itself).map{ |k, v| [k, v.size] }.to_h

group_byをしているほうがイケてる感じ。しかしto_hがなんかダサい…。これをなんとかしたい…。

3. array.group_by後にtransform_values

l = [1,2,2,1,3]
l.group_by(&:itself).transform_values(&:size)

うおお、あったー!transform_valuesメソッド!Hashのvalueを更新するメソッド。 メソッド名はちと長いけれど、これが一番イケてる。ブロック書かずに済んだし。

itselfメソッドも知らなかったので収穫であった。

こういう話題が社内で流れてくると楽しいですね。

追記(2022-04-22)

Ruby 2.7でEnumerable#tallyメソッドが追加されています。これを使うと同じことができます。めちゃくちゃ短くてシンプル!

docs.ruby-lang.org

l = [1,2,2,1,3]
l.tally # => {1 => 2, 2 => 2, 3 => 1}

ニックネームを管理するgem imyou(異名)をリリースした

仕事で使うためにgemを作ったったという話です。

自分が担当している製品は、情報を収集して、それを集計して…みたいなことをよくやるのですが、その時の課題が『表記揺れ』だったりします。よくありますよねー。ASUSのことをどう読むのか?みたいな問題。いくら公式が『エイスース』と言っても、『エーサス』『アーサス』『アサス』などと書かれることはあるわけです。情報を収集するときに『エーサス』と書かれているものは『エイスース』として認識したい!ということですが、そのためにはニックネームを管理する仕組みが必要です。

そこで、ニックネームを管理するためのgem imyou(異名)を作りました。

github.com

gemの名前はニックネームを被りにくい日本語にしたものです。Rails 4と5に対応しています。

使い方

使い方はREADMEに書いていますが、一応説明を。

インストール

Gemfileに書いてbundle installしてください。

gem 'imyou'

マイグレーション

imyou用のテーブルを生成する必要があります。generatorを定義してあるので、それを使ってmigration用のファイルを生成して、マイグレーションしてください。

$ rails generate imyou:migration
$ rails db:migrate

Userモデル

Userモデルには、nameカラムがあるとします。

class User < ApplicationRecord
  has_imyou
end

ニックネーム検索するときの対象として、nameカラムも含めたい場合は以下のように。

class User < ApplicationRecord
  has_imyou :name
end

実際に使う

登録系

登録は、add_nicknameメソッドと、nicknames=メソッドを定義してあります。

@user = User.create(name: '孫悟空')

# ニックネームを登録
@user.add_nickname('カカロット')

# ニックネームを取得
@user.nicknames # => ['カカロット']

# ニックネームを配列で登録
@user.nicknames = %w(カカロット 孫くん ゴクウ)
@user.nicknames # => ['カカロット', '孫くん', 'ゴクウ']
削除系

削除は、remove_nicknameメソッドと、remove_all_nicknamesメソッドを定義してあります。

@user = User.create(name: '孫悟空')
@user.nicknames = %w(カカロット 孫くん ゴクウ)

# ニックネームを削除
@user.remove_nickname('孫くん')
@user.nicknames # => ['カカロット', 'ゴクウ']

# ニックネームを全て削除
@user.remove_all_nicknames
@user.nicknames # => []
検索系

検索系は、完全一致検索と、部分一致検索を定義してあります。本名(?)を検索対象にするためにオプションwith_name_columnがあり、デフォルトでtrueになっています。 もしニックネームのみを検索対象にしたい場合は、falseを設定します。

@user = User.create(name: '孫悟空')
@user.add_nickname('カカロット')

# 名前とニックネームで完全一致検索
User.match_by_nickname('孫悟空').exists? # => true
User.match_by_nickname('カカロット').exists? # => true
User.match_by_nickname('悟空').exists? # => 完全一致していないのでfalse

# 名前とニックネームで部分一致検索
User.partial_match_by_nickname('悟空').exists? # => 部分一致しているのでtrue
User.partial_match_by_nickname('カカ').exists? # => 部分一致しているのでtrue

# ニックネームのみを対象にして検索
# 完全一致検索
User.match_by_nickname('孫悟空', with_name_column: false).exists? # => ニックネームはカカロットのためfalse
User.match_by_nickname('カカロット', with_name_column: false).exists? # => true
# 部分一致検索
User.partial_match_by_nickname('悟空', with_name_column: false).exists? # => ニックネームに部分一致していないのでfalse
User.partial_match_by_nickname('カカ', with_name_column: false).exists? # => 部分一致しているのでtrue
参照系

with_nicknamesスコープを定義しているので、これを呼ぶとeager_loadが実行されるのでN+1は起きません。

@user = User.with_nicknames.first

以上です。

こんな機能があったらいいなぁとか、IssueなりPRなりお願いします!

シェルスクリプトを書くときのおまじない

最近、他の人がRundeckに登録していたジョブを修正していってるのだが、bundle installとかherokuコマンドが動かなかったりしたので、動かしているジョブではどうやっているのかなと思ったら、以下のように書いてあった。

/bin/bash -l -c 'bundle install'

-lはログイン状態のシェルで-cは引数のコマンドを実行するということだったのだけれど、場合によっては、これを複数行書いているようだった。

/bin/bash -l -c 'なにかの処理1'
/bin/bash -l -c 'なにかの処理2'
/bin/bash -l -c 'なにかの処理3'

うーむ、シェルスクリプトって普通、先頭に#!/bin/bashとか書くんじゃなかったっけ?と思いながら調べたら、1行目のオプションで-lをつければそれでいいらしい。

#!/bin/bash -l
set -Ceu

なにかの処理1
なにかの処理2
なにかの処理3

set -Ceuは、間違ってファイルを上書きしたりしないようにとか、エラーが起きたら落ちるようにするためのものらしい。基本、つけておいたほうが安心だなと思ったので付ける。

qiita.com

これで綺麗な感じでシェルスクリプトが書けるようになってきたのでよかった。

また損切りを断行!

ここ数日、円高がすごく進行した影響もあり、打診買いしていたドル円のロングがストップロスにひっかかってしまった。さらに下げたのでまぁよかったといえるが、今現在だとだいぶ戻っている。NZドル円もトレーリングストップしていたので引っかかってしまい、総合すると数百円プラスという程度になってしまった。

ただ、下げたタイミングで残っていたトルコリラ円のショートの決済(損切り)を一部断行したので、多少は安く済ませられた…。

ダウ平均が下がって、日経平均が下がって、リスクオンで円が買われて円高で、という流れが続いているので、円高傾向かなと思う。ダウ平均が上がれば、円安になっている。これが結構曲者で、スタート時点でダウ平均が下がっているけれど、マイナスが縮小してくると円安になるけれど、ダウ平均は、いうてもマイナスのままなんですよ。難しい。

日経平均が下がったのでまた楽天ダブルベアを一部決済。日経平均が上がったら買い戻ししようかなと思っているけれど、じわじわ下がっていってるので、タイミングがくるかどうか…。

ストップロスをおくのが大事っていうのはよく聞くし頭でも理解できているけれど、なかなかうまくできない。しかしそれが素人。 で、素人はストップロスをおけばええんや!と思ってストップロスをおくけれど、バンバン切られたあとにリバウンドしていって損だけが残ってしまい、もう損をしたくないと思って損切りしないで耐える作戦をしいて、大損を抱えて自滅する。

とまぁ、ここまでは経験してきたので今更わかったというのもなんだが、

  • レンジ相場でストップロス戦略を取ると刈り取られるだけ
  • レンジ相場はトラリピが強い
  • トレンド相場になるとトラリピは辛い(塩漬け)
  • トレンド相場で勝率が高いタイミングでストップロス付きで参入する
  • もしストップロスをつけても、もう一段下がったら、下げトレンドの底か、再び上昇トレンドになったタイミングでストップロス付けて参入する
  • これを繰り返していれば、損小利大できる
  • しかし素人は利益が出るとすぐに決済するので利大しづらい。欲にかられながらも決済を急がないこと

ということなのだろう。

底がくるのを待ちます。

トルコリラのショートを損切りした

8月の大暴落のときにトルコリラを両建てしていた。

そのショートに被せてくるくるワイドを設定してみていた。意図としては、さらに下がったとしても損は半分になるし、上がったとしたら、くるくるワイドの決済によって多少は儲かるからこれ以上の損失は防げるという算段だった。

結果的に、くるくるワイドの効果もあって6万円くらいプラスにはなったのだが、なんせ金利の高いトルコリラのショートなのでこの2ヶ月でマイナススワップが2万円分くらいになっていた。だからこの時点でプラス4万。さらにさらに、どこでこの両建てを解除するか…。トルコリラが20円代に乗ったので、ショートを決済しようとすると、全部でマイナス20万…。合計すると結局-16万になる。

こう考えると、両建てしたのは完全に失敗だったのではないか?というのが頭をよぎるが、両建てしなければ評価損が大幅に進行して精神衛生上よくなかったというのはある…。まぁ言えるのは、両建てのタイミングが遅すぎたことだ…。16円くらいだったので。

最近また世界的に株安が進行していて、円が買われる雰囲気になってきているので、両建てを解除するタイミングをずらしていこうと思い、とりあえず半分は損切りした。これでも-10万なのでかなり堪える。

くるくるワイドは負けない投資法として、理論的にはいいと思うのだけれど、スワップが大きな通貨で行うとそもそも儲けにくくなる。なのでトルコリラのような通貨ではやるべきではないなと思った。暴落当時はボラティリティが大きかったのでよくヒットしていたが、ここ最近は鳴かず飛ばずで、ショート本体のマイナススワップを穴埋めするには程遠かった。

値動きが激しく、マイナススワップが発生しない通貨がいいんだろうけれど、そうなるとポンド…?いやーあれには何度も痛い目に合わされているのでなかなか…。しかもブレグジットが控えているので恐ろしい。

結局安かったところでトルコリラのポジションを大きくとれなかったあたりが、いかんかったなぁと思う。

HerokuのpostgresqlのvCPUについて調べた

最近、ようやく[改定新版]内部構造から学ぶPostgreSQLを読み終えた。

[改訂新版]内部構造から学ぶPostgreSQL 設計・運用計画の鉄則 (Software Design plus)

[改訂新版]内部構造から学ぶPostgreSQL 設計・運用計画の鉄則 (Software Design plus)

いわずもがな、めっちゃいい本だと思います!この本はSQLの書き方とかは全く載っていなくて、設計・運用のノウハウがめっちゃ詰まっています。レプリケーションやパフォーマンスチューニング、Explainの見方や、ここ最近加わった機能の紹介などが載っています。

PostgreSQLは9.6からパラレルクエリが使えるようになっていて、並列で問い合わせの処理が行えるようになりました。その説明や、実際にどれくらい速くなるかの実験結果も載っていました。それによると、4CPUで4並列でクエリを処理した結果、速度はおおよそ3倍速くなっていました。4並列で処理するといっても、その分割処理などが入るため、素直に4倍速くなるわけではありません。

私が担当しているプロジェクトや、チーム内の他のプロジェクトでもPostgreSQLを使っているので、この情報は社内のチャットでも情報共有しておきました。また、担当プロジェクトで現在利用しているPostgreSQLバージョンは、まだ9.5のため、パラレルクエリが使えません。

実際に変更したら速くなるのか?を調査するにしても、利用環境のCPUの数がシングルコアだとバージョンアップでの高速化はそこまで見込めないのかな?と思ったので、とりあえず調べたところ、HerokuのPostgreSQLのCPUコア数が公開されてました。

devcenter.heroku.com

standard-0以上であれば、vCPUは2以上あるので、パラレルクエリでの高速化は見込めそうかなと思います。

PostgreSQLバージョンアップだけで高速化が期待できるのは嬉しい! 引き続き検証してバージョンアップをしていく次第。

また結果とかも書いていこうかなと思います。

トレード日誌:株は様子見

週明けの様子は日経平均も全面安。投資信託とロボアドはこの1週間で5%超の下落。ちょっと売却していかないとなーと思っていた矢先だったのでタイミング的に悔やまれる。 とはいえ、日経平均が下がるということは、楽天ダブルベアは調子がいいということ。リスクヘッジの目的で買っていたが、早くも役立っている。今日は決済せずに様子見。世界的に株安になるのではという話もあるので、このままにしておく。

FXはドル円のショートを決済してしまったので何の心配もなく見ていられる。116.6円でショートしていたスイスフランを決済した。113.4円くらいだったので、結構大きな差益が取れた。この取引が上手くいった理由は、ショートのタイミングを抵抗線のあたりで仕込んでいたため。実際は117円代でショートしていて、ビビってすぐ決済したけれど、もっと下がるだろうと思って再びショートしたら思った通り下げトレンドになって上手くいった。112.8辺りに抵抗線があり、急激に戻ってきはじめたので決済した。ここを抜けるともっと下がるかもしれない。

トレンドも重要だけれど、その都度その都度発生している抵抗線支持線を見極めることがポイントかなと思うようになってきた。

自分のようなビビりは、レバレッジを低めにして、抵抗線支持線付近でトレンド転換したかなと思ったらトレードに入るのがいいかなと思うようになった。そうなると売買の機会はグンと減るのだけれど、勝率は少し上がる。あと自分の思った通りにならずに反転しても、戻ってくるまで精神的にも資本的にも耐えられる。

ということで、NZドル円を73円で少しロングした。週足で支持線を確認し、3度跳ね返されていること。日足では下げトレンド終わったかな?くらいだけれど、4時間足では上げトレンドになったかな?と思えたので。75円くらいまで上がったら、逆指値を入れて利益をある程度確保した状態にして、放置しておこうと思う。