ほげ - omi

競プロ日記になりそう

AtCoder 第二回マスターズ選手権 -決勝- 参戦記

4/19 土曜日に開催された 第二回マスターズ選手権 -決勝- に参加してきました。

去年: AtCoder 第一回マスターズ選手権 -決勝- 参戦記 - ほげ - omi

今年もスペースが入ったり入らなかったりしている

参加まで

tsukammo さん「今年も出ますか」
omi 「はい」

予選

tsukammo さんがビジュアライザ作成。私とtanzakuさんで別々に方針を決めることに。

ところが岩を蹴り転がすかどうかみたいな場合分けを一生バグらせていて途中から唸るだけ。 バグらせている間に tanzaku さんと tsukammo さんは進捗を生み、今回は去年と違ってまあまあ ABC 各問題の独立性が高かったため無事戦犯をかまし、見た目の順位61位。

絶対落ちたと思って深酒をしたんですが数日経ったらなんか突破していました。そうか……。

本戦

なんかJRが止まっていたので経路に悩んで、早起きに任せて限界散歩がてら n 年ぶりの東43系統に乗って向かいました。早朝から十分暑くて後悔。

問題はまた面倒くさそうな幾何問題に tsukammo さんがまずビジュアライザを作成。私と tanzaku さんで別々に方針を決めることに。

atcoder.jp 自エージェントが拾っていいオブジェクトと拾っちゃだめなオブジェクトがあり、右を固定して左手→左を固定して右手→…… の繰り返しによって作られる三角形の領域で最速で拾い切るには2エージェントをどう動かす? という問題。

文章中しれっと書いてありますが左手と右手は別々かつ順番に動くため、左右で 0, 1 番からスタート→「左を1番に→右を2番に」→「左を2番に→右を3番に」……を繰り返すと 線分上にゴミがなければ 三角形を一切登場させることなく線分だけを考えることができます。この前提に立つならばエージェントは割と独立に考えてよい。というのも、採点スコアはざっくりいうと両手の移動 (Euclid) 距離の和が基準であり(出力行数 = ターン数ではない)、2つ独立に動かして片方のチェックポイントまでにかかった時間のぶんだけもう片方を動かせばよいためです。

つまりTSP。適当に 2-opt 山登りを書いて A 670M。この辺で D にいい感じの凸包を考えて頑張ろうとする人間を殺すケースを作成して提出

10000 四方に75点の配置を四隅にコピーして、3種類を適当に割り振り

上の A をそのまま C に出して 563 M。落ちてる (1M 行かない = 罰点が入っている) ケースを見ると x が 30 万と 70 万のところに縦に半々に引かれていたり、座標が 1 と 999999 の隅に並べられていたり、格子点上に並べられていたり、とにかく直線最短距離で動くと死ぬケースがやまほど。 このあたりで A の方針ミスを自覚したこともあって C に専念。

まず TSP で経路を確定させたあとに、「直線移動の時に干渉するやつがいるなら (±100, ±100) くらいズレた点を先に踏む」「時間経過での中途半端な移動時に、内分点の丸めの結果干渉する経路を取ることになるなら移動せずその場に留まる」の2点を導入することで落ちるケースはなくなったんじゃないかと。都合677M。結構バグらせたのは残念でしたが最後の最後でマトモな点数が取れてよかったです。

その間 A を進めていた tanzaku さん、B を作問の想定外から破壊した tsukammo さんと合わせて総合 9 位でした。

懇親会

今年も見ず知らずの大飯喰らいメイツと感想戦をしたり、(数は少ないですが)旧知の方々と久しぶりにお話をしたり。

スポンサーブースを複数回らないとアルコール摂取がアンロックされないという クソ 仕様 は撤回されていましたね。それでもスポンサーブースは盛況で良かったです。

今年も入賞! 来年も頑張ります。

その後

睡眠不足のため帰宅。なぜ6時起きした?

AtCoder 第一回マスターズ選手権 -決勝- 参戦記

4/20 土曜日に開催された 第一回マスターズ選手権 -決勝- に参加してきました。

ここにスペースは入るのか入らないのか

参加まで

まあまあ波乱怒涛だった 23 年も末、趣味と趣味と仕事と推し活と趣味と順番に復旧させていた最中、競技プログラミングのスイッチが入り切っていなかった頃合いに tsukammo さんから社の slack で DM が飛んできました。

「これ、もしよければ何だけど出ません? (中略)https://atcoder.jp/contests/masters-qual

1分で「出ます」と返答して出場が決まりました。持つべきは頼るべき先達。 もう一人のチームメンバーはHTTF入社枠の先輩である tanzaku さんでした。

予選

tsukammo さんがビジュアライザに専念。私とtanzakuさんで別々に方針を決めることに。

移動一手読みから n 手読みに発展させていけばいいかな、あわよくば焼こうか ← 最後まで無理でした

