思考ノイズ

無い知恵を絞りだす。無理はしない。

2つの株を平均的に購入するための株数を計算する

問題

A君は月毎に一定額を貯蓄をしようと考えましたが、銀行の金利が低すぎるのでインデックスファンド(ETF)への積み立てを考えました。ファンドAとファンドBにいままでの額と合わせて評価額が同額になるように毎月割り当てようとしましたが、10口単位での購入のため決まった額範囲内でバランスをとる必要があります。毎月の時価に合わせてそれぞれのファンド何口ずつかう必要があるでしょうか。 <<

という悩みをプログラムで解決してみます

積み立てをおこなっているのですが、国内インデックスと海外インデックスのバランスを取りながら毎月購入をしたいんですよね。購入時期になった時の悩みが10口単位の口座を何ずつ買えばよいのか、ということになります。解決するためのプログラムを書いてみました。Git Hubにコード上げてます。

github.com

解説

まず本日の価格を設定する必要があります。関数で適当に設定しますが、一応ランダムに変更するように設定しました。ファンドは2つあるので数値で識別します。

def stock_value(stk):
    if stk == 0:
        value = 1400
    elif stk == 1:
        value = 1000
    err = random.randrange(-100, 100)
    return value + err 

あとは初期設定。現在持っているそれぞれのストック数、価格などを適当に設定します。

# Current stocks you have
purchased_stock = [90, 110]
# range to purchase in next time
purchase_range = [50000, 60000]
# amount of unit to buy in next time 
buy_balance = [0, 0]
# List for buy units
balances = []
# Dispersion
vers = []
# current value for each account 
stk_value = [stock_value(0), stock_value(1)]

10口単位でそれぞれの株数を上げながら、購入する価格のレンジ内に収まる株数をチェックします。総当たりです。

total_value = sum([buy_balance[i]*stk_value[i] for i in range(2)])
while total_value < purchase_range[1]:
    while total_value < purchase_range[1]:
        # If the balance is in purchase range,
        # culculate dispersion and add lists
        if purchase_range[0] < total_value :
            balances.append(list(buy_balance))
            values = sum([(purchased_stock[i]+buy_balance[i]) * stk_value[i] for i in range(2)])
            ver = sum([((purchased_stock[i]+buy_balance[i])*stk_value[i] - values/2)**2 for i in range(2)])
            print(buy_balance, stk_value, total_value, values)
            vers.append(ver)
        buy_balance[0] += 10
        total_value = sum([buy_balance[i]*stk_value[i] for i in range(2)])
    buy_balance[0] = 0
    buy_balance[1] += 10
    total_value = sum([buy_balance[i]*stk_value[i] for i in range(2)])

それぞれの組み合わせの中で分散値が一番低いものを選びます。それが、総額のバランスがとれた今回の組み合わせの口数を示す番号となります。

# Checking minimum dispersion in the list
idx = vers.index(min(vers))

結果

BALANCED PLANが今回買うべき株数になります。

 ======== RESULT ========
CURRENT STOCK VAL:  [1440, 943]
CURRENT STOCK    :  [90, 110]
CURRENT PURCHASED:  [129600, 103730]
CURRENT VALUE    :  [1440, 943]
PURCHASE RANGE   :  [50000, 60000]
BALANCED PLAN    :  [10, 40]
AFTER PURCHAE    :  [144000, 141450]