Skip to content

Commit

Permalink
Merge branch 'release/0.3.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
suminb committed Aug 5, 2018
2 parents 39f8543 + 84b980a commit 2d69680
Show file tree
Hide file tree
Showing 135 changed files with 10,772 additions and 2,590 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
provision.sh
.cache
.DS_Store
frontend/src/assets
sample-data/*
*.pyc
*.swp

# Ignore .json files at root
/*.json
40 changes: 24 additions & 16 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@ sudo: false

language: python
python:
- "3.5"
- "3.6"

addons:
postgresql: "9.4"
sonarcloud:
organization: "suminb-github"

services:
- postgresql

env:
global:
- DB_URI="postgres:///finance"
- DB_URL="postgres:///finance"
- TEST_DB_URL="postgres:///finance"
- PYTHONPATH=.

install:
- pip install -r requirements.txt
- pip install -r tests/requirements.txt
- pip install -e .

before_script:
- psql -c 'CREATE DATABASE finance;' -U postgres
- finance create_all

script:
- py.test -v tests --cov finance --cov-report term-missing

after_success:
- coveralls
jobs:
include:
- stage: type checking
script:
- pip install mypy
- mypy --ignore-missing-imports finance
- stage: build
install:
- pip install -r requirements.txt
- pip install -r tests/requirements.txt
- pip install -e .
before_script:
- psql -c 'CREATE DATABASE finance;' -U postgres
- finance create_all
script:
- py.test -v tests --cov finance --cov-report term-missing
- sonar-scanner
after_success:
- coveralls
106 changes: 98 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,41 @@ Personal Finance Project
.. image:: https://coveralls.io/repos/github/suminb/finance/badge.svg?branch=develop
:target: https://coveralls.io/github/suminb/finance?branch=develop

NOTE: 제가 주로 사용하는 에디터인 vim 에서 한글 타이핑이 불편하기 때문에 영어로 문서를 작성하는 것이 일반적이지만, 이 프로젝트의 경우 한국어를 사용하는 청중이 대부분인 관계로 문서를 한국어로 작성합니다.
.. image:: https://sonarcloud.io/api/project_badges/measure?project=finance&metric=alert_status
:target: https://sonarcloud.io/dashboard?id=finance

NOTE: 제가 주로 사용하는 에디터인 vim 에서 한글 타이핑이 불편하기 때문에 영어로
문서를 작성하는 것이 일반적이지만, 이 프로젝트의 경우 한국어를 사용하는 청중이
대부분인 관계로 문서를 한국어로 작성합니다.


목표
----
이 프로젝트의 목적은 크게 두 가지입니다.

#. 총 자산 가치를 자동으로 추적하고 (비공식) SB 펀드의 가격을 산정하기.
#. 장기 가치 투자에 필요한 보조 도구들을 제공하기.


비전
----
`NDC 2016 - 프로그래머가 투자하는 법 <http://www.slideshare.net/suminb/how-programmers-invest>`_


(TODO: NDC에서 이런 발표를 하게 된 계기에 대한 설명 적어놓기)

현재 상태
---------
코드를 보신 분은 아시겠지만, 이 프로젝트는 미완성 상태입니다. 아니, 그냥 미완성 상태가 아니라 시작한지 얼마 되지 않았다고 얘기 하는편이 더 정확하겠군요. 지금 구현된 기능은 다음과 같습니다.
코드를 보신 분은 아시겠지만, 이 프로젝트는 미완성 상태입니다. 아니, 그냥 미완성
상태가 아니라 시작한지 얼마 되지 않았다고 얘기 하는편이 더 정확하겠군요. 지금
구현된 기능은 다음과 같습니다.

* 데이터베이스 모델: 기본적인 틀은 갖추어졌지만, 아직 부족한 점이 많아 앞으로 점진적으로 개선해 나갈 계획입니다. 데이터베이스 마이그레이션 도구로는 `Alembic <https://pypi.python.org/pypi/Flask-Alembic>`_ 을 사용할 계획입니다.

* 웹 인터페이스: 지금은 일자별 net asset value를 계산해서 보여주는 것만 겨우 돌아가도록 만들어놓은 상태입니다. 발표용으로 급조한거(...)

* 금융 자산 가격 가져오기: 여러가지 데이터 소스로부터 금융 자산 가격 정보를 받아옵니다.

* 주식: `야후 파이낸스 <http://finance.yahoo.com>`_ 에서 받아옵니다. 20분 지연된 정보이긴 하지만, 일 단위 가격을 받아오는 것이기 때문에 지연 시간은 중요하지 않습니다.
* 펀드: 금융투자협회(KOFIA)에 공시된 정보를 받아옵니다.
* 8퍼센트: API를 제공하지 않기 때문에 HTML을 파싱해서 정보를 가져옵니다.


Daily Net Asset Values
**********************
Expand All @@ -41,9 +53,87 @@ Daily Net Asset Values
:alt: Daily net asset values


지금 고민중인 내용들
--------------------
* `수익률 계산 <https://github.com/suminb/finance/wiki/%EC%88%98%EC%9D%B5%EB%A5%A0-%EA%B3%84%EC%82%B0>`_
* `전자공시데이터(DART) 가져오기 <https://github.com/suminb/finance/issues/1>`_


앞으로 할 일들
--------------
* 웹 인터페이스: 포트폴리오 구성을 한 눈에 볼 수 있는 인터페이스를 만들 계획입니다.
* 웹 인터페이스: 포트폴리오 구성을 한 눈에 볼 수 있는 인터페이스를 만들 계획입니다. 처음 써보는 `Angular <https://angular.io/docs/ts/latest/>`_ 로 웹 인터페이스를 작성하는 중입니다.
* 자동으로 데이터 받아오기: 주식, 펀드 가격 등 거래소에 공시되는 가격을 주기적으로 받아오는 무언가를 만들어야 합니다. 사용할 도구로는 AWS Lambda가 적당해보입니다.

(TODO: 내용 계속 채워넣기)

Some Technical Details
----------------------

Create Tables
*************

.. code::
finance create_all
Insert Test Data
****************

.. code::
finance insert_test_data
Import Stock Values
*******************

.. code::
finance fetch_stock_values 009830.KS | finance import_stock_values 009830.KS
The ``fetch_stock_values`` command strictly fetches data from Google Finance
as CSV, and the ``import_stock_values`` imports the structured data into the
database.

PostgreSQL in Docker
********************

.. code::
docker run -d \
-p 5432:5432 -e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=qwerasdf \
-e POSTGRES_DB=finance \
-v $HOME/postgres:/var/lib/postgresql/data \
-t postgres
psycopg2 on Mac
***************

If you fail to build the ``psycopg2`` package on Mac OS X with an error
message saying the following,

.. code::
ld: library not found for -lssl
You may want to build ``pscycopg2`` as follows:

.. code::
env LDFLAGS="-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib" pip install psycopg2
That's assuming you have ``openssl`` installed in your system. If you are
using ``brew`` you may install ``openssl`` as following:

.. code::
brew install openssl
SonarCloud with Travis CI
*************************

Set ``SONAR_TOKEN`` environment variable on Travis CI repository settings.
Refer `this document <https://docs.travis-ci.com/user/sonarcloud/>`_ for more
details. Then you will need to set up ``sonar-project.properties`` file as
described in `this document
<https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner>`_.
74 changes: 74 additions & 0 deletions alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# A generic, single database configuration.

[alembic]
# path to migration scripts
script_location = alembic

# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s

# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =

# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false

# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false

# version location specification; this defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions

# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8

# sqlalchemy.url = postgres://localhost/finance


# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
1 change: 1 addition & 0 deletions alembic/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generic single-database configuration.
82 changes: 82 additions & 0 deletions alembic/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from __future__ import with_statement

import os

from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig

from finance.models import db

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = db.Model.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True)

with context.begin_transaction():
context.run_migrations()


def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
# Loads database URL from an environment variable
if bool(config.get_main_option('pytest.istest')):
config.set_main_option('sqlalchemy.url', os.environ['TEST_DB_URL'])
else:
config.set_main_option('sqlalchemy.url', os.environ['DB_URL'])

connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool)

with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata
)

with context.begin_transaction():
context.run_migrations()


if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
24 changes: 24 additions & 0 deletions alembic/script.py.mako
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}

"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}

# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}


def upgrade():
${upgrades if upgrades else "pass"}


def downgrade():
${downgrades if downgrades else "pass"}

0 comments on commit 2d69680

Please sign in to comment.