バグらせている間に一手読みの方針を聞いた tanzaku さんがうまく読みターン数を増やす方法を思いつき、見た目の順位41位で無事突破。椅子を温める結果で情けなかったものの tanzaku さんに初期方針を褒めていただき自尊心++;

本戦

都民ではないので都内への移動が結構しんどい、という理由で前日の部署飲み会を1次会で切り上げ、翌日無事起床成功。

問題はまた面倒くさそうな幾何問題に tsukammo さんがビジュアライザに専念。私と tanzaku さんで別々に方針を決めることに。

とはいえ計測が面倒くさすぎて、速度調整も面倒くさすぎて、推定位置をログに吐いてみても全然違うところを表示する始末。

一旦誤差の小さい B 問題をこなすために、複数 agent を最も近そうな経路に向かって同時に走らせて、壁にぶつかった・ぶつかってない、ゴールした・してない、で矛盾した奴らを順番に kill する、という方針で進めていくことにしました。 最も近そうな経路というのも、直線移動ができないなら進路を遮る壁の右回り or 左回りで迂回、というのを再帰的に考えると一応各ゴールまで通れるはずのルートが計算できるので、あとは誤差が小さいことを 信じて 進みます。面倒なので計測はしません。面倒なので加速度は 499 固定で制御はしません。

ちょっと壁に挟まれると agent が壁にぶつかったぶつからないで矛盾を起こして一瞬で全滅し、あとは 5000T 風にたゆたうだけの儚い存在になります。もしくは通れないくらいの狭い壁に挟まれて一生抜け出せずスコアのマイナスを掘り下げることになります。 スコア平均が 5000 くらいしかない……あと1時間切ってる……何処がバグっているのかな……

ここで放置していた順位表を見ると B で 300k (=5k*60) 出せば良い方でした。みんなバグってるな? 提出。

そのあと経路計算の打ち切りをしてみたり agent 数を増やしたりして B を40 万まで伸ばし、あとは乱数 seed ガチャをしたり(本当に何もしていないのに 1 万くらいブレるので結構本質)B から何も改善していないコードを C に出したりしてタイムアップ。 Aにも出したんですが、壁にぶつかるかゴールするかしないと位置に関する情報が取れないアホな仕様上壁がないと悲惨なスコアになってました。

その間 A を詰めていた tanzaku さんと合わせて総合 5 位でした。

懇親会

朝昼ともにほぼ無みたいな食事をしたせいで腹が大変減っており、大飯喰らいに。見ず知らずの大飯喰らいメイツと感想戦をしたり、(数は少ないですが)旧知の方々と久しぶりにお話をしたり。

スポンサー企業ブースが複数並んでおり、その上複数回らないとアルコール摂取がアンロックされないという バグ 仕様 のせいでとても盛況でした。いつもなら人混み凸はしないのですが、酒を人質に取られていては……。

翌日IPA試験がある、という理由で1次会で切り上げ、翌日。知恵熱を出し無事ダウン。 近所なら受験しに行ったんですが片道一時間と交通費そこそこを掛けてまで落ちに行く気にもなれず蟄居しました。😿

その後

あとはパーティクルフィルタというらしい今回の手法を真面目に復習のために写経したら一瞬で 8 割でてびっくりしました。

今回の問題で計測した値をごにょごにょすれば各 agent の「有り得そうな確率」が出るのはわかったのですが、その活かし方を知らなかった。その確率に応じて重み付きで random pick すれば結果的に正しい確率分布に沿った状態に更新できるのは目からウロコでした。

AHCサボりまくっていますが、典型知識部分の不足を痛感したので成績云々抜きにちゃんと問題と手法を見ていかないとな……と思いました。今後頑張ります。

普通の人が資産運用で 99.1 点をとる方法とその考え方

この記事は、みすてむず いず みすきーしすてむず Advent Calendar 2023 12/13 の記事です。

adventar.org

昨日 12/12 は中村さんのVueどーじょー1期生の感想 でした。


はじめに

hayatoito.github.io

これを読んでください

本記事には当該記事の 0.1/99 くらいの価値があります

あと新NISAの口座を作ってください

免責事項

本記事に記載されるいかなる情報も、投資への勧誘、特定の商品の勧誘や売買の推奨、特定商品、銘柄および株式指数その他投資対象の上昇または下落、あるいは将来の運用成果について示唆あるいは保証することを目的としたものではありません。

本記事は「現状有姿のまま」で提供します。筆者は可能な限り正確性を保つ努力を行いましたが、本記事の記述内容が現に正確またはいかなる読者の投資目的に適合することを保証するものではなく、筆者は本記事についての一切の責任を負いません。また本記事に含まれる情報もしくは内容を利用することで直接・間接的に生じた損失・損害に対し筆者が一切責任を負わないことについて、読者は本記事の閲覧を継続することで同意したものとします。

