Алексей Манин
Алексей Манин личный блог
23 января 2022, 22:31

Buy and Hold. Купил и держи.

Решил в Python протестировать стратегию «Купил и Держи». Причем захотелось посмотреть какой будет доход если инвестировать ежемесячно равные суммы в течении определенного периода. 

Немного об интерпретации результатов:

1. Дивиденды не учитываются. Учитывается только курсовой рост.

2. Доход по стратегии показан напротив года начала инвестирования, хотя фактически он соответствует дате начала инвестирования + заданный период.

3. На картинках QQQ, $100 ежемесячно в течении 10 лет

Это цифры )Это график )

Если кому интересно, вот ссылка на pastebin

Error: [31m The Parser function of type «linkTool» is not defined. Define your custom parser functions as: [34mhttps://github.com/pavittarx/editorjs-html#extend-for-custom-blocks [0m

Ой, что-то ссылка покраснела (

Ну тогда код. Можете сами тикер поменять (тянет данные с Yahoo), даты, сумму… Ну думаю разберетесь, там всё просто.

<code>from datetime import date
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt


class Bar:
    def __init__(self):
        self.year = 0
        self.month = 0
        self.day = 0
        self.open = 0
        self.high = 0
        self.low = 0
        self.close = 0


def run(df, start_date, end_date, increment, date_increment, fees):
    """ Основная функция """
    df = df[start_date:end_date]

    cur_bar = Bar()
    prev_bar = Bar()

    increment_is_completed = False  # Приращение депо в этом месяце (False-не было, True-было)
    depo = 0  # Брокерский счет в валюте
    sum_depo = 0  # Общая сумма инвестирования (переведено на брокерский счет)
    portfolio = 0  # Количество бумаг в портфеле

    # Перебор строк DF, как приход нового бара
    for row in df.itertuples():  # Перебор строк DF (аналог появления нового бара)
        # Обновляем информацию текущего бара
        cur_bar.day = date.timetuple(row[0])[2]
        cur_bar.month = date.timetuple(row[0])[1]
        cur_bar.year = date.timetuple(row[0])[0]
        cur_bar.open = row[1]
        cur_bar.high = row[2]
        cur_bar.low = row[3]
        cur_bar.close = row[4]

        if cur_bar.month != prev_bar.month:  # Если месяц предыдущего бара не равен месяцу текущего бара
            increment_is_completed = False  # Признак инвестирования в текущем месяце в False
        elif cur_bar.month == prev_bar.month and not increment_is_completed and cur_bar.day >= date_increment:
            depo += increment  # Увеличение брокерского счета на сумму ежемесячных инвестиций
            sum_depo += increment  # Увеличение общей суммы инвестиций
            increment_is_completed = True  # Признак инвестирования в текущем месяце в True

        while cur_bar.open + cur_bar.open * fees < depo:  # Если сумма на счете позволяет купить фин. инструмент
            depo -= cur_bar.open + cur_bar.open * fees  # Списываем с брокерского счета сумму на покупку
            portfolio += 1  # Увеличиваем количество бумаг в портфеле

        # Обновление информации предыдущего бара
        prev_bar.day = cur_bar.day
        prev_bar.month = cur_bar.month
        prev_bar.year = cur_bar.year
        prev_bar.open = cur_bar.open
        prev_bar.high = cur_bar.high
        prev_bar.low = cur_bar.low
        prev_bar.close = cur_bar.close

    return sum_depo, portfolio * cur_bar.close, depo, portfolio, cur_bar.close


if __name__ == '__main__':
    # BRK-B IJH SPY AAPL QQQ
    ticker = 'QQQ'  # Тикер финансового инструмента как он отображается на Yahoo Finance
    increment: int = 100  # Сумма ежемесячного инвестирования
    date_increment: int = 15  # Дата пополнения(число месяца)
    year_invest: int = 10  # Количество лет инвестирования
    fees = 0.0006  # 0.05% комиссия брокера ВТБ + 0.01% комиссия биржи

    df_ticker = yf.download(ticker)  # Загрузка данных с Yahoo Finance
    df_ticker = df_ticker.drop(columns=['Adj Close', 'Volume'])  # Удаляем ненужные колонки

    df_rez_ticker = pd.DataFrame()

    for year in range(1993, 2022):
        start_date: date = date(year, 1, 1)  # Дата старта инвестирования(год, месяц, число)
        end_date = date(start_date.year + year_invest, start_date.month, start_date.day)

        rez = run(df_ticker, start_date, end_date, increment, date_increment, fees)

        dohod = rez[1] - rez[0] + rez[2]
        # print(f'Год начала инвестирования: {year}\n'
        #       f'Всего инвестировано: {rez[0]}\n'
        #       f'Конечная стоимость портфеля: {rez[1]:.2f}\n'
        #       f'Остаток денег на брокерском счете: {rez[2]:.2f}\n'
        #       f'Количество бумаг в портфеле: {rez[3]}\n'
        #       f'Текущая стоимость одной бумаги: {rez[4]:.2f}\n'
        #       f'Доход: {dohod:.2f}\n'
        #       f'---------------------------------------------')

        # new_row = {'Год начала': year, 'Инвестировано': rez[0], 'Стоим портфеля': rez[1], 'Деньги': rez[2],
        #            'Бумаги': rez[3], 'Стоимость бумаги': rez[4], 'Доход': dohod}

        new_row = {'Год начала': str(year),
                   'Инвестировано': round(rez[0], 2),
                   'Стоим портфеля': round(rez[1], 2),
                   'Доход': round(dohod, 2)
                   }

        # append row to the dataframe
        df_rez_ticker = df_rez_ticker.append(new_row, ignore_index=True)

    pd.set_option('display.max_columns', None)  # Сброс ограничений на число столбцов
    print(df_rez_ticker)

    # Построение графика
    index = df_rez_ticker['Год начала']
    values = df_rez_ticker['Доход']
    plt.title(f'Доход за {year_invest} лет ежемесячного инвестирования по ${increment} в инструмент {ticker}')
    plt.bar(index, values, label='Доход')
    plt.xticks(index, df_rez_ticker['Год начала'])
    plt.xlabel("Год начала инвестирования")
    plt.ylabel("Доход в $")
    plt.legend(loc=2)
    plt.show()
</code>
5 Комментариев
  • master1
    23 января 2022, 22:48
    какой рынок?
      • Виктор
        24 января 2022, 21:57
        Алексей Манин, интересно, могли бы для лентяев сказать: выгодно или нет?) В сравнении с индексом)
  • sergik99
    24 января 2022, 08:53
    Купил и дрожи

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн