Калькулятор хостится на Digital Ocean, сервер на Centos 7, Python + Dash фреймворк для визуализации.
С данным реальным примером, можно начать увлекательное путешествие в мир финансовой грамотности, попутно, ознакомившись с языком Питон, который идеально подходит под задачи автоматизации математических вычислений, а так же фреймворком Даш (на основе более известного фреймворка Flask c элементами ReactJS, но это все под капотом)
Принцип работ инвестиций в дивидендные акции
Один из самых интересных и надежных способов получать пассивный доход — это инвестиции в надежные дивидендные акции.
Наиболее оправданный подход (проверенный годами и многими публичными пассивными инвесторами) — это вкладывать регулярно (например раз в месяц или раз в неделю) одинаковыми суммами в понятные и надежные компании, которые годами наращивают свою выручку и, соответсвенно, дивиденды.
Далее, эти растущие дивиденды ре-инвестируются, что увеличивает регулярные пополнения. Машина раскручивается, и капитал растет.
Все это происходит по экспоненте, как можно убедиться после использования данного калькулятора.
Установка окружения для нашего проекта и размещение на хостинге, публикация.
Подразумевается, что хостинг приобретен, операционная система установлена, веб сервер настроен (или же вы работаете на локальномкомпьютере на Mac или Linux)
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from app import app
app.layout = html.Div([
html.Label(children="Hello world"),
])
server = app.server
app.config.suppress_callback_exceptions = True
if __name__ == '__main__':
app.run_server(debug=True)
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from app import app
app.layout = html.Div([
html.Label(children="Hello world"),
])
server = app.server
app.config.suppress_callback_exceptions = True
if __name__ == '__main__':
app.run_server(debug=True)
Сохраняем CTRL+O и закрываем редактор CRTL+X
Теперь мы готовы запускать нашу тестовую среду (более подробную документацию на английском языке, если это понадобится, можно найти здесь)
# gunicorn --bind 0.0.0.0:8000 index:server
# gunicorn --bind 0.0.0.0:8000 index:server
# gunicorn --bind 0.0.0.0:8000 index:server
Наше страничка «Hello world» доступна по адресу
http://{ip_адрес_сервера}:8000
Если успешно запустили, дезактивируем виртуальную среду
# deactivate
# deactivate
# deactivate
и приступаем к настройкам веб-сервера (коим в нашем примере небезызвестный Caddy), чтобы калькулятор был доступен по доменному имени.
Первым делом создаем .service файл в папке /etc/systemd/system
И, последним делом, осталось дать понять, где искать наш сервис веб-серверу. В случае с Caddy это делается максимально просто, плюс с коробки получаем сертификат
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
def get_header():
header = html.Div([
html.Div([
html.H1(
'List of Dashes')
], className="twelve columns padded"),
], className="gs-text-header")
return header
def get_menu():
menu = html.Div([
dcc.Link('Home ', href='/', className="p-2 text-dark"),
dcc.Link('Dividends Calculator ', href='/dividends-calculator', className="p-2 text-dark")
], className="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm")
return menu
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
def get_header():
header = html.Div([
html.Div([
html.H1(
'List of Dashes')
], className="twelve columns padded"),
], className="gs-text-header")
return header
def get_menu():
menu = html.Div([
dcc.Link('Home ', href='/', className="p-2 text-dark"),
dcc.Link('Dividends Calculator ', href='/dividends-calculator', className="p-2 text-dark")
], className="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm")
return menu
# nano home.py
# nano home.py
# nano home.py
Вставляем код ниже и сохраняем
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from apps import commonmodules
from app import app
layout = html.Div([
commonmodules.get_menu(),
html.H1('This is home screen'),
html.A('My blog', href='https://questpro.club')
])
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from apps import commonmodules
from app import app
layout = html.Div([
commonmodules.get_menu(),
html.H1('This is home screen'),
html.A('My blog', href='https://questpro.club')
])
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from apps import commonmodules
from app import app
layout = html.Div([
commonmodules.get_menu(),
html.H1('This is home screen'),
html.A('My blog', href='https://questpro.club')
])
# nano dividends_calculator.py
# nano dividends_calculator.py
# nano dividends_calculator.py
Вставляем код ниже и сохраняем
# -*- coding: utf-8 -*-
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from apps import commonmodules
from app import app
meta_tags = [
{'name':'description',
'content':'Дивидендный калькулятор с учетом сложного процента'},
{'name':'title',
'content':'Дивидендный калькулятор'}
]
#app.external_stylesheets = external_stylesheets
app.meta_tags = meta_tags
app.title = 'Дивидендный калькулятор'
bottom_text = '''
1) Все суммы в долларах, проценты - в %
2) По умолчанию, средний размер дивидентов при покупке акций устанавливаем в 4%, можно поменять
3) По умолчанию, средний рост цены акций в год устанавливаем в 12.5% на основе статистики по S&P Dividends aristocrats
4) Все дивиденды реинвестируем для осуществления скорейшего роста - сложный процент
'''
default__divident_income_per_month = 1000
default__start_capital = 1000
default__regular_payment = 1000
default__start_divident_percent = 4
default__average_cost_grow_percent = 12.5
layout = html.Div([
commonmodules.get_menu(),
html.H1('Дивидендный калькулятор с учетом сложного процента'),
html.Div([
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Желаемый средний доход в месяц по дивидентам ($)', className="col-sm-4 col-form-label"),
return"Желаемая дивидендная доходность не найдена в заданном промежутке времени, попробуйте выставить больше лет для графиков"
return"Требуемый ежемесячный дивидендный результат будет достигнут через [ {} месяцев], при этом будет вложено: [ {}$ ], но ваш капитал достигнет к этому времени за счет сложного процента: [ {:.2f}$ ]".format(result['month'], result['current_capital_no_reinv'], result['current_capital_with_all_reinv'])
""" Var in input field might be not floar, but empty or string, in such case use default value """
# -*- coding: utf-8 -*-
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from apps import commonmodules
from app import app
meta_tags = [
{'name':'description',
'content':'Дивидендный калькулятор с учетом сложного процента'},
{'name':'title',
'content':'Дивидендный калькулятор'}
]
#app.external_stylesheets = external_stylesheets
app.meta_tags = meta_tags
app.title = 'Дивидендный калькулятор'
bottom_text = '''
1) Все суммы в долларах, проценты - в %
2) По умолчанию, средний размер дивидентов при покупке акций устанавливаем в 4%, можно поменять
3) По умолчанию, средний рост цены акций в год устанавливаем в 12.5% на основе статистики по S&P Dividends aristocrats
4) Все дивиденды реинвестируем для осуществления скорейшего роста - сложный процент
'''
default__divident_income_per_month = 1000
default__start_capital = 1000
default__regular_payment = 1000
default__start_divident_percent = 4
default__average_cost_grow_percent = 12.5
layout = html.Div([
commonmodules.get_menu(),
html.H1('Дивидендный калькулятор с учетом сложного процента'),
html.Div([
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Желаемый средний доход в месяц по дивидентам ($)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='divident-income-per-month', value='1000', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Первоначальный взнос ($)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='start-capital', value='1000', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Частота очередного поступления: 1 месяц', className="col-sm-4 col-form-label")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Размер очередного поступления ($)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='regular-payment', value='1000', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Средний размер дивидентов в при покупке акций (%)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='start-divident-percent', value='4', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Средний рост цены акций в год (%)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='average-cost-grow-percent', value='12.5', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Отображаем на графиках максимум лет', className="col-sm-4 col-form-label"),
], className="form-group row"),
dcc.Slider(
id='maximum-years',
min=10,
max=50,
marks={i: 'рассматриваем максимум лет {}'.format(i) if i == 1 else str(i) for i in range(5, 51)},
value=11
),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(id='result', className="col-sm-8 col-form-label"),
], className="form-group row")
], className=""),
html.Div([
dcc.Graph(id='dividends-graph'),
dcc.Graph(id='capital-graph')
]),
html.Div([
dcc.Markdown(children=bottom_text)
])
])
def create_dividends_graph(df, target, title):
return {
'data': [dict(
x=df['month'],
y=df['dividend_no_reinv'],
mode='lines',
name='Дивиденды - без реинвестиций и без роста цены акций'
), dict(
x=df['month'],
y=df['dividend_percent_reinv'],
mode='lines',
name='Дивиденды - рост за счет роста акций'
), dict(
x=df['month'],
y=df['dividend_all_reinv'],
mode='lines',
name='Дивиденды - рост цен акций и реинвестирование дивидендов'
), dict(
x=[target['month'],],
y=[target['dividend'],],
mode='marks',
name='Целевое значение дивидендов'
)],
'layout': {
'annotations': [{
'x': 0, 'y': 0, 'xanchor': 'left', 'yanchor': 'bottom',
'xref': 'paper', 'yref': 'paper', 'showarrow': False,
'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',
'text': title
}],
'yaxis': {'type': 'linear', 'title': 'Дивиденды в месяц, $'},
'xaxis': {'showgrid': True, 'title': 'Месяцы'}
}
}
def create_capital_graph(df, target, title):
return {
'data': [dict(
x=df['month'],
y=df['cap_no_reinv'],
mode='lines',
name='Капитал - вложенные деньги'
), dict(
x=df['month'],
y=df['cap_percent_reinv'],
mode='lines',
name='Капитал - с ростом цены акций'
), dict(
x=df['month'],
y=df['cap_all_reinv'],
mode='lines',
name='Капитал - с ростом цены акций и реинвестицией дивидендов'
)],
'layout': {
'annotations': [{
'x': 0, 'y': 0, 'xanchor': 'left', 'yanchor': 'bottom',
'xref': 'paper', 'yref': 'paper', 'showarrow': False,
'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',
'text': title
}],
'yaxis': {'type': 'linear', 'title': 'Капитал, $'},
'xaxis': {'showgrid': True, 'title': 'Месяцы'}
}
}
def total_result_div(result):
if result['dividend'] <= 0:
return "Желаемая дивидендная доходность не найдена в заданном промежутке времени, попробуйте выставить больше лет для графиков"
return "Требуемый ежемесячный дивидендный результат будет достигнут через [ {} месяцев], при этом будет вложено: [ {}$ ], но ваш капитал достигнет к этому времени за счет сложного процента: [ {:.2f}$ ]".format(result['month'], result['current_capital_no_reinv'], result['current_capital_with_all_reinv'])
""" Var in input field might be not floar, but empty or string, in such case use default value """
def input_to_float(var, default):
if var.isdigit():
return float(var)
else:
return default
@app.callback(
[Output(component_id='dividends-graph',component_property='figure'),
Output(component_id='capital-graph',component_property='figure'),
Output(component_id='result', component_property='children')],
[Input(component_id='maximum-years', component_property='value'),
Input(component_id='divident-income-per-month', component_property='value'),
Input(component_id='start-capital', component_property='value'),
Input(component_id='regular-payment', component_property='value'),
Input(component_id='start-divident-percent', component_property='value'),
Input(component_id='average-cost-grow-percent', component_property='value')]
)
def update_output_div(maximum_years, divident_income_per_month, start_capital, regular_payment, start_divident_percent, average_cost_grow_percent):
max_years_number = int(maximum_years)
MONTH_COUNT_YEAR = 12
divident_income_per_month = input_to_float(divident_income_per_month, default__divident_income_per_month)
start_capital = input_to_float(start_capital, default__start_capital)
regular_payment = input_to_float(regular_payment, default__regular_payment)
start_divident_percent = input_to_float(start_divident_percent, default__start_divident_percent)
average_cost_grow_percent = input_to_float(average_cost_grow_percent, default__average_cost_grow_percent)
current_capital_no_reinv = current_capital_just_percent_reinv = current_capital_with_all_reinv = start_capital
info = {}
info['month'] = []
info['dividend_no_reinv'] = []
info['dividend_percent_reinv'] = []
info['dividend_all_reinv'] = []
info['cap_no_reinv'] = []
info['cap_percent_reinv'] = []
info['cap_all_reinv'] = []
target = {'month': 0, 'dividend': 0, 'current_capital_no_reinv': 0, 'current_capital_with_all_reinv': 0}
for month in range(max_years_number * MONTH_COUNT_YEAR):
info['month'].append(month)
current_capital_no_reinv += regular_payment
info['cap_no_reinv'].append(current_capital_no_reinv)
current_dividend_no_reinv = current_capital_no_reinv * start_divident_percent / 100 / MONTH_COUNT_YEAR
info['dividend_no_reinv'].append(current_dividend_no_reinv)
current_capital_just_percent_reinv = (current_capital_just_percent_reinv + regular_payment) * (1 + average_cost_grow_percent / 100 / MONTH_COUNT_YEAR)
info['cap_percent_reinv'].append(current_capital_just_percent_reinv)
current_dividend_just_percent_reinv = current_capital_just_percent_reinv * start_divident_percent / 100 / MONTH_COUNT_YEAR
info['dividend_percent_reinv'].append(current_dividend_just_percent_reinv)
current_dividend_with_all_reinv = current_capital_with_all_reinv * start_divident_percent / 100 / MONTH_COUNT_YEAR
info['dividend_all_reinv'].append(current_dividend_with_all_reinv)
current_capital_with_all_reinv = (current_capital_with_all_reinv + regular_payment + current_dividend_with_all_reinv) * (1 + average_cost_grow_percent / 100 / MONTH_COUNT_YEAR)
info['cap_all_reinv'].append(current_capital_with_all_reinv)
if((current_dividend_with_all_reinv >= divident_income_per_month) and not (target['month'])):
target['month'] = month
target['dividend'] = current_dividend_with_all_reinv
target['current_capital_no_reinv'] = current_capital_no_reinv
target['current_capital_with_all_reinv'] = current_capital_with_all_reinv
return create_dividends_graph(info, target, ''), create_capital_graph(info, target, ''), total_result_div(target)
# -*- coding: utf-8 -*-
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from apps import commonmodules
from app import app
meta_tags = [
{'name':'description',
'content':'Дивидендный калькулятор с учетом сложного процента'},
{'name':'title',
'content':'Дивидендный калькулятор'}
]
#app.external_stylesheets = external_stylesheets
app.meta_tags = meta_tags
app.title = 'Дивидендный калькулятор'
bottom_text = '''
1) Все суммы в долларах, проценты - в %
2) По умолчанию, средний размер дивидентов при покупке акций устанавливаем в 4%, можно поменять
3) По умолчанию, средний рост цены акций в год устанавливаем в 12.5% на основе статистики по S&P Dividends aristocrats
4) Все дивиденды реинвестируем для осуществления скорейшего роста - сложный процент
'''
default__divident_income_per_month = 1000
default__start_capital = 1000
default__regular_payment = 1000
default__start_divident_percent = 4
default__average_cost_grow_percent = 12.5
layout = html.Div([
commonmodules.get_menu(),
html.H1('Дивидендный калькулятор с учетом сложного процента'),
html.Div([
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Желаемый средний доход в месяц по дивидентам ($)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='divident-income-per-month', value='1000', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Первоначальный взнос ($)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='start-capital', value='1000', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Частота очередного поступления: 1 месяц', className="col-sm-4 col-form-label")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Размер очередного поступления ($)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='regular-payment', value='1000', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Средний размер дивидентов в при покупке акций (%)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='start-divident-percent', value='4', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Средний рост цены акций в год (%)', className="col-sm-4 col-form-label"),
html.Div([
dcc.Input(id='average-cost-grow-percent', value='12.5', type='text', className="form-control-plaintext")
], className="col-sm-4")
], className="form-group row"),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(children='Отображаем на графиках максимум лет', className="col-sm-4 col-form-label"),
], className="form-group row"),
dcc.Slider(
id='maximum-years',
min=10,
max=50,
marks={i: 'рассматриваем максимум лет {}'.format(i) if i == 1 else str(i) for i in range(5, 51)},
value=11
),
html.Div([
html.Div([], className="col-sm-1"),
html.Label(id='result', className="col-sm-8 col-form-label"),
], className="form-group row")
], className=""),
html.Div([
dcc.Graph(id='dividends-graph'),
dcc.Graph(id='capital-graph')
]),
html.Div([
dcc.Markdown(children=bottom_text)
])
])
def create_dividends_graph(df, target, title):
return {
'data': [dict(
x=df['month'],
y=df['dividend_no_reinv'],
mode='lines',
name='Дивиденды - без реинвестиций и без роста цены акций'
), dict(
x=df['month'],
y=df['dividend_percent_reinv'],
mode='lines',
name='Дивиденды - рост за счет роста акций'
), dict(
x=df['month'],
y=df['dividend_all_reinv'],
mode='lines',
name='Дивиденды - рост цен акций и реинвестирование дивидендов'
), dict(
x=[target['month'],],
y=[target['dividend'],],
mode='marks',
name='Целевое значение дивидендов'
)],
'layout': {
'annotations': [{
'x': 0, 'y': 0, 'xanchor': 'left', 'yanchor': 'bottom',
'xref': 'paper', 'yref': 'paper', 'showarrow': False,
'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',
'text': title
}],
'yaxis': {'type': 'linear', 'title': 'Дивиденды в месяц, $'},
'xaxis': {'showgrid': True, 'title': 'Месяцы'}
}
}
def create_capital_graph(df, target, title):
return {
'data': [dict(
x=df['month'],
y=df['cap_no_reinv'],
mode='lines',
name='Капитал - вложенные деньги'
), dict(
x=df['month'],
y=df['cap_percent_reinv'],
mode='lines',
name='Капитал - с ростом цены акций'
), dict(
x=df['month'],
y=df['cap_all_reinv'],
mode='lines',
name='Капитал - с ростом цены акций и реинвестицией дивидендов'
)],
'layout': {
'annotations': [{
'x': 0, 'y': 0, 'xanchor': 'left', 'yanchor': 'bottom',
'xref': 'paper', 'yref': 'paper', 'showarrow': False,
'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',
'text': title
}],
'yaxis': {'type': 'linear', 'title': 'Капитал, $'},
'xaxis': {'showgrid': True, 'title': 'Месяцы'}
}
}
def total_result_div(result):
if result['dividend'] <= 0:
return "Желаемая дивидендная доходность не найдена в заданном промежутке времени, попробуйте выставить больше лет для графиков"
return "Требуемый ежемесячный дивидендный результат будет достигнут через [ {} месяцев], при этом будет вложено: [ {}$ ], но ваш капитал достигнет к этому времени за счет сложного процента: [ {:.2f}$ ]".format(result['month'], result['current_capital_no_reinv'], result['current_capital_with_all_reinv'])
""" Var in input field might be not floar, but empty or string, in such case use default value """
def input_to_float(var, default):
if var.isdigit():
return float(var)
else:
return default
@app.callback(
[Output(component_id='dividends-graph',component_property='figure'),
Output(component_id='capital-graph',component_property='figure'),
Output(component_id='result', component_property='children')],
[Input(component_id='maximum-years', component_property='value'),
Input(component_id='divident-income-per-month', component_property='value'),
Input(component_id='start-capital', component_property='value'),
Input(component_id='regular-payment', component_property='value'),
Input(component_id='start-divident-percent', component_property='value'),
Input(component_id='average-cost-grow-percent', component_property='value')]
)
def update_output_div(maximum_years, divident_income_per_month, start_capital, regular_payment, start_divident_percent, average_cost_grow_percent):
max_years_number = int(maximum_years)
MONTH_COUNT_YEAR = 12
divident_income_per_month = input_to_float(divident_income_per_month, default__divident_income_per_month)
start_capital = input_to_float(start_capital, default__start_capital)
regular_payment = input_to_float(regular_payment, default__regular_payment)
start_divident_percent = input_to_float(start_divident_percent, default__start_divident_percent)
average_cost_grow_percent = input_to_float(average_cost_grow_percent, default__average_cost_grow_percent)
current_capital_no_reinv = current_capital_just_percent_reinv = current_capital_with_all_reinv = start_capital
info = {}
info['month'] = []
info['dividend_no_reinv'] = []
info['dividend_percent_reinv'] = []
info['dividend_all_reinv'] = []
info['cap_no_reinv'] = []
info['cap_percent_reinv'] = []
info['cap_all_reinv'] = []
target = {'month': 0, 'dividend': 0, 'current_capital_no_reinv': 0, 'current_capital_with_all_reinv': 0}
for month in range(max_years_number * MONTH_COUNT_YEAR):
info['month'].append(month)
current_capital_no_reinv += regular_payment
info['cap_no_reinv'].append(current_capital_no_reinv)
current_dividend_no_reinv = current_capital_no_reinv * start_divident_percent / 100 / MONTH_COUNT_YEAR
info['dividend_no_reinv'].append(current_dividend_no_reinv)
current_capital_just_percent_reinv = (current_capital_just_percent_reinv + regular_payment) * (1 + average_cost_grow_percent / 100 / MONTH_COUNT_YEAR)
info['cap_percent_reinv'].append(current_capital_just_percent_reinv)
current_dividend_just_percent_reinv = current_capital_just_percent_reinv * start_divident_percent / 100 / MONTH_COUNT_YEAR
info['dividend_percent_reinv'].append(current_dividend_just_percent_reinv)
current_dividend_with_all_reinv = current_capital_with_all_reinv * start_divident_percent / 100 / MONTH_COUNT_YEAR
info['dividend_all_reinv'].append(current_dividend_with_all_reinv)
current_capital_with_all_reinv = (current_capital_with_all_reinv + regular_payment + current_dividend_with_all_reinv) * (1 + average_cost_grow_percent / 100 / MONTH_COUNT_YEAR)
info['cap_all_reinv'].append(current_capital_with_all_reinv)
if((current_dividend_with_all_reinv >= divident_income_per_month) and not (target['month'])):
target['month'] = month
target['dividend'] = current_dividend_with_all_reinv
target['current_capital_no_reinv'] = current_capital_no_reinv
target['current_capital_with_all_reinv'] = current_capital_with_all_reinv
return create_dividends_graph(info, target, ''), create_capital_graph(info, target, ''), total_result_div(target)
Перезапускаем сервис, чтобы применить все изменения на веб сайте
# systemctl restart invests
# systemctl restart invests
# systemctl restart invests
Работа дивидендного калькулятора
Значения по-умолчанию для основных входных данных:
желаемый ежемесячный доход от дивидендов — 1000 долларов
стартовый капитал — 1000 долларов
ежемесячные пополнения брокерского счета для покупки дивидендных акций — 1000 долларов
средний процент дивидендов по акциям — 4%
средний рост цены дивидендных акций — 12.5% (на основе статистики по S&P Dividends aristocrats)
Все входные данные размещаются в редактируемых полях
Результаты можно увидеть на графиках ниже, куда поступают данные после обработки
график роста ежемесячных дивидендов:
a) получаемый за счет роста цены акций и ежемесячных ре-инвестирований дивидендов,
б) получаемый только за счет роста цены акций, т.е. если дивиденды будем выводить (без ре-инвестирования),
в) получаемый, если бы мы просто клали деньги «под подушку», без инвестирования, вообще
график роста капитала при тех же трех, рассматриваемых в первом пункте, условиях (а), б), в))
Бонусом, над графиками, представлен «ползунок» взгляда в будущее (изменяется в месяцах): что же произойдет с нашими дивидендами и капиталом, если продолжать вкладывать деньги следуя той же стратегии и далее
Итог
При взгляде на 2 графика: роста дивидендного дохода и роста капитала, можно сделать несколько интересных выводов:
значительно выгоднее вкладывать деньги в хорошие компании, чем просто хранить их
значительно выгоднее ре-инвестировать дивиденды
особенно, эффект заметен на горизонте более 10 лет
рост происходит экспоненциально
цифры по прошествии 20 лет и больше, даже, пугают
другие выводы можете сделать самостоятельно, «поиграв» с настройками, входными параметрами