0.1 点を稼ぐ

外国ETFで99.1点になる話は本文中にもありますが、それではありません。

市場平均ってなんか言葉が弱そうなんですけど。ほんとに市場平均を目指すだけでいいんですか?もっと上を目指さなくてよいのでしょうか?

はい。

  • 株式市場の取引のほとんどは「機関投資家」というプロ中のプロにより行われています。

ここです。ここに付け入る隙があります。

機関投資家は株をごっそり買います。ごっそり買ったときのリターンが最適になるのが「市場平均」なのですが、一方で我々個人は買えて単元株――日本株100株とか、です。 そのため、当該記事中でも「個別株を買うとリターンに比してリスクが大きい個別株をポートフォリオ(PF)に入れるとシャープレシオが下がる」(要約)と言われています。

では、個別株をPFに入れるリスクに相応するリターンがあればいいですね。

程よい株主優待を探しましょう。

株主優待の探し方

[株主優待 おすすめ] [検索]

↑ ばかです

基本的に、そういう検索で上位に出てくるのは大多数の人がふんわりと納得している程度のものでしかありません。

  • その株主優待は本当にPFを歪めるだけのリターンがありますか?
  • そのグルメ詰め合わせ(もしくは、類似の汎用性が低すぎるクーポン券・商品現物・etc.)は本当に額面相応の効用がありますか?
  • そのクオカード(もしくは、類似の汎用性が高すぎる商品券・引換券・etc.)は企業の本業と関連性がありませんが、本当に株主優待は継続しそうですか?

よって考えるべきは、1, 2 番に yes と答えられて、3 番に該当しないもの。自分が日頃使うサービスであったり、小売・飲食店であったり……が 運良く やけに割のいい株主優待をしている場合のみPFに入れるべきでしょう。

具体例

免責事項をもう一回読んでください

  • 百貨店系
    • 大体現金 or 系列カード限定で10%引き。年あたりにその株の評価額 (いまのところ15-30万くらい) 使えば利回り10%。
    • 上の「検索」で出てくる有名所と比較すると、たとえばイオンは 3% キャッシュバック。年に100万くらい使えば (イオンガチ勢は普通に使いそうだな……?) 利回り10%。
  • クリスタ
    • 私は使ってないんですが :twitter: でよく見た。でも買い切り版復活したらしいですね
  • 鉄道系
    • 自宅がターミナルから遠いほど役に立つとされる [要出典]

定額なんぼよりも何%引き/何%増しとか、利用権・全線無料とかのほうが嵌ったときに率が良いので狙い目。そういう優待は金額換算しづらいことも多く、「株主優待 おすすめ」では出てきません。ちょっとググってみたけど0円換算されているサイトばっかり。

個別株を含むポートフォリオの構築

eMAXIS Slim 全世界株式(除く日本)を個別日本株の評価額の16倍を目安に買います。これでPF構築は完了です。 本文中でも述べられていることですが、ロボアドとか銘柄を自動リバランスする包括サービスとか、まして人の温かみのある匠の技なんてものはすべてコストが余計に掛かる要因なので忘れてください。

逆に言えばリスク資産全体の1/17くらいしか日本株に手を出すべきではありません(ホームアセットバイアス)。 この割合を守っていても株主優待がある企業は to-C のサービスで稼いでいるところが多く、さて日本株全てを「そういう」株に代表させていることになるので、日本の工業株金融株なんかはPFに入ってきません。

世界全体からトヨタを抜いた代わりに国内の個人にしか使えないお小遣いがもらえるPFは、トヨタが調子良いときには相対的に損をすることになります。その分の超過リターンがもらえるか、個別株に手を出す前にもう一度考えましょう。

 

 

でも年一で送られてくる優待の封筒は「儲かってまんな」という気持ちになって嬉しいのも確か。元記事に言う「年に30分のリバランス」にもう30分だけ掛けていい感じの個別株を探すのはアリだと思います。

見つからなかったらその分TOPIX連動でも買ってください。


明日12/14はサンドリヨン鈴木さんの「多分Subsonic APIとかかもしれません」です。Subsonic は音楽ストリーミングソフトの Subsonic でしょうか?

adventar.org

【BPL2021】SUPERNOVA Tohoku セミファイナルへの道

この記事はセカンドステージ 10 試合目終了時点のものです。

順位の決定方法

  1. 勝点
  2. 全獲得pt
  3. 直対での獲得pt
  4. 抽選

セミファイナル進出条件

  • ファーストステージまたはセカンドステージで 1 位である
  • ファーストステージまたはセカンドステージで 2 位であり、ファーストステージ、セカンドステージを通してより勝ち点が高い

問題点

  • 両ステージで 1 位チームが同一である場合の成文規定がない
    • セミファイナル優先順位からの憶測で 2 位チーム 2 つが進出できそう
  • ある 2 位チームが別ステージで 1 位を獲得している場合の成文規定がない
    • 当然「ファーストステージ、セカンドステージを通してより勝ち点が高い」のは 1 位を取ってる方の 2 位チームである
    • セミファイナル優先順位からの憶測でもう片方の 2 位チームが進出できそう
  • 両ステージの {1位, 2位} が一致した場合の規定がない
    • → この場合 3 位チーム同士で勝点の和が高いほうが SF 進出と仮定する
  • 両ステージの 2 位チーム(若しくは上記の例での 3 位チーム) の勝点の和が同一の場合の規定がない
    • → この場合両ステージの直対での獲得 pt の和 → 抽選 の順と仮定する
      • (SUPERNOVA の勝ち点が低いのでここのケースはあまり問題にならない)

ファーストステージ

  • 1位:ROUND1 (勝点 13)
  • 2位:APINA VRAMES (勝点 8)
  • 3位:GAME PANIC (勝点 7)
  • 4位:SILK HAT (勝点 6)
  • 5位:LAISURE LAND (勝点 3)
  • 6位:SUPERNOVA Tohoku (勝点 2)

セカンドステージ

ST R1 AV GP LL SH 勝点 pt
SUPERNOVA Tohoku x ○ 7-5 ○ 8-4 ● 2-10 ○ 12-0 9 29 1
ROUND1 ● 5-7 x ○ 10-2 ○ 9-3 6 24 2
APINA VRAMES ● 4-8 x ○ 8-4 ○ 11-1 6 23 2
GAME PANIC ○ 10-2 ● 2-10 ● 4-8 x ○ 7-5 6 23 1
LEISURE LAND ● 1-11 ● 5-7 x ○ 11-1 3 17 2
SILK HAT ● 0-12 ● 3-9 ● 1-11 x 0 4 2

実はここまで引分がない

SUPERNOVA Tohoku セミファイナル進出条件

  • LL との直対に勝ち
    • 12 pt なら (R1 or AV) の片方にしか負け得ないので、進出確定
  • LL との直対に引き分け
    • (R1△AV, R1○LL, AV○SH) 以外 ならセーフ
      • ↑をやられると 1st 3 位の GP との 3 位比較で負ける、という芸術的な負け方をする
      • (厳密には 7 pt 以上同士の引分をすると pt 差で勝てる可能性が微粒子レベルで存在するが無視)
ST R1 AV GP LL SH 勝点 pt 順位
SUPERNOVA Tohoku x ○ 7-5 ○ 8-4 ● 2-10 △ 6-6 ○ 12-0 10 35 3
ROUND1 ● 5-7 x △ 6-6 ○ 10-2 ○ >7-x ○ 9-3 10 >37 1-2
APINA VRAMES ● 4-8 △ 6-6 x ○ 8-4 ○ 11-1 ○ >7-x 10 >36 1-2
GAME PANIC ○ 10-2 ● 2-10 ● 4-8 x ○ 7-5 ? 6~9 23 4
LEISURE LAND △ 6-6 ● 1-11 ● 5-7 x ○ 11-1 4 24 5
SILK HAT ● 0-12 ● 3-9 ? ● 1-11 x 0~3 4 6

↑芸術的な負け方

  • LL との直対に負けると結構細い他力条件
    • 現状 R1-AV が残っているので ST は実質 2 位、というのが厳しい
ST R1 AV GP LL SH 勝点 pt
SUPERNOVA Tohoku x ○ 7-5 ○ 8-4 ● 2-10 ○ 12-0 9 <34 0
ROUND1 ● 5-7 x ○ 10-2 ○ 9-3 6 24 2
APINA VRAMES ● 4-8 x ○ 8-4 ○ 11-1 6 23 2
GAME PANIC ○ 10-2 ● 2-10 ● 4-8 x ○ 7-5 6 23 1
LEISURE LAND ● 1-11 ● 5-7 x ○ 11-1 6 >24 1
SILK HAT ● 0-12 ● 3-9 ● 1-11 x 0 4 2
  • 勝点 9 単独 1 位
    • SH - GP で GP が ST の pt 以上となるような勝利をせず、かつ R1, AV がここから 2 分け
      • めちゃくちゃ細い
  • 勝点 9 で 2 位かつ R1 or AV が 1 位
    • (R1△AV, R1○LL, AV○SH) でない、かつ厳し目な pt 勝負
      • これも細い

結論

勝て。

レバナス vs QLD

はてブロはカス

何故か画像が表示されないのでこっち見て→ https://github.com/omi-key/misc/blob/master/QLD/%E3%83%AC%E3%83%90%E3%83%8A%E3%82%B9%20vs%20QLD.ipynb

.

.

.

.

.

.

.

本編

昨今のコロナ禍も目じゃなく伸びている株式インデックスにアメリカ株の NASDAQ100 がある。

だいたいGAFAMとテスラとzoomとなんかそこら辺の強そうな株が突っ込まれているので、日経平均だの S&P 500 だのを尻目にぐんぐん伸びている。

ところで ETF 界には、各インデックスに「日々の値動きの x 倍 (-3 <= x <= 3)を目指す」ような設計をしたやつが結構いる。先物取引を利用してなんやかんやするのだが、昨今の低金利でキャリーコストが下がっているせいもあってめちゃくちゃに流行っている。

当然 NASDAQ100 に正のレバレッジを掛けた商品を買いたいのだが、如何せん日本から直接は買えない。(S&P 500 のレバレッジ ETF は買えたりする)

しかしここに iFreeレバレッジ NASDAQ100 、通称レバナスなる投信があって、これはドルベースで NASDAQ100 指数の 2 倍を目指して運用されているような商品である。じゃあこれは日本で買えない QLD の替わりになるんじゃないか、ということで QLD 比でのパフォーマンスを調べてみた。

免責事項 - Disclaimer

本記事に記載されるいかなる情報も、投資への勧誘、特定の商品の勧誘や売買の推奨、特定商品、銘柄および株式指数その他投資対象の上昇または下落、あるいは将来の運用成果について示唆あるいは保証することを目的としたものではありません。

本記事は「現状有姿のまま」で提供します。筆者は可能な限り正確性を保つ努力を行いましたが、本記事の記述内容が現に正確またはいかなる読者の投資目的に適合することを保証するものではなく、筆者は本記事についての一切の責任を負いません。また本記事に含まれる情報もしくは内容を利用することで直接・間接的に生じた損失・損害に対し筆者が一切責任を負わないことについて、読者は本記事の閲覧を継続することで同意したものとします。

データ元

それぞれデータDLボタンがあるので押して適当にリネームして配置した。

方針

日本の投信のうち主に海外資産を運用する商品は、時差を考慮して

  • x 日 15:00 購入締め切り
  • x+1 日 未明 アメリカ市場 (現地 x 日) 終値決定 ←ここの値を
  • x+1 日 夕方 x+1 日付基準価額決定、約定 ←ここで使う
  • x+2 日 受け渡し

という流れになっている。つまり、日本時間で x 日の基準価額はアメリカ時間 x-1 日の終値に依存する。

まあ日米ともに祝日があるはずだけれど、今回のコードでは深く考えずに、日本時間の基準日の直前営業日における終値を用いて QLD の米ドル・日本円価格を算出した。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns

データ読み込み

レバナス

csv ははやく encoding が UTF-8 のもののみ valid として欲しい。

levnas = pd.read_csv("rev_NASDAQ.csv", encoding="SHIFT-JIS")

用いる終値、基準日を取り出しておく。

ついでに後ほど求める QLD 価格(usd, jpy) のカラムを用意する。

levnas["close"] = levnas["基準価額"]
levnas["date"] = pd.to_datetime(levnas["基準日"], format="%Y%m%d")
levnas["qldusd"] = 0
levnas["qldjpy"] = 0

levnas
基準日 基準価額 前日比 純資産総額 直近決算日 直近分配金 分配金再投資基準価額 close date qldusd qldjpy
0 20181019 10001.0 0 300033250 0 0 10001 10001.0 2018-10-19 0 0
1 20181022 9998.0 -3 299937512 0 0 9998 9998.0 2018-10-22 0 0
2 20181023 10132.0 134 303965761 0 0 10132 10132.0 2018-10-23 0 0
3 20181024 10090.0 -42 302696469 0 0 10090 10090.0 2018-10-24 0 0
4 20181025 9250.0 -840 277496525 0 0 9250 9250.0 2018-10-25 0 0
... ... ... ... ... ... ... ... ... ... ... ...
425 20200727 18468.0 -1427 13454015575 20191018 0 18468 18468.0 2020-07-27 0 0
426 20200728 19227.0 759 14006937427 20191018 0 19227 19227.0 2020-07-28 0 0
427 20200729 18743.0 -484 13900493329 20191018 0 18743 18743.0 2020-07-29 0 0
428 20200730 19218.0 475 14477773611 20191018 0 19218 19218.0 2020-07-30 0 0
429 20200731 19649.0 431 14811577362 20191018 0 19649 19649.0 2020-07-31 0 0

430 rows × 11 columns

QLD, USD/JPY

qld = pd.read_csv("QLD.txt")
qld.set_index("Date")
usdjpy = pd.read_csv("USDJPY.txt")
usdjpy.set_index("Date")
Open High Low Close Adj Close Volume
Date
1996-10-30 114.370003 114.480003 113.610001 114.180000 114.180000 0.0
1996-10-31 NaN NaN NaN NaN NaN NaN
1996-11-01 113.500000 113.500000 113.500000 113.500000 113.500000 0.0
1996-11-04 113.279999 113.980003 112.949997 113.879997 113.879997 0.0
1996-11-05 113.709999 114.330002 113.449997 114.250000 114.250000 0.0
... ... ... ... ... ... ...
2020-07-27 105.956001 106.046997 105.121002 105.992996 105.992996 0.0
2020-07-28 105.291000 105.679001 104.958000 105.261002 105.261002 0.0
2020-07-29 105.110001 105.227997 104.800003 105.107002 105.107002 0.0
2020-07-30 105.015999 105.288002 104.912003 105.031998 105.031998 0.0
2020-07-31 104.667000 105.795998 104.198997 104.682999 104.682999 0.0

6198 rows × 6 columns

Date を key にしてマージ。

QLD の Adj. Close と USD/JPY の Close を掛け合わせることで JPY での終値を算出する。

配当などを調整した後の価格が Adj. Close らしい。

qldusd = pd.merge(qld, usdjpy, on="Date")
qldusd["close_JPY"] = qldusd["Adj Close_x"] * qldusd["Close_y"]
qldusd[["Date", "Adj Close_x", "close_JPY"]]
Date Adj Close_x close_JPY
0 2006-06-21 7.940834 911.885664
1 2006-06-22 7.753991 900.083291
2 2006-06-23 7.730908 900.960026
3 2006-06-26 7.759483 901.807122
4 2006-06-27 7.469331 868.832582
... ... ... ...
3548 2020-07-27 162.600006 17234.461786
3549 2020-07-28 158.559998 16690.184267
3550 2020-07-29 162.229996 17051.508514
3551 2020-07-30 163.860001 17210.543297
3552 2020-07-31 169.679993 17762.610538

3553 rows × 3 columns

あきらかにこんなにいらないのと、現状 Date カラムが str なので datetime へ変換しておく。

qldjpy = qldusd.loc[3000:, ["Date","Adj Close_x", "close_JPY"]]
qldjpy["Date"] = pd.to_datetime(qldjpy["Date"], format="%Y-%m-%d")
qldjpy
Date Adj Close_x close_JPY
3000 2018-05-22 83.026443 9214.108342
3001 2018-05-23 84.413208 9346.736700
3002 2018-05-24 84.333389 9265.625369
3003 2018-05-25 84.512985 9238.283078
3004 2018-05-29 83.754745 9161.847801
... ... ... ...
3548 2020-07-27 162.600006 17234.461786
3549 2020-07-28 158.559998 16690.184267
3550 2020-07-29 162.229996 17051.508514
3551 2020-07-30 163.860001 17210.543297
3552 2020-07-31 169.679993 17762.610538

553 rows × 3 columns

マージ

競プロ民とは思えぬ脳死 O(N2)

430 くらいなら実用上問題ないのでセーフ

for i in range(430):
    keydate = levnas.loc[i, "date"]
    j = 3000
    while qldjpy.loc[j, "Date"] < keydate:
        j += 1
    levnas.loc[i, "qldusd"] = qldjpy.loc[j-1, "Adj Close_x"]    
    levnas.loc[i, "qldjpy"] = qldjpy.loc[j-1, "close_JPY"]

プロット

QLD(usd)

レバナスの商品設計としてはドルベースの NASDAQ100 (の 2 倍)が基準なので、まずはそのままドルベースの QLD と比較する。

だいたいここ 2 年くらいドル円が 110 円くらいだった気がするので 110 を掛けてプロットしてみる

plt.plot(levnas["date"], levnas["close"], label="leveraged_NASDAQ")
plt.plot(levnas["date"], levnas["qldusd"] * 110, label="QLD(USD)")
plt.legend()
<matplotlib.legend.Legend at 0x7f2ede4dd9d0>

[f:id:omi_ut:20200803000111p:plain]

よくわからないのでおとなしく比を取る。

plt.plot(levnas["date"], levnas["close"]/levnas["qldusd"])
[<matplotlib.lines.Line2D at 0x7f2ede3d6fa0>]

[f:id:omi_ut:20200803000121p:plain]

設定当初は 117 くらいだった比がコロナ前 114 までじわじわと落ち込み、その直後急に 118 程度で安定するようになっている。

これはレバナスが為替ヘッジをしているためであると考えられる。コロナ前は日米金利差がそこそこあったのでヘッジコストが発生していたが、アメリカの金利もゼロになったのでヘッジコストがほぼ無になり、この比が安定したようだ。たぶん。

じゃあなんで為替ヘッジなんてするんですかというと、先物取引の都合上ファンド資金はそのまま証拠金になり、ドルベースの差金決済取引になるので本来差金分しか為替影響を受けない中途半端な状態になってしまう。であれば、ということでいっそまるごと為替ヘッジをしているのだと思う。

たぶん。

QLD(jpy)

ところで普通にアメリETF 買った時に為替ヘッジなんてしませんよね? ということで日本円ベースで比較

plt.plot(levnas["date"], levnas["close"], label="leveraged_NASDAQ")
plt.plot(levnas["date"], levnas["qldjpy"], label="QLD")
plt.legend()
<matplotlib.legend.Legend at 0x7f2ede3b62b0>

[f:id:omi_ut:20200803000131p:plain]

plt.plot(levnas["date"], levnas["close"]/levnas["qldjpy"])
[<matplotlib.lines.Line2D at 0x7f2ede3833a0>]

f:id:omi_ut:20200802235640p:plain

ドル円の動きに従って結構跳ねているが、コロナ以降はQLD比 1.1 と今までよりも高値圏で安定しているように見られる。

当然円高が進めばその分為替ヘッジを行うレバナス有利な戦いになる。

ここまでのまとめ

ヘッジコスト (≒金利差) が発生しない間においては、レバナスは完全に QLD 互換と言ってよさそう。

結論

大和アセットマネジメントははやく TQQQ 互換を出して やくめでしょ

intro-heuristics AtCoder Contest Scheduling

https://atcoder.jp/contests/intro-heuristics/standings

20 位。ギリギリ1ページ目に乗った

~21:00

テンプレートの準備。 rdtsc と焼きなましテンプレはコピペして、ONLINE_JUDGE があるので input() の受け取りをするかどうかを環境で切り分ける部分も先に書いておいた。

cat /proc/cpuinfo で CPU が 3.00 GHz に上がっていることを確認して rdtsc の定数をちょっと書き換えたりした。

21:00~ 21:09

A を読んでから B を解く。 やりたいことはなんとなくわかったけど、サンプルが D=5 なせいでちょっと手間取る。こういうのは const int D=365; したくなる人間なので……。

なんとか直して提出!

f:id:omi_ut:20200629181853p:plain
なんで?

毎行出力させるのを忘れていました。ちゃちゃっと直した所 B 問題にも 5 分制限が掛かっててキレた。

B問題の「毎行出力させる」のはあきらかに高速化の障害になる気がするのですがどうなんでしょうか。

21:09 ~ 21:24

思考停止で一点変更焼きなましを書く。差分計算を高速化する前にとりあえず書くものを書いて提出した。 → 98.5M

score 関数も 1 日ごとに計算する形のまま、開始温度も適当。 たしかこの時点で 40 位くらいで首を傾げながら唸ってた記憶がある。

21:24 ~ 21:49

裏で時間制限 60s とかまで伸ばすと 110M 相当 (ケース平均 1.2 M くらい)くらい出ることがわかったので高速化。ただしケースごとの分散がバカでかいのであんまり当てにならないなーとか思いながら眺めていた。

score 関数で毎回開催間隔のペナルティを引く必要はないので、開催したときと全部終わった後に c * Δ * (Δ-1) / 2 を引いてあげればよい。ちなみに 1-indexed になるのでもれなくバグ祭りが開催されており、死ぬ気でバグを取って提出!

f:id:omi_ut:20200629182904p:plain
なんで????

デバッグ用の早期リターンを消し忘れてた。ついでに 1-indexed バグも取りきれてなかったので除去して再提出。 → 110.9M

21:49 ~ 22:13

ここまで差分計算が全計算の差になっているので、差分だけ軽く計算する方法を考えていく。

s_i,d はすぐだが、c[i] の絡むペナルティが面倒だった。お絵かきをすると、変更する d 日目の前後 prv, nxt をなんとか持ってきて c[x] * ((nxt-d-1) * (nxt-d-2) / (d+1-prv) * (d-prv) / 2 - (nxt-prv) * (nxt-prv-1) / 2); を足し引きすれば良いことがわかる。prv nxt は set<int> + set.lower_bound で持ってきていたが 実はこれが大間違い らしく、コンテナの中の要素数が高々 20 くらいなので普通に vector でゴリゴリやるほうが速いらしい。たしかに……。

まあなんとか実装したし、回転数も 5 倍以上、 20M くらい回るし、提出! → 106.8M なんで????????

22:13 ~ 22:26

また 1-indexed でバグらせていた。 21:24 の提出 (確実に Δscore が合っている submit) と突き合わせながら必死のバグ取りをして 117.5M

22:26 ~ 22:38

ここからは温度変化管理の時間。温度管理たのしい!!!!

dscore, prob, score, maxscore を stderr に並べながら良さげに焼ききれるかを目でなんとなく眺める作業。最終盤にスコアが伸び切らない場合は緩すぎて、中盤からそこそこの値で息切れする場合は固すぎる。

またなんかバグが残ってたのを取ったりしてパラメタ調整。 118.0M

22:30くらい ~ 22:44

今更 裏で回していたスコア検証用のコードに再現性がないことに気づく。原因としては手抜き XorShift を使っていたため seed が固定できていなかった。

ちゃんとしたものを探してコピペし直し、提出用コードにも反映した。 120.0M

22:44 ~ 22:55

裏で温度管理芸人しながら 2 点変更 (2 点 swap ではなく、 1 点変更 * 2 回) したら満遍なくならないかなーと考えているうちに時間切れ。

最終的に startTemp = 1150 という中途半端な数字で 121.5M

23:00

提出時 19 位が 20 位になっていた。

その後も抜かされることはなく 1 ページ目はギリギリで死守できた。

23:03

C 問題を見る。

「日付 $d$ とコンテストタイプ $q$ をランダムに選び、$d$ 日目に開催するコンテストをタイプ $q$ に変更する」という変形操作は実はあまり良くありません。

はい。

abc143_f Distinct Numbers

原因

列を取り出す個数の方を固定して最大の長さを考える

↑この考察がどうひねっても出てこなかった

問題

https://atcoder.jp/contests/abc143/tasks/abc143_f

$N (\le 3 \times 10^{5})$ 個の整数 $A_i \in [1,N]$ から、相異なるちょうど $K$ 個の整数からなる組を最大いくつ取り出せるか。

$K = 1, 2, \ldots, N$ のそれぞれについて求めよ。

自力考察

長さ $K$ の狭義単調増加列を何個取り出せるか、と読み替える。

$C_j$ を $A_i = j$ なる $i$ の個数、$D_k$ を $C_j \textcolor{magenta}{\le} k$ なる $j$ の個数とするまでは良かったが、$\sum D_k / K$ がダメだったため頭を抱える。(ちょっと $D$ の定義が違うけど誤差)

反例: $A=\{1,1,2,3,4,5,5\}$ (→ $D = \{5,2\}$)で $K=3$ のとき。

解説AC

$X(1 \le X \le N)$ 個の列を取り出すときの最大の長さを表す関数 $f(X)$ を考えます。

天才

$X$ 個の列を取り出すとき、各整数 $j \in [1,N]$ は高々 $X$ 回しか使えないのでそれを超える分を無視しても一緒。あとは各列に整数を満遍なく割り振ることで、各列の長さの(最小値の)最大を実現することが可能である。

以上を式で表すと

$$ f(X) = \left\lfloor \frac{\sum_{j=1}^N \min(C_j,X)}{X} \right\rfloor $$

となる。すべての $X$ について $f(X)$ を求めたあとは各 $K$ について $K \le f(X)$ を満たすような最大の $X$ を出力すればよい。

考察

解説下段の $f(X)$ の式変形が面倒(というか絶対思いつけなさそう)なので元の式 $$ f(X) = \left\lfloor \frac{\sum_{j=1}^N \min(C_j,X)}{X} \right\rfloor $$ からなんとか考察したい。

$C_j$ は$A_i = j$ なる $i$ の 個数 なので、$C_j$ の昇順にソートしても差し支えない。ここで、二分探索によって $C_j \le X$ となる最大の添字 $j = J$ を求めることで、 $$ f(X) = \left\lfloor \frac{\sum_{j=1}^N \min(C_j,X)}{X} \right\rfloor = (N-J) + \left\lfloor \frac{\sum_{j=1}^J C_j}{X} \right\rfloor $$

と求めることができる。これは予め $C$ の累積和を求めておけば時間 $O(\log N)$ で求められるので、全体で $O(N \log N)$ で解ける。

あとは $X=0$ でバグらないようにして完成。

提出コード

初AC(添字お祈りゲーをした): https://atcoder.jp/contests/abc143/submissions/9960709

リファイン版: https://atcoder.jp/contests/abc143/submissions/9961265

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for (int i=0;i<(int)(n);i++)

int n,a;
vector<int> c, rui;

int f(int x){
    auto it = lower_bound(c.begin(), c.end(), x);
    int i = (int)(it - c.begin());
    return (n-i) + rui[i] / x;
}

signed main(){
    cin >> n;
    c.resize(n);
    REP(i,n){
        cin >> a;
        c[a-1]++;
    }
    sort(c.begin(), c.end());
    rui.resize(n+1);
    REP(i,n) rui[i+1] = rui[i] + c[i];

    int ans = n;
    REP(i,n){
        while(ans && f(ans) < i+1) ans--;
        cout << ans << "\n";
    }
}

感想

解説通りにACとならないときに、こういう記事に自分の考察まとめるのこそ競プロブログって感じがする

変数の方を固定してなんとかする発想は言われてみれば頻出な気がしてきたので今後頑張ります

_ が2個あると装飾が優先されるからエスケープする