[
  {
    "path": ".github/workflows/sonarcloud.yml",
    "content": "# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\n# This workflow helps you trigger a SonarCloud analysis of your code and populates\n# GitHub Code Scanning alerts with the vulnerabilities found.\n# Free for open source project.\n\n# 1. Login to SonarCloud.io using your GitHub account\n\n# 2. Import your project on SonarCloud\n#     * Add your GitHub organization first, then add your repository as a new project.\n#     * Please note that many languages are eligible for automatic analysis,\n#       which means that the analysis will start automatically without the need to set up GitHub Actions.\n#     * This behavior can be changed in Administration > Analysis Method.\n#\n# 3. Follow the SonarCloud in-product tutorial\n#     * a. Copy/paste the Project Key and the Organization Key into the args parameter below\n#          (You'll find this information in SonarCloud. Click on \"Information\" at the bottom left)\n#\n#     * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN\n#          (On SonarCloud, click on your avatar on top-right > My account > Security\n#           or go directly to https://sonarcloud.io/account/security/)\n\nname: SonarCloud analysis\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n  workflow_dispatch:\n\npermissions:\n  pull-requests: read # allows SonarCloud to decorate PRs with analysis results\n\njobs:\n  Analysis:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Analyze with SonarCloud\n\n        # You can pin the exact commit or the version.\n        # uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049\n        uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information\n          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}   # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret)\n        with:\n          # Additional arguments for the sonarcloud scanner\n          args:\n            # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu)\n            # mandatory\n            -Dsonar.projectKey=\n            -Dsonar.organization=\n            -X\n            # Comma-separated paths to directories containing main source files.\n            #-Dsonar.sources= # optional, default is project base directory\n            # When you need the analysis to take place in a directory other than the one from which it was launched\n            #-Dsonar.projectBaseDir= # optional, default is .\n            # Comma-separated paths to directories containing test source files.\n            #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/\n            # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing.\n            #-Dsonar.verbose= # optional, default is false\n    \n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# IPython Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# dotenv\n.env\n\n# virtualenv\nvenv/\nENV/\n\n# Spyder project settings\n.spyderproject\n\n# Rope project settings\n.ropeproject\n\n# Release jobs\nrelease.sh\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 0.9.4 (31.05.2017\n* added support for export to Excel\n\n# 0.9.3 (13.01.2017)\n* changed some bugs for rendering HTML content\n\n# 0.9.2 (05.01.2017)\n* financial_life now supports meta-data for account objects and payments\n\n# 0.9.1\n* minor issues with DataFrame support\n* plots in examples are displayed, when examples are called from command line\n* improved setup-script\n\n# 0.9\n* financial-life supports DataFrames\n\n# 0.8\n* first release on Github\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MANIFEST",
    "content": "# file GENERATED by distutils, do NOT edit\nsetup.py\nfinancial_life/__init__.py\nfinancial_life/test_general.py\nfinancial_life/calendar_help/__init__.py\nfinancial_life/examples/__init__.py\nfinancial_life/examples/dependencies.py\nfinancial_life/examples/meta_data.py\nfinancial_life/examples/simple_examples.py\nfinancial_life/financing/__init__.py\nfinancial_life/financing/accounts.py\nfinancial_life/financing/colors.py\nfinancial_life/financing/identity.py\nfinancial_life/financing/plotting.py\nfinancial_life/financing/test_financing.py\nfinancial_life/financing/test_meta.py\nfinancial_life/financing/test_status.py\nfinancial_life/financing/validate.py\nfinancial_life/products/germany/lbs/__init__.py\nfinancial_life/reports/__init__.py\nfinancial_life/reports/html.py\nfinancial_life/tax/germany/__init__.py\nfinancial_life/templates/html/standard/__init__.py\nfinancial_life/templates/html/standard/account_details.html\nfinancial_life/templates/html/standard/index.html\nfinancial_life/templates/html/standard/render.py\n"
  },
  {
    "path": "README.md",
    "content": "# financial-life\nA framework for analysing financial products in personalized contexts\n\n<table>\n<tr>\n\t<td>\n\tLatest Release\n\t</td>\n\t<td>\n\t<img src=\"https://img.shields.io/pypi/v/financial_life.svg\" alt=\"latest release\" />\n\t</td>\n</tr>\n<table>\n\n[CHANGELOG.md](CHANGELOG.md)\n\n# Description\n\nfinancial_life is an opinionated framework written in Python that allows to simulate monetary flows between different types of accounts. These simulations allow a deeper understanding of financial plans and a better comparison of financial products (in particular loan conditions) for personal circumstances. With financial_life you can\n\n* analyse loan conditions and payment strategies\n* describe the properties of your financial plans with a few lines of code\n* create dynamic monetary flows between accounts for modeling more realistic scenarios\n* extend the code by controller functions (e.g. for modeling tax payments)\n\nView [documentation](docs/README.md) for a more detailed introduction.\n\n# Example\nSay you want to model an account with regular income and payments to a loan\n\n```python\nfrom financial_life.financing import accounts as a\nfrom datetime import timedelta, datetime\n\n# create a private bank account and a loan account\naccount = a.Bank_Account(amount = 1000, interest = 0.001, name = 'Main account')\nloan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit')\n\n# add these accounts to the simulation\nsimulation = a.Simulation(account, loan)\n\n# describe monetary flows between accounts\nsimulation.add_regular('Income', account, 2000, interval = 'monthly')\nsimulation.add_regular(account, loan, lambda: min(1500, -loan.account), interval = 'monthly')\n\n# simulate for ten years\nsimulation.simulate(delta = timedelta(days=365*10))\n\n# plot the data\nsimulation.plt_summary()\n\n# print reports summarized by years\nprint(account.report.yearly())\nprint(loan.report.yearly())\n\n# analyze data\nprint(\"Interests on bank account: %.2f\" % sum(account.report.yearly().interest))\nprint(\"Interests on loan account: %.2f\" % sum(loan.report.yearly().interest))\n```  \n\nThe output will look like this:\n\n<img src=\"docs/img/simple_example_01_small.png\" alt=\"Simple simulation in financial_life\" width=\"800\">\n\n\n\tMain account\n\tDate          account     output     input    interest\n\t----------  ---------  ---------  --------  ----------\n\t31.12.2016    2000.32   -3000.00   4000.00        0.32\n\t31.12.2017    8005.58  -18000.00  24000.00        5.26\n\t31.12.2018   14016.85  -18000.00  24000.00       11.27\n\t31.12.2019   20034.13  -18000.00  24000.00       17.28\n\t31.12.2020   26057.42  -18000.00  24000.00       23.29\n\t31.12.2021   32086.74  -18000.00  24000.00       29.32\n\t31.12.2022   46271.00   -9853.30  24000.00       37.56\n\t31.12.2023   70330.32       0.00  24000.00       59.32\n\t31.12.2024   94413.68       0.00  24000.00       83.36\n\t31.12.2025  118521.15       0.00  24000.00      107.47\n\t01.10.2026  138521.15       0.00  20000.00        0.00\n\tHouse Credit\n\tDate          account    interest    payment\n\t----------  ---------  ----------  ---------\n\t31.12.2016  -97190.22     -190.22    3000.00\n\t31.12.2017  -80064.23     -874.01   18000.00\n\t31.12.2018  -62766.98     -702.75   18000.00\n\t31.12.2019  -45296.76     -529.78   18000.00\n\t31.12.2020  -27652.02     -355.26   18000.00\n\t31.12.2021   -9830.65     -178.63   18000.00\n\t31.12.2022       0.00      -22.65    9853.30\n\t31.12.2023       0.00        0.00       0.00\n\t31.12.2024       0.00        0.00       0.00\n\t31.12.2025       0.00        0.00       0.00\n\tInterests on bank account: 374.45\n\tInterests on loan account: -2853.30\n\nNow let's say, we put some money on a special savings account with better interests, because we want to purchase in two years a car. With financial_life, you just add the necessary changes to your model.\n\n```python\n# create new account\nsavings = a.Bank_Account(amount = 5000, interest = 0.007, name = 'Savings')\n\n# add it to the simulation (or create a new simulation with all three accounts)\nsimulation.add_account(savings)\n\n# add regular payment to the savings-account\nsimulation.add_regular(account, savings, 500, interval = 'monthly')\n\n# somewhere in the distant future we will make a payment to\n# the vendor of a car\nsimulation.add_unique(savings, 'Vendor of a car', 10000, '17.03.2019')\n```\n\nThe plot will now include the savings-account as well.\n\n<img src=\"docs/img/simple_example_02_small.png\" alt=\"Simple simulation in financial_life\" width=\"800\">\n\nYou can also export the simulation to HTML to explore your model in the browser:\n\n```python\nfrom financial_life.reports import html\n\nhtml.report(simulation, style=\"standard\", output_dir = result_folder)\n```\n\n<img src=\"docs/img/html_summary_01.png\" alt=\"Simple simulation in financial_life\" width=\"800\" height=\"407\">\n\nYou can analyse the reports as [pandas](https://github.com/pandas-dev/pandas) DataFrame as well and export it to excel:\n\n```python\nimport pandas as pd\nfrom financial_life.reports import excel\n\naccount.report.as_df()    # Hello pandas\nexcel.report(simulation, filename='reports.xls')  # explore the results in excel\n\n```\n\n[Here](financial_life/examples/README.md) are more examples. financial_life supports:\n* [dependencies between accounts](financial_life/examples/dependencies.py), e.g. to model how the ownership of a property rises when the loan decreases\n* [meta-data](financial_life/examples/meta_data.md), e.g. for writing tax-calculations, which require additional knowledge about your payments\n* [controller-functions](financial_life/examples/meta_data.md) for dynamic changes of the simulation properties during simulation\n\n# Installation\n\nfinancial_life is available in version 0.9.2. It is written in Python 3.4 and has not been tested for Python 2.x.\n\nTo get a working environment, simply do\n\n\tgit clone https://github.com/MartinPyka/financial_life.git\n\tcd financial_life\n\tvirtualenv venv\n\tsource venv/bin/activate\n\tpip install -r requirements.txt\n\t# test an example\n\tpython financial_life/examples/simple_examples.py\n\nFor installing the package:\n\n\tgit clone https://github.com/MartinPyka/financial_life.git\n\tcd financial_life\n\tpython setup.py install\n\nOr use pip\n\n \tpip install financial_life\n\nYou can checkout the example with\n\n\tpython financial_life/examples/simple_examples.py\n\n# Why financial_life\n\nfinancial_life was designed with the idea in mind that any line of code should contribute to the description of the problem you want to model. In spreadsheets, you would deal with a lot of auxiliary tables to accurately calculate the course of a loan influenced by incoming payments and generated interests. In financial_life, you just create your loan account with the given interests rate and you define the regular payments going into this loan account. That's it. Changes in the model and the exploration of different parameters within this model are therefore way easier to accomplish than in a spreadsheet-based simulation.\n"
  },
  {
    "path": "docs/01_first_simulation.md",
    "content": "# The first simulation\n\nIn this chapter, we are going to introduce the basic concepts of creating a simulation in order to analyse monetary flows and growth on bank accounts.\n\nIn the following, financial_life will be abbreviated with fl.\n\nEach simulation setup can be devided into three different steps:\n\n1. Define the accounts involved in the simulation\n2. Define monetary flows between these accounts\n3. Simulate\n\nAfter that you have several options to explore the outcome of your simulation.\n\n## 1. Define the accounts involved in the simulation\n\nThe module `financial_life.financing.accounts` features some basic accounts like a normal bank account with yearly interests and a loan account that does not allow to be in the positive value range and only permits money transferd to the account but not from the account.\n\nThe package also contains a Property account that establishes a dependency to a loan account. We will come to that later.\n\nLets start with creating an ordinary bank account, which you would use to receive your salary and pay your bills. This account is called `Bank_Account` and it is defined in the following way:\n\n```python\nclass Bank_Account(Account):\n    \"\"\" This is a normal bank account that can be used to manage income and\n    outgoings within a normal household \"\"\"\n    def __init__(self, amount, interest, date = None, name = None):\n```\n<table>\n  <tr>\n    <th>\n      Keyword Argument\n    </th>\n    <th>\n      Description\n    </th>\n  </tr>\n  <tr>\n    <td>\n      amount\n    </td>\n    <td>\n      The amount of money to start with\n    </td>\n  </tr>\n  <tr>\n    <td>\n      interest\n    </td>\n    <td>\n      The yearly interest rate for this account. 1% correspond to 0.01 as argument value\n    </td>\n  </tr>\n  <tr>\n    <td>\n      date\n    </td>\n    <td>\n      The date, when this account should exist. Before this date, the account will not be simulated. For quick simulation, where this nuance is not relevant, this field can be left empty. As date, datetime and strings in the format '%d.%m.%y', '%m/%d/%Y' are accepted.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      name\n    </td>\n    <td>\n      Simply a string representation of the account id. If empty, fl will create a random string sequence.\n    </td>\n  </tr>\n</table>\n\nSo an ordinary bank account with a volume of 1000 and an interest rate of 0.1% is defined like this.\n\n```python\nfrom financial_life.financing import accounts as a\n\naccount = a.Bank_Account(amount = 1000,\n                         interest = 0.001,\n                         name = 'Main account')\n```\n\nA `loan` account, although its behavior is slightly different, is defined in the same manner. For a loan of 100,000 you would create a `loan` account like this:\n\n```python\nloan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit')\n```\n\nLast but not least, you need to assign these accounts to a simulation. This is a one-liner as well.\n\n```python\nsimulation = a.Simulation(account, loan)\n```\n\n## 2. Define monetary flows between these accounts\n\nNext. we define monetary flows between these accounts. Here, we will introduce the methods for regular and unique payments but we will cover only a part of it. In later chapters, we will dig deeper into the kind of stuff that you can create with it.\n\nThe simulation class is also defined in `financial_life.financing.accounts` and looks like this\n\n```python\ndef add_regular(self, from_acc, to_acc, payment, interval,\n                date_start=datetime.min,\n                day=1,\n                name = '',\n                date_stop = None,\n                fixed = False):\n\ndef add_unique(self, from_acc, to_acc, payment,\n               date,\n               name = '',\n               fixed = False):             \n```\n<table>\n  <tr>\n    <th>\n      Keyword Argument\n    </th>\n    <th>\n      Description\n    </th>\n  </tr>\n  <tr>\n    <td>\n      from_acc,\n      to_acc\n    </td>\n    <td>\n      The account objects that define from where money flows and to which account money flows. For money transfers that involve accounts outside of the simulation, a string can be used.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      payment\n    </td>\n    <td>\n      The amount of money that is transfered.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      interval\n    </td>\n    <td>\n      'monthly' or 'yearly' are possible currently.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      date_start / date\n    </td>\n    <td>\n      The start date of this regular payments. It can be either a datetime object or a string. It has also a default value, so it does not need to be used for quick simulations.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      day\n    </td>\n    <td>\n      The day in a month on which this transfer should be initiated.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      name\n    </td>\n    <td>\n      Name of the money transfer. Corresponds to the subject in a normal money transfer.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      date_stop\n    </td>\n    <td>\n      Stop date of regular payments. Can be either datetime, string or even a callable. We will cover this later.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      fixed\n    </td>\n    <td>\n      Whether sender and receiver of the money should insist of the full money transfer. If, for example, a loan has only 100 to be payed, but the transfer defines 150, this would cause an error, when `fixed` is true. If it is false, the rest money is transfered back.\n    </td>\n  </tr>\n</table>\n\nHere are two examples of how money flows can be defined. The first one describes the salary, coming from an account outside of this simulation. The second one is a money transfer within our simulation.\n\n```python\nsimulation.add_regular('Income', account, 2000, interval = 'monthly')\nsimulation.add_regular(account, loan, 1500, interval = 'monthly')\n```\n\n## 3. Simulate\n\nThe simulation is started for a given amount of time. This period is either defined through a stop date or by a time interval delta. If both keywords are used the earlier stop date ends the simulation.\n\n```python\ndef simulate(self, date_stop = None, delta = None)\n```\n\n<table>\n  <tr>\n    <th>\n      Keyword Argument\n    </th>\n    <th>\n      Description\n    </th>\n  </tr>\n  <tr>\n    <td>\n      date_stop\n    </td>\n    <td>\n      Stop date of the simulation\n    </td>\n  </tr>\n  <tr>\n    <td>\n      delta\n    </td>\n    <td>\n      Number of days to simulate.\n    </td>\n  </tr>\n</table>\n\nIn order to simulate our model for 10 years from now on, we use the delta-keyword.\n\n```python\nsimulation.simulate(delta = timedelta(days=365*10))\n```\n\n## Explore the simulation\n\nHere are some examples of exploring the outcome of your data.\n\n```python\n# use matplotlib for a graphical summary of the simulation\nsimulation.plt_summary()\n\n# print reports summarized in years\nprint(account.report.yearly())\nprint(loan.report.yearly())\n\n# analyze data\nprint(\"Interests on bank account: %.2f\" % sum(account.report.interest))\nprint(\"Interests on loan account: %.2f\" % sum(loan.report.interest))\n\n# create html report\ncwd = os.path.dirname(os.path.realpath(__file__))\nresult_folder = cwd + '/example2'\nhtml.report(simulation, style=\"standard\", output_dir = result_folder)\n```\n\n## Code snippet\n\nAnd here is the code for the simulation again as a whole\n\n```python\naccount = a.Bank_Account(amount = 1000, interest = 0.001, name = 'Main account')\nloan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit')\n\nsimulation = a.Simulation(account, loan)\n\nsimulation.add_regular('Income', account, 2000, interval = 'monthly')\nsimulation.add_regular(account, loan, 1500, interval = 'monthly')\n\nsimulation.simulate(delta = timedelta(days=365*10))\nsimulation.plt_summary()\n```\n"
  },
  {
    "path": "docs/02_using_callables_for_dynamic_changes.md",
    "content": "# Using callables for dynamic changes\n\nSometimes, parameters like the amount of money to be transfered or the stop date cannot be statically defined at the beginning of your simulation. Instead, they depend on the current state of your simulation.\n\nTherefore, fl supports callables as arguments for some keywords, in order to cover more complex modeling scenarios.\n\n## Dynamic payments\n\nCallables can be used to determine the amount of money to be transfered from one account to the other. This means, instead of writing\n\n```python\nsimulation.add_regular(from_acc=account,\n                       to_acc=loan,\n                       payment=1500,\n                       interval='monthly')\n```\n\nwe can make sure, that we transfer only the amount of money to the loan account that is necessary: either 1500 or what is left. We can achieve this with a simple lambda-function.\n\nThe loan account provide the property `loan.account`, which returns the debts we still need to pay. This is a negative number, therefore, we must put a minus-sign in front of it:\n\n```python\nsimulation.add_regular(from_acc=account,\n                       to_acc=loan,\n                       payment=lambda: min(1500, -loan.account),\n                       interval='monthly')\n```\n\nLet's go a step further and say we want to transfer at maximum 1500 but also make sure that we always have 2000 on our account and that we transfer only the money that is really need on the loan account.\n\n```python\nsimulation.add_regular(from_acc=account,\n                       to_acc=loan,\n                       payment=lambda: min(\n                                          max(\n                                              min(1500, account.account-2000),\n                                              0),\n                                          -loan.account),\n                       interval='monthly')\n```\n\nThe max-statement in this lambda-function is included to make sure that when `account.account - 2000` is a negative number, we won't initiate a negative transfer to the loan-account (which would be captured by the loan-account anyway).\n\nThese callables are executed in each simulation cycle (which is every day within the simulation) and therefore decide depending on the simulation state how much money is transfered to the loan account.\n"
  },
  {
    "path": "docs/03_dependencies_between_accounts.md",
    "content": "# Dependencies between accounts\n\nModeling loan accounts can be helpful to calculate payment durations and interests costs. But modeling them alone, does not reflect the accumulation of asset that one gains by decreasing the amount of debts. For example, if you start a loan in order to buy a house, you would like to take into account that the value of the house is more and more part of your asset the more you decrease the loan. This means, there is a dependency between the loan you pay and the value of the house that belongs to your asset.\n\nIn fl, there is a class `Property` in order to model this relationship. And it can be used in the following way:\n\n```python\nloan = a.Loan(200000, 0.0185, name = 'Credit' )\nhouse = a.Property(200000, 0, loan, name='House')\n```\n\nYou create a new object of type `Property`, in which you state the value of the property, the amount of own capital you put into it, and the loan account on which it depends. When during the simulation, the loan goes down, the property will go up.\n\n<img src=\"img/dependencies.png\" alt=\"Dependency between loan and house\" width=\"800\">\n\nHere is the [example code](../financial_life/examples/dependencies.py)\n\nThe class `Property` is defined in the following way:\n\n```python\nclass Property(Account):\n    \"\"\"\n    This class can be used to reflect the amount of property that is gained\n    from filling up a loan. This account does nothing else than adjusting the\n    amount of property depending on the payments transfered to the loan class\n    \"\"\"\n\n    def __init__(self, property_value, amount, loan, date = None, name = None):\n```\n\n<table>\n  <tr>\n    <th>\n      Keyword Argument\n    </th>\n    <th>\n      Description\n    </th>\n  </tr>\n  <tr>\n    <td>\n      property_value\n    </td>\n    <td>\n      The value of the property.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      amount\n    </td>\n    <td>\n      The amount of money that is already part of the own property.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      loan\n    </td>\n    <td>\n      A loan object to which a dependency is created.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      date\n    </td>\n    <td>\n      The date, when this account should exist. Before this date, the account will not be simulated. For quick simulation, where this nuance is not relevant, this field can be left empty. As date, datetime and strings in the format '%d.%m.%y', '%m/%d/%Y' are accepted.\n    </td>\n  </tr>\n  <tr>\n    <td>\n      name\n    </td>\n    <td>\n      Simply a string representation of the property id. If empty, fl will create a random string sequence.\n    </td>\n  </tr>\n</table>\n"
  },
  {
    "path": "docs/README.md",
    "content": "# Documentation\n\nThis documentations aims to introduce the user into the concepts of financial_life to start developing your own applications.\n\n## User guide\n\n1. [The first simulation](01_first_simulation.md)\n2. [Using callables for dynamic changes](02_using_callables_for_dynamic_changes.md)\n3. [Dependencies between accounts](03_dependencies_between_accounts.md)\n\n## Developers guide\n\n1. Short intro into the main routines\n2. Adding your own account class\n"
  },
  {
    "path": "financial_life/README.md",
    "content": "# Enter the code\n\nHere, you are about to enter the code part of this repository. You might want to check out the [examples folder](examples/), where you will find another [README.md](examples/README.md). The other folders do not contain an MD file anymore.\n"
  },
  {
    "path": "financial_life/__init__.py",
    "content": "'''\nCreated on 03.12.2016\n\n@author: martin\n'''\n\n__version__ = '0.9.4'\n"
  },
  {
    "path": "financial_life/calendar_help/__init__.py",
    "content": "from calendar import monthrange\nfrom datetime import date\nfrom datetime import timedelta\nfrom datetime import datetime\n\nclass Bank_Date(datetime):\n    \"\"\" This is a helper class that adds some additional functionality\n    to the datetime class, like adding months to the date or calculating\n    the difference between two dates in months    \n    \"\"\"\n    def is_end_of_month(self):\n        \"\"\" returns true, if the current day is the end of month \"\"\"\n        return monthrange(self.year, self.month)[1] == self.day\n    \n    def add_month(self, months):\n        \"\"\" introduces calculation with months \"\"\"\n        new_year = self.year + int((self.month + months - 1)/12)\n        new_month = ((self.month + months - 1) % 12) + 1\n        new_day = min(self.day, monthrange(new_year, new_month)[1])\n        return Bank_Date(year = new_year, month = new_month, day = new_day)\n                   \n    def diff_months(self, sub2):\n        \"\"\" calculates the differences in months between two dates \"\"\"\n        if not isinstance(sub2, datetime):\n            raise NotImplementedError\n                        \n        years = 0\n        months = 0\n        if (sub2.year > self.year):\n            years = max(0, sub2.year - (self.year + 1))\n            months = (12 - self._month + 1) + sub2.month\n        elif (sub2.year == self.year):\n            months = sub2.month - self.month\n        elif sub2.year < self.year:\n            years = min(0, sub2.year + 1 - self.year)\n            months = -(12 - sub2.month + 1) - self.month\n            \n        return years * 12 + months\n    \n\ndef get_days_per_year(year):\n    # returns the number of days per year\n    return 365 if monthrange(year, 2)[1] == 28 else 366\n\n# deprecated, old methods for maniuplating datetime\n\ndef add_month(start_date, months):\n    \"\"\" introduces calculation with months \"\"\"\n    new_year = start_date.year + int((start_date.month + months - 1)/12)\n    new_month = ((start_date.month + months - 1) % 12) + 1\n    new_day = min(start_date.day, monthrange(new_year, new_month)[1])\n    new_date = date(new_year, new_month, new_day)\n    return new_date\n                   \ndef diff_months(sub1, sub2):\n    \"\"\" calculates the differences in months between two dates \"\"\"\n    years = 0\n    months = 0\n    if (sub2.year > sub1.year):\n        years = max(0, sub2.year - (sub1.year + 1))\n        months = (12 - sub1.month + 1) + sub2.month\n    elif (sub2.year == sub1.year):\n        months = sub2.month - sub1.month\n    elif sub2.year < sub1.year:\n        years = min(0, sub2.year + 1 - sub1.year)\n        months = -(12 - sub2.month + 1) - sub1.month\n        \n    return years * 12 + months"
  },
  {
    "path": "financial_life/constants/__init__.py",
    "content": ""
  },
  {
    "path": "financial_life/constants/intervals.py",
    "content": "'''\nCreated on 20.01.2017\n\nThis module lists all intervals, that can be used through out fl\n\n@author: martin\n'''\n\ndaily = 'daily'\nmonthly = 'monthly'\nyearly = 'yearly'"
  },
  {
    "path": "financial_life/examples/README.md",
    "content": "# Examples\n\nIn this folder, you find a list of examples that will help you getting started.\n\n### simple_examples.py\n\n[simple_examples.py](simple_examples.py) shows three beginner examples that are also highlighted on the [front-page](../../README.md) of this repository.\n\n### dependencies.py\n\n[dependencies.py](dependencies.py) showcases the account-class `Property`, which uses access to a loan class to determine, how much value of a given property has been transfered to the owner, when the loan is decreased. The key lines are:\n\n```python\nloan = a.Loan(200000, 0.0185, name = 'Credit' )\n# the class property defines a dependency on loan. When loan\n# decreases, the house-property increases\nhouse = a.Property(200000, 0, loan, name='House')\n```\n\nThe value of `house` will becomes bigger when the value of `loan` decreases. See also more information about this construct in the [documentation](../../docs/03_dependencies_between_accounts.md).\n\n### meta_data.py\n\n[meta_data.py](meta_data.py) demonstrates the usage of meta-data and controllers, in order to model tax returns. A full explanation of this example can be found [here](meta_data.md).\n"
  },
  {
    "path": "financial_life/examples/__init__.py",
    "content": ""
  },
  {
    "path": "financial_life/examples/dependencies.py",
    "content": "'''\nCreated on 14.08.2016\n\n@author: martin\n'''\n# standard libraries\nfrom datetime import timedelta, datetime\nimport os\n\n# third-party libraries\n\n# own libraries\nfrom financial_life.financing import accounts as a\nfrom financial_life.reports import html\nfrom matplotlib.pyplot import show\n\ndef dependencies():\n    loan = a.Loan(200000, 0.0185, name = 'Credit' )\n    \n    # the class property defines a dependency on loan. When loan\n    # decreases, the house-property increases\n    house = a.Property(200000, 0, loan, name='House')\n\n    simulation = a.Simulation(loan, house)\n\n    simulation.add_regular('Income', loan, 1000, interval = 'monthly')\n\n    simulation.simulate(delta = timedelta(days=365*20))\n    simulation.plt_summary()\n    show(block=True)\n\nif __name__ == '__main__':\n    dependencies()\n"
  },
  {
    "path": "financial_life/examples/meta_data.md",
    "content": "# Meta-Fields and Controller functions\n\nYou can attach meta-data to any account class and any payment. These meta-data can be used to facilitate more complex calculations within your simulations, like the correct calculation of tax payments. With the help of controller-functions you can access these information to incorporate them into your simulation.\n\nMeta-data can be attached like this in your simulation definition:\n\n```python\n# meta data for account classes, like loans\nloan = a.Loan(amount = 100000,\n              interest = 0.01,\n              name = 'House Credit',\n              date=\"01.09.2016\",\n              meta = {'tax': {'outcome': 'yearly_interests'}}\n              )\n\n# meta data for payments\nsimulation.add_regular('Income', account, 2000,\n                       interval = 'monthly',\n                       date_start=\"01.09.2016\",\n                       meta={'type': 'income',\n                             'tax': {\n                                     'brutto': 2500,\n                                     'paid': 310,\n                                     'insurance': 190\n                                     }\n                            }\n                       )\n```\n\nfinancial_life let's you add controller-functions to your simultions, which are executed every day before money is transfered between accounts. The controller-function is called with the simulation-object as argument:\n\n```python\ndef controller_tax(s):\n    \"\"\" This is a controller function that calculates annual tax rates\n    's' is the simulation object\n    \"\"\"\n    # Do something, e.g. check, how much brutto income have been\n    # transfered to the account and home much interests from the\n    # loan account must be subtracted from it. See meta_data.py\n    # for a full example\n\n# add controller function to simulation\nsimulation.add_controller(controller_tax)\n```\n\nWith `account.report.with_meta()` or `simulation.report.with_meta()` you can make these information visible, when you print the report.\n\nThe result will look like this:\n```\nMain account\nDate        description    kind               interest    input  foreign_account      account    output  Meta\n----------  -------------  ---------------  ----------  -------  -----------------  ---------  --------  ---------------------------------------------------------------------------------------------------------------------------------------------------------------\n01.09.2016                                        0.00     0.00                       1000.00      0.00  {}\n01.09.2016                 regular                0.00  2000.00  Income               3000.00      0.00  {'type': 'income', 'tax': {'insurance': 190, 'paid': 310, 'brutto': 2500}}\n01.09.2016                 regular                0.00     0.00  House Credit         1500.00  -1500.00  {}\n01.10.2016                 regular                0.00  2000.00  Income               3500.00      0.00  {'type': 'income', 'tax': {'insurance': 190, 'paid': 310, 'brutto': 2500}}\n01.10.2016                 regular                0.00     0.00  House Credit         2000.00  -1500.00  {}\n01.11.2016                 regular                0.00  2000.00  Income               4000.00      0.00  {'type': 'income', 'tax': {'insurance': 190, 'paid': 310, 'brutto': 2500}}\n01.11.2016                 regular                0.00     0.00  House Credit         2500.00  -1500.00  {}\n01.12.2016                 regular                0.00  2000.00  Income               4500.00      0.00  {'type': 'income', 'tax': {'insurance': 190, 'paid': 310, 'brutto': 2500}}\n01.12.2016                 regular                0.00     0.00  House Credit         3000.00  -1500.00  {}\n31.12.2016                 yearly interest        0.75     0.00                       3000.75      0.00  {}\n01.01.2017                 regular                0.00  2000.00  Income               5000.75      0.00  {'type': 'income', 'tax': {'insurance': 190, 'paid': 310, 'brutto': 2500}}\n```\nSee the full example [here](meta_data.py).\n"
  },
  {
    "path": "financial_life/examples/meta_data.py",
    "content": "'''\nCreated on 21.12.2016\n\n@author: martin\n'''\n# standard libraries\nfrom datetime import timedelta\n\n# third-party libraries\n\n# own libraries\nfrom financial_life.financing import accounts as a\nfrom financial_life.tax import germany as tax_ger\n\n\ndef controller_tax(s):\n    \"\"\" This is a controller function that calculates annual tax rates\n    's' is the simulation object\n    \"\"\"\n    # perform tax calculation always on 15th of February, just to\n    # reflect the fact that tax payments for the previous year are\n    # never made on the 1st of January, which has an impact on \n    # interests as well\n    if ((s.current_date.month == 2) and \n       (s.current_date.day == 15)):\n        # account class for payments\n        account = s.accounts[0]\n        \n        # filter for all transactions that occured in the previous year\n        # and of type 'income'\n        income_report = s.report.subset(lambda st: (st.date.year == (s.current_date.year-1)) and \n                                                   (st.meta.get('type','') == 'income'))\n        \n        # using list comprehensions, we can easily calculate a few sums\n        #m_income = sum(income.value)  \n        m_brutto = sum(payment.meta['tax']['brutto'] for payment in income_report)\n        m_paid = sum(payment.meta['tax']['paid'] for payment in income_report)\n        \n        # get all accounts which have the field tax.outcome == 'yearly_interests'\n        loans = [account for account in s.accounts \n                 if account.meta.get('tax', {}).get('outcome','') == 'yearly_interests']\n        # get only the reports of last year\n        interests_reports = [loan.report.subset(lambda st: st.date.year == (s.current_date.year-1)) \n                             for loan in loans]\n        # sum up all interests from all interests reports\n        m_interests = sum(sum(report.interest) for report in interests_reports)                \n        \n        # as interests for loans are negative, we effectively\n        # subtract the payed interests from the brutto we earned in the last year\n        m_tax_relevant_money = m_brutto + m_interests\n        # now, we apply german tax rules from 2016 to the tax-relevant money\n        m_tax, m_tax_percentage = tax_ger.tax_to_pay(2016, m_tax_relevant_money)\n        # this is the money we either receive from the state (positive value\n        # or we need to pay (negative value)\n        m_diff = m_paid - m_tax\n        \n        s.add_unique('State', account, m_diff, \n                     date = s.current_date + timedelta(days=1),\n                     name = 'Tax',\n                     fixed = True,\n                     meta = {\n                             'taxpayment': {\n                                            'tax_relevant_money': m_tax_relevant_money,\n                                            'tax_to_pay': m_tax,\n                                            'tax_percentage': m_tax_percentage,\n                                            'paid': m_paid,\n                                            'difference': m_diff\n                                            }\n                             }\n                     )\n        \n    \ndef example_meta_controller(print_it = True):\n    \"\"\" This example shows, how meta-information for payments and account data could\n    be used to calculate annual tax-return \"\"\"\n    account = a.Bank_Account(amount = 1000, interest = 0.001, name = 'Main account', date=\"01.09.2016\")\n    \n    # define meta-data for accounts. here: some fields that are relevant for \n    # tax calculations \n    loan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit', date=\"01.09.2016\",\n                  meta = {'tax': {\n                                  'outcome': 'yearly_interests' \n                                  }\n                          }\n                  )\n    \n    # add these accounts to the simulation\n    simulation = a.Simulation(account, loan, date='01.09.2016')\n\n    # our employee receives monthly 2000 netto, coming from 2500 brutto,\n    # 310 are subtracted directly from the loan, which is less than she\n    # needs to pay. 190 are paid for insurance\n    simulation.add_regular('Income', account, 2000, \n                           interval = 'monthly', \n                           date_start=\"01.09.2016\", \n                           meta={'type': 'income', \n                                 'tax': {\n                                         'brutto': 2500, \n                                         'paid': 310,\n                                         'insurance': 190\n                                         }\n                                }\n                           )\n\n    simulation.add_regular(account, loan, lambda: min(1500, -loan.account), \n                           interval = 'monthly', \n                           date_start=\"01.09.2016\")\n    simulation.add_controller(controller_tax)\n\n    # simulate for ten years\n    simulation.simulate(delta = timedelta(days=365*10))\n\n    # this function is also part of a unittest. Therefore, we want to be able to\n    # control, whether we print some information or not\n    if print_it:\n        # print reports summarized in years\n        print(account.report.with_meta())\n        #print(loan.report.with_meta())\n    return simulation\n\nif __name__ == '__main__':\n    example_meta_controller()\n"
  },
  {
    "path": "financial_life/examples/simple_examples.py",
    "content": "'''\nCreated on 14.08.2016\n\n@author: martin\n'''\n# standard libraries\nfrom datetime import timedelta, datetime\nimport os\n\n# third-party libraries\nfrom matplotlib.pyplot import show\n\n# own libraries\nfrom financial_life.financing import accounts as a\nfrom financial_life.reports import html\n\ndef example1():\n    # create a private bank account and a loan\n    account = a.Bank_Account(amount = 1000, interest = 0.001, name = 'Main account')\n    loan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit')\n\n    # add these accounts to the simulation\n    simulation = a.Simulation(account, loan)\n\n    # describe single or regular payments between accounts. note, that\n    # a string can be used for external accounts that you don't want to model.\n    # also note the lambda function for the payments to the loan.\n    simulation.add_regular('Income', account, 2000, interval = 'monthly')\n    # you can also use lambda function to dynamically decide how much money\n    # you would like to transfer\n    simulation.add_regular(account, loan, lambda: min(1500, -loan.account), interval = 'monthly')\n\n    # simulate for ten years\n    simulation.simulate(delta = timedelta(days=365*10))\n    # plot the data\n    simulation.plt_summary()\n    show()\n\n    # print reports summarized in years\n    print(account.report.yearly().as_df())\n    print(loan.report.yearly().as_df())\n\n    # analyze data\n    print(\"Interests on bank account: %.2f\" % sum(account.report.yearly().interest))\n    print(\"Interests on loan account: %.2f\" % sum(loan.report.yearly().interest))\n\n    return simulation\n\ndef example2():\n    # create a private bank account and a loan\n    account = a.Bank_Account(amount = 1000, interest = 0.001, name = 'Main account')\n    savings = a.Bank_Account(amount = 5000, interest = 0.007, name = 'Savings')\n    loan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit')\n\n    # add these accounts to the simulation\n    simulation = a.Simulation(account, savings, loan)\n\n    # describe single or regular payments between accounts. note, that\n    # a string can be used for external accounts that you don't want to model.\n    # also note the lambda function for the payments to the loan.\n    simulation.add_regular('Income', account, 2000, interval = 'monthly')\n    simulation.add_regular(account, savings, 500, interval = 'monthly')\n    simulation.add_regular(account, loan, lambda: min(1500, -loan.account), interval = 'monthly')\n    simulation.add_unique(savings, 'Vendor for car', 10000, '17.03.2019')\n\n    # simulate for ten years\n    simulation.simulate(delta = timedelta(days=365*10))\n    # plot the data\n    simulation.plt_summary()\n\n    # print reports summarized in years\n    print(account.report.yearly().as_df())\n    print(loan.report.yearly().as_df())\n    \n    # analyze data\n    print(\"Bank account: %.2f\" % (account.account + savings.account))\n\n    cwd = os.path.dirname(os.path.realpath(__file__))\n    result_folder = cwd + '/example2'\n\n    html.report(simulation, style=\"standard\", output_dir = result_folder)\n    show()\n\ndef example3():\n    account = a.Bank_Account(amount = 1000, interest = 0.001, name = 'Main account')\n    savings = a.Bank_Account(amount = 5000, interest = 0.013, name = 'Savings')\n    loan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit')\n\n    simulation = a.Simulation(account, savings, loan, name = 'Testsimulation')\n    simulation.add_regular(from_acc = 'Income',\n                           to_acc = account,\n                           payment = 2000,\n                           interval = 'monthly',\n                           date_start = datetime(2016,9,15),\n                           day = 15,\n                           name = 'Income')\n\n    simulation.add_regular(from_acc = account,\n                           to_acc = savings,\n                           payment = 500,\n                           interval = 'monthly',\n                           date_start = datetime(2016,9,30),\n                           day = 30,\n                           name = 'Savings')\n\n    simulation.add_regular(from_acc = account,\n                           to_acc= loan,\n                           payment = 1000,\n                           interval = 'monthly',\n                           date_start = datetime(2016,9,15),\n                           day = 15,\n                           name = 'Debts',\n                           fixed = False,\n                           date_stop = lambda cdate: loan.is_finished())\n\n    simulation.add_regular(from_acc = account,\n                           to_acc= loan,\n                           payment = lambda : min(8000, max(0,account.get_account()-4000)),\n                           interval = 'yearly',\n                           date_start = datetime(2016,11,20),\n                           day = 20,\n                           name = 'Debts',\n                           fixed = False,\n                           date_stop = lambda cdate: loan.is_finished())\n\n    simulation.simulate(delta=timedelta(days=2000))\n\n    simulation.plt_summary()\n    show()\n\n    print(account.report)\n    print(loan.report)\n\nif __name__ == '__main__':\n    example1()\n\n"
  },
  {
    "path": "financial_life/financing/__init__.py",
    "content": "\"\"\" some basic classes for creating financing classes and\nreports \"\"\"\n\n# standard libraries\nfrom datetime import datetime\nfrom calendar import monthrange\nimport warnings\nfrom copy import deepcopy\nfrom collections import defaultdict\nfrom collections import Callable\n\n# third-party libraries\nfrom tabulate import tabulate\nimport numpy as np\nfrom numpy.core.numeric import result_type\nimport pandas as pd\n\n# own libraries\nfrom financial_life.calendar_help import Bank_Date\nfrom financial_life.financing.identity import id_generator\nfrom financial_life.financing import validate\n\npd.set_option('display.width', 1000)\n\n# degrees of precision. the higher the number the more\n# precise is the category\nC_precisions = {'year' : 1.,\n              'month': 2.,\n              'day': 3.,\n              }\n\nC_default_payment = {'date': Bank_Date.max,\n                     'payment': 0.,\n                     'name': 'End reached'}\n\n# semantic of reports. Columns of the report can be assigned\n# to one or several of this categories. This allows to\n# visualize collection of reports in a semantical meaningful manner\nreport_semantics = {'input_abs': [],    # money transfered to the financial product as absolute number\n                    'input_cum': [],    # ...as increment\n                    'output_abs': [],   # money transfered from the financial product\n                    'output_cum': [],   # ...as decrement\n                    'cost_abs': [],     # created costs by the f.p.\n                    'cost_cum': [],     # ...as increment\n                    'win_abs': [],      # created win by the f.p.\n                    'win_cum': [],      # ...as increment\n                    'debt_abs': [],     # debt value\n                    'debt_cum': [],     # ...as increment\n                    'debtpayment_abs': [], # payment to equalize debts\n                    'debtpayment_cum': [], # ...as increment\n                    'saving_abs': [],   # saving value\n                    'saving_cum': [],   # ...as increment\n                    'none': [],         # none of the above things\n                    }\n\n\ndef conv_payment_func(x):\n    \"\"\" converts any payment to what is needed in order to be applicable in\n    the simulation process. if it is a number, it is multiplied by 100, if it\n    is a function, the function is called and the result is multiplied by 100.\n    \"\"\"\n    return Payment_Value(x)\n\n\ndef create_stop_criteria(date_stop):\n    \"\"\" This is a function that returns a functions, which defines\n    a stop criteria for the iterators. If date_stop is a date,\n    the resulting functions simply compares date_stop with a given date.\n    if date_stop is a callable, it executes the callable \"\"\"\n    if isinstance(date_stop, datetime) or isinstance(date_stop, Bank_Date):\n        def compare_dates(cdate):\n            return cdate < date_stop\n        return compare_dates\n    elif isinstance(date_stop, Callable):\n        def check_callable(cdate):\n            return not date_stop(cdate)\n        return check_callable\n    else:\n        raise ValueError(\"date_stop is %s but should be either date-type or Callable\" % type(date_stop))\n\n\ndef iter_regular_month(regular, date_start = None):\n    \"\"\" creates an iterator for a regular payment. this function is for example\n    used by payment to create iterators for every item in _regular\n        regular: item of the structure Payments._regular\n        date_start: date the payment generator wants to start the payments,\n                    this can be a date after regular['date_start']\n    \"\"\"\n    if not date_start:\n        date_start = regular['date_start']\n    else:\n        # determine the greater date\n        date_start = max(date_start, regular['date_start'])\n\n    # if day is bigger than start_date.day, than this month is gonna\n    # be the first payment\n    if date_start.day <= regular['day']:\n        i = 0\n    # otherwise it will be in the next month\n    else:\n        i = 1\n\n    date_start = Bank_Date(year = date_start.year,\n                           month = date_start.month,\n                           day = min(regular['day'], monthrange(date_start.year, date_start.month)[1]),\n                           hour = date_start.hour,\n                           minute = date_start.minute,\n                           second = date_start.second)\n\n    date_stop = regular.get('date_stop', Bank_Date.max)\n    stop_criteria = create_stop_criteria(date_stop)\n\n    current_date = date_start.add_month(i)\n\n    while stop_criteria(current_date):\n        yield Payment(from_acc = regular['from_acc'],\n                      to_acc = regular['to_acc'],\n                      date = current_date,\n                      name = regular['name'],\n                      kind = 'regular',\n                      payment = regular['payment'],\n                      fixed = regular['fixed'],\n                      meta = regular['meta']\n                      )\n        i += 1\n        current_date = date_start.add_month(i)\n\ndef iter_regular_year(regular, date_start = None):\n    \"\"\" creates an iterator for a yearly payment. this function is\n    used by payment to create iterators for every item in _regular\n    It takes the day and month in regular['date_start'] to schedule the payment\n        regular: item of the structure Payments._regular\n        date_start: date the payment generator wants to start the payments,\n                    this can be a date after regular['date_start']\n    \"\"\"\n\n    if not date_start:\n        date_start = regular['date_start']\n    else:\n        # determine the greater date\n        date_start = max(date_start, regular['date_start'])\n\n    current_date = datetime(year=date_start.year,\n                            month=regular['date_start'].month,\n                            day=regular['date_start'].day)\n\n    if current_date < date_start:\n        current_date = datetime(year=date_start.year + 1,\n                                month=regular['date_start'].month,\n                                day=regular['date_start'].day)\n\n    date_stop = regular.get('date_stop', Bank_Date.max)\n    stop_criteria = create_stop_criteria(date_stop)\n\n    while stop_criteria(current_date):\n        yield Payment(from_acc = regular['from_acc'],\n                      to_acc = regular['to_acc'],\n                      date = current_date,\n                      name = regular['name'],\n                      kind = 'regular',\n                      payment = regular['payment'],\n                      fixed = regular['fixed'],\n                      meta = regular['meta']\n                      )\n\n        current_date = datetime(year = current_date.year + 1,\n                                month=regular['date_start'].month,\n                                day=regular['date_start'].day)\n\n\n# functions for generating regular payments\nC_interval = {\n              'monthly': iter_regular_month,\n              'yearly': iter_regular_year,\n              }\n\n\n\nclass Status(object):\n    \"\"\" This class represents the status of a financing product\n    at a particular date \"\"\"\n\n    def __init__(self, date, **kwargs):\n        \"\"\" Creates a new status object. Note, that all of kwargs\n        elements are written to _status, except 'meta', which is\n        treated special, as it may contain dict-data again \"\"\"\n        if not isinstance(date, datetime):\n            raise TypeError(\"date must be from type datetime\")\n\n        self._date = date\n        self._meta = {}\n\n        if 'meta' in kwargs:\n            self._meta = kwargs.pop('meta')\n\n        self._status = kwargs\n        self._format = \"%d.%m.%Y\"\n\n    def __str__(self):\n        result = \"Date: %s\" % self._date.strftime(self._format) + '\\n'\n        for key, value in self._status.items():\n            result += (\"%s: %s\\n\" % (key, str(value)))\n        return result\n\n    def keys(self):\n        \"\"\" Returns a list of keys \"\"\"\n        return self._status.keys()\n\n    @property\n    def date(self):\n        return self._date\n\n    @property\n    def strdate(self):\n        return self._date.strftime(self._format)\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def meta(self):\n        return self._meta\n\n    def __getitem__(self, key):\n        if key == 'date':\n            return self._date\n        return self._status[key]\n\n    def __getattr__(self, name):\n        return self.__getitem__(name)\n    \n    def get(self, attr, default):\n        \"\"\" Get attribute or default value from data-dictionary \"\"\"\n        if attr == 'date':\n            return self._date\n        return self._status.get(attr, default)\n\nclass Report(object):\n    \"\"\" A report is a collection of statuses with some additional\n    functionallity in order to merge and plot reports. One key\n    feature of report is that it can handle heterogenous types of\n    statuses\n    \"\"\"\n\n    def __init__(self, name=None,\n                 format_date = \"%d.%m.%Y\",\n                 precision = 'daily'\n                 ):\n        self._statuses = []\n        self._keys = []    # list of all keys used so far\n\n        self._format_date = format_date\n        # precision for merging statuses with similar date\n        self._precision = precision\n        self._semantics = deepcopy(report_semantics)\n\n        if not name:\n            name = id_generator(8)\n        self._name = name\n\n    def add_semantics(self, key, semantics=None):\n        \"\"\" adds semantic description to the report, important for\n        plotting\n\n        usage:\n            Single assignments\n                .add_semantics('loan', 'debt_abs')\n\n            Group assignments\n                .add_semantics(['interest', 'insurence'], 'cost_cum')\n\n            Entire assignments\n                .add_semantics({'cost_cum':['interest', 'insurence']})\n        \"\"\"\n        if isinstance(key, dict):\n            for k, items in key.items():\n                if k in self._semantics:\n                    self._semantics[k] = items\n                else:\n                    raise AttributeError('Key %s not in semantics' % k)\n            return\n\n        if semantics not in self._semantics:\n            raise AttributeError('Semantic \"%s\" not in semantics' % semantics)\n\n        if isinstance(key, list):\n            self._semantics[semantics] = self._semantics[semantics] + key\n            self._keys = list(set(self._keys) | set(key))\n            return\n\n        if isinstance(key, str):\n            self._semantics[semantics].append(key)\n            self._keys = list(set(self._keys) | set((key,)))\n            return\n\n    def semantics(self, semantic):\n        \"\"\" returns list of elements in semantic \"\"\"\n        return self._semantics[semantic]\n\n    def semantics_of(self, key):\n        \"\"\" returns the semantic in which the key appears \"\"\"\n        for semantic, values in self._semantics.items():\n            if key in values:\n                return semantic\n        return ''\n\n    def append(self, status = None, date = None, **kwargs):\n        \"\"\" adds either an instance of status to the list or\n        data given to the append method as keyword arguments \"\"\"\n        assert((status and not date) or (date and not status))\n\n        if date:\n            status = Status( Bank_Date.fromtimestamp(date.timestamp()), **kwargs )\n\n        if not isinstance(status, Status):\n            raise TypeError(\"status must be of type Status\")\n\n        self._statuses.append(status)\n        # add potential new keys to the list\n        self._keys = list(set(self._keys) | set(status.keys()))\n\n    @property\n    def size(self):\n        \"\"\" Returns the number of status entries\"\"\"\n        return len(self._statuses)\n\n    @property\n    def name(self):\n        return self._name\n\n    @name.setter\n    def name(self, name):\n        self._name = name\n\n    @property\n    def precision(self):\n        return self._precision\n\n    def get_from_date(self, date, interval):\n        \"\"\" help function to make the creation monthly, yearly reports more\n        generic. This function returns e.g. month or year from a given date \"\"\"\n        if interval == 'yearly':\n            return Bank_Date(date.year, 1, 1)\n        if interval == 'monthly':\n            return Bank_Date(date.year, date.month, 1)\n        if interval == 'daily':\n            return date\n        raise TypeError(\"interval has to be either monthly or yearly\")\n\n    def monthly(self):\n        return self.create_report(interval='monthly')\n\n    def yearly(self):\n        return self.create_report(interval='yearly')\n\n    def create_report(self, interval='yearly'):\n        \"\"\" generic function for returning a report for certain\n        intervals \"\"\"\n\n        def add_data(data, status):\n            \"\"\" add status data to existing dictionary \"\"\"\n            for key, value in status.status.items():\n                # for cumulative data, we need to add, for other we just\n                # need to take the latest value\n                if \"cum\" in self.semantics_of(key):\n                    data[key] += value\n                elif not self.semantics_of(key) is \"none\":\n                    data[key] = value\n            return data\n\n        if interval == 'daily':\n            return self\n\n        i = 0\n        result = Report(name = self._name,\n                        format_date = self._format_date,\n                        precision = interval\n                        )\n        result._semantics = deepcopy(self._semantics)\n\n        while (i < len(self._statuses)):\n            # get the value of the interval type, e.g. exact month or exact year\n            frame = self.get_from_date(self._statuses[i].date, interval)\n            # create a new dictionary and add the current status to it\n            data = add_data(defaultdict(int), self._statuses[i])\n            i += 1\n            # iterate to the following statuses as long as frame equals the current value of the\n            # the given interval type (e.g. as long as the month is the same)\n            while (i < len(self._statuses)) and (frame == self.get_from_date(self._statuses[i].date, interval)):\n                data = add_data(data, self._statuses[i])\n                i += 1\n\n            # if the while loop ended because of i, we need to correct it again\n            if (i == len(self._statuses)):\n                result.append(date=self._statuses[i-1].date, **data)\n            else:\n                result.append(date=self._statuses[i-1].date, **data)\n\n        return result\n    \n    def subset(self, lambda_func):\n        \"\"\" creates a subset of report based on lambda-function that is \n        used within a list comprehension. The lambda function gets every\n        status element of report and returns either true (to be inlcuded\n        in subset) or false (excluded). This is a very generic way of\n        applying queries to the report in order to reduce its the report\n        to its requsted items.\n        Note, that this is not a deepcopy of the report. Therefore, the \n        it is more appropriate to use it for reading data rather than\n        writing data.\n        \"\"\"\n        if not isinstance(lambda_func, Callable):\n            raise TypeError('lambda_func must be of the form lambda status: True <or> False')\n        result = Report(name = self._name,\n                        format_date = self._format_date,\n                        precision = 'custom'\n                        )\n        result._semantics = self._semantics\n        result._keys = self._keys\n        result._statuses = [s for s in self._statuses if lambda_func(s)]\n        return result\n\n    def table_rows(self):\n        \"\"\" Creates a list of lists, where each inner list\n        represents a row of a table. This is used by the tabulate\n        package for plotting tables. \"\"\"\n        records = []\n        for s in self._statuses:\n            data = [s.date.strftime(self._format_date)] + [s.get(key, '') for key in self._keys]\n            records.append(data)\n        return records\n\n    def with_meta(self):\n        \"\"\" Returns the table with meta-information \"\"\"\n        print(self.name)\n        records = []\n        for s in self._statuses:\n            data = [s.date.strftime(self._format_date)] + [s.get(key, '') for key in self._keys] + [str(s._meta)]\n            records.append(data)\n        return tabulate(records, headers=(['Date'] + self._keys + ['Meta']), floatfmt=\".2f\")\n\n    def sum_of(self, semantic):\n        \"\"\"\n        Returns the sum of a given semantic, e.g.\n            .sum_of('cost')\n        for all cost_cum and cost_abs items, or\n            .sum_of('cost_cum')\n        for cost_cum items only\n        \"\"\"\n        result = 0\n        for sem in self._semantics:\n            if semantic in sem:\n                # if this is a cumulative list, we need to calculate the sum\n                if '_cum' in sem:\n                    for key in self._semantics[sem]:\n                        result += np.sum(self.get(key, num_only = True))\n                # if abs, get only the last element\n                if '_abs' in sem:\n                    for key in self._semantics[sem]:\n                        result += self.get(key, num_only = True)[-1]\n        return result\n\n    def __getitem__(self, key):\n        result = Report(format_date = self._format_date,\n                        precision = self._precision)\n        for s in self._statuses:\n            if key in s.keys():\n                result.append(date = s['date'], **{key: s[key]})\n        return result\n\n    def __getattr__(self, name):\n        result = [s.get(name, 'None') for s in self._statuses]\n        return result\n        if (name == 'date'):\n            return result\n        else:\n            return np.array(result)\n        \n    def __len__(self):\n        return len(self._statuses)        \n\n    def get(self, name, num_only = False):\n        replace = 0 if num_only else 'None'\n        result = [s.get(name, replace) for s in self._statuses]\n        return result\n        if (name == 'date'):\n            return result\n        else:\n            return np.array(result)\n\n    def __str__(self):\n        \"\"\" Prints all statuses in table view \"\"\"\n        print(self.name)\n        records = self.table_rows()\n        return tabulate(records, headers=(['Date'] + self._keys), floatfmt=\".2f\")\n    \n    def __iter__(self):\n        \"\"\" Iteratores through all statuses \"\"\"\n        return self._statuses.__iter__()\n\n    def as_df(self):\n        \"\"\" Returns the report as pandas.DataFrame \"\"\"\n        dates, data = list(zip(*((s.date, s.status) for s in self._statuses)))\n        return pd.DataFrame(list(data), index=dates)\n\nclass Payment_Value(object):\n    \"\"\" This is a class that represents a payment value. If the payment\n    is an integer or float it is returned right away, if it is a\n    function it is evaluated during runtime. Furthermore, this class\n    can return a clear statement, whether it is a payment or not\n    to any reporting instance (e.g. html reports)\n    \"\"\"\n    def __init__(self, payment):\n        \"\"\" checks, whether x is a number or a function and\n        prepares the reporting variable \"\"\"\n        self._name = \"could not be determined\"\n        self._payment = payment\n        if isinstance(payment, int) or isinstance(payment, float):\n            self._name = str('%0.2f' % payment)\n        if isinstance(payment, Callable):\n            self._name = \"dynamic\"\n\n    @property\n    def name(self):\n        return self._name\n\n    def __call__(self):\n        if isinstance(self._payment, int) or isinstance(self._payment, float):\n            return int(self._payment * 100)\n        if isinstance(self._payment, Callable):\n            return int(self._payment() * 100)\n\nclass Payment(object):\n    \"\"\" Class that describes one specific payment between two accounts\n    accounts can here be of type \"Account\" or str, which is an\n    abstract account that always complies \"\"\"\n\n    def __init__(self, from_acc, to_acc, date, name,\n                 kind, payment, fixed=True, meta={}):\n        \"\"\" Initialization of Payment\n        from_acc:      sending account\n        to_acc:        receiving account\n        date:          due date\n        name:          Name of the payment\n        payment:       Amount of the payment (money value)\n        fixed:         Whether the receiving side needs to take the entire money\n                       or can accept less (useful for loans)\n        \"\"\"\n        self._data = {\n                     'from_acc': from_acc,\n                     'to_acc': to_acc,\n                     'date': date,\n                     'name': name,\n                     'kind': kind,\n                     'payment': payment,\n                     'fixed': fixed,\n                     'meta': meta\n                     }\n\n    @property\n    def from_acc(self):\n        return self._data['from_acc']\n\n    @property\n    def to_acc(self):\n        return self._data['to_acc']\n\n    @property\n    def date(self):\n        return self._data['date']\n\n    @property\n    def name(self):\n        return self._data['name']\n\n    @property\n    def kind(self):\n        return self._data['kind']\n\n    @property\n    def payment(self):\n        return self._data['payment']\n\n    @property\n    def json(self):\n        return {'from_acc': self._data['from_acc'].name,\n                'to_acc': self._data['to_acc'].name,\n                'date': self._data['date'].date(),\n                'name': self._data['name'],\n                'kind': self._data['kind'],\n                'payment': self._data['payment'].name,\n                'fixed': self._data['fixed'],\n                'meta': self._data['meta'],\n                }\n\n    def __getitem__(self, key):\n        return self._data[key]\n\n\nclass PaymentList(object):\n    \"\"\" Hanldes the complexities of payments including unique\n    payments and regular payments \"\"\"\n\n    def __init__(self):\n        self._uniques = []\n        self._regular = []\n\n    @property\n    def uniques(self):\n        return self._uniques\n\n    @property\n    def regular(self):\n        return self._regular\n\n    def check_errors_payment(self, payment):\n        \"\"\" checks for any errors in the payment variable \"\"\"\n        if (not isinstance(payment, int) and\n            not isinstance(payment, float) and\n            not isinstance(payment, Callable)):\n            raise TypeError(\"Payment must be int, float or a function\")\n\n    def add_unique(self, from_acc, to_acc, payment,\n                   date, name = '', fixed = True, meta={}):\n        \"\"\" adds a one-time payment to the list, optional give it\n        a name \"\"\"\n        if not isinstance(date, datetime):\n            raise TypeError(\"Date must be at least from type datetime\")\n\n        self.check_errors_payment(payment)\n\n        # converts any input to a function that returns the right value\n        conv_payment = conv_payment_func(payment)\n\n        self._uniques.append(\n                             Payment(\n                                     from_acc = from_acc,\n                                     to_acc = to_acc,\n                                     date = Bank_Date.fromtimestamp(date.timestamp()),\n                                     name = name,\n                                     kind = 'unique',\n                                     payment = conv_payment,\n                                     fixed = fixed,\n                                     meta = meta\n                                     )\n                             )\n\n        # sort the whole list with date as key\n        self._uniques = sorted(self._uniques, key = lambda p: p['date'] )\n\n    def add_regular(self, from_acc, to_acc, payment, interval,\n                    date_start, day=1, name='', date_stop = None,\n                    fixed = False, meta={}):\n        \"\"\" Adds a regular payment to the list, with a given\n        payment: amount to pay\n        interval: 'monthly': every month\n                  'quarter': every quarter with date_start as start month\n                  'quarter_year': every quarter of a year (mar, jun, sep, dec)\n                  'yearly': every year\n        day: day to start with\n        date_start: start date of this payment\n        name : optional name\n        fixed: only everything or nothing must be transfered (true)\n               or depending on the receiving account a smaller amount\n               can be transfered (false)\n        \"\"\"\n        if not interval in C_interval.keys():\n            raise ValueError(\"interval must be one of '\" + '\\',\\''.join(C_interval))\n        if day >= 29:\n            warnings.warn((\"note that in months which have less days than {} the \" +\n                           \"payment will be transferred earlier\").format(day)\n                          )\n        self.check_errors_payment(payment)\n\n        if not date_stop:\n            date_stop = Bank_Date.max\n        else:\n            date_stop = validate.valid_stop_date(date_stop)\n\n        # converts any payment to a function\n        conv_payment = conv_payment_func(payment)\n\n        self._regular.append({'from_acc': from_acc,\n                              'to_acc': to_acc,\n                              'interval': interval,\n                              'day' : day,\n                              'date_start': Bank_Date.fromtimestamp(date_start.timestamp()),\n                              'date_stop': date_stop,\n                              'payment': conv_payment,\n                              'name' : name,\n                              'fixed': fixed,\n                              'meta': meta\n                              }\n                             )\n\n    def clear_regular(self):\n        \"\"\" Removes all regular payments \"\"\"\n        self._regular = []\n\n    def payment(self, start_date):\n        \"\"\" returns an interator that iterates through all\n        payments \"\"\"\n\n        assert isinstance(start_date, datetime), \"start_date must be of type datetime\"\n        # creates for each item an iterator that returns just this\n        # item. this list is later on amended by iterators for regular\n        # payments\n        iters = [iter([u]) for u in self._uniques if u['date']>= start_date]\n        for r in self._regular:\n            # creates an iterator based on the interval in r\n            iters.append(C_interval[r['interval']](r, start_date))\n\n        # list of next dates. this list is inline with the iters list\n        # the second parameter in next prevents the command to raise a\n        # StopIteration Exception\n        dates = [next(iter, C_default_payment) for iter in iters]\n\n        # as long as there is still a date, below infinity\n        min_date = min(dates, key = lambda d: d['date'])\n\n        # in this routine, the next command must be called after yield, as there might\n        # be some callables which need to be called right after the payments, but not\n        # before\n        while min_date['date'] != Bank_Date.max:\n            # find all indices and payments in the list that have the same daet\n            indices, payments = zip(*[(i, d) for (i, d) in enumerate(dates) if (d['date'].date() == min_date['date'].date())])\n            yield payments\n            for i in indices:\n                dates[i] = next(iters[i], C_default_payment)\n            min_date = min(dates, key = lambda d: d['date'])\n\nclass Currency():\n    \"\"\" Standard class for currencies to assure correct computing\n    of numbers. Right now this class is not in use \"\"\"\n    def __init__(self, value, digits = 2):\n        self._value = int(value * (10**digits))\n"
  },
  {
    "path": "financial_life/financing/accounts.py",
    "content": "'''\nCreated on 24.03.2016\n\n@author: martin\n'''\n\n# standard libraries\nfrom datetime import datetime, timedelta\nfrom collections import Callable\nimport warnings\nimport logging\n\n# third-party libraries\n\n# own libraries\nfrom financial_life.financing import PaymentList\nfrom financial_life.financing import Report\nfrom financial_life.financing import C_default_payment\nfrom financial_life.calendar_help import Bank_Date, get_days_per_year\nfrom financial_life.financing import plotting as plt\nfrom financial_life.financing import validate\n\nlogger = logging.getLogger(__name__)\n\n\n# maximal time span that will be simulated\nC_max_time = 365 * 100\n# format for dates\nC_format_date = '%d.%m.%Y'\n\n# generic transfer codes\nC_transfer_OK = 0           # transfer confirmed\nC_transfer_NA = 1           # transfer not allowed\nC_transfer_NEM = 2          # not enough money on the account\nC_transfer_ERR = 3          # general transfer error\nC_transfer_codes = {C_transfer_OK: 'OK',\n                    C_transfer_NA: 'Not allowed',\n                    C_transfer_NEM: 'Not enough money',\n                    C_transfer_ERR: 'ERROR'}\n\ndef neg_func(func):\n    \"\"\" negates the outcome of func. this function is used as a wrapper\n    to negate the output of payments which are determined at runtime. This\n    wrapper is used e.g. by the class Transfers\n    \"\"\"\n    def foo():\n        result = func()\n        return -result\n    return foo\n\ndef valid_account_type(*accounts):\n    \"\"\" Checks whether all accounts given to this function\n    are either from type Account or from type string\n    Accounts of type string are converted to DummyAccount\n    The corrected list is returned\n\n    The only reason this method is not in the validate module\n    is because it would create an import loop\n    \"\"\"\n    result = []\n    for account in accounts:\n        if (isinstance(account, Account)):\n            result.append(account)\n        elif (isinstance(account, str)):\n            result.append(DummyAccount(account))\n        else:\n            raise TypeError('the given account must be either derived from type Account or of type string')\n    return tuple(result)\n\n\n\n\nclass TransferMessage(object):\n    \"\"\" Message returned by a transfer function with some information about\n    the success or failure of the money transfer \"\"\"\n    def __init__(self, code, money, message = ''):\n        if code in C_transfer_codes:\n            self._code = code\n        else:\n            raise ValueError(\"Transfercode is not in C_transfer_codes\")\n\n        self._message = message\n        self._money = money\n\n    @property\n    def code(self):\n        return self._code\n\n    @property\n    def message(self):\n        return self._message\n\n    @property\n    def money(self):\n        return self._money\n\n\nclass Simulation(object):\n    \"\"\" This class simulates the interaction between different accounts. It\n    provides the framework in which dependencies between accounts and state-\n    dependent changes of account-modi can be managed \"\"\"\n\n    def __init__(self, *accounts, name = None, date = None, meta = None):\n        \"\"\" Simulations can be initialized with names, to make differentiate\n        between different simulations \"\"\"\n        # check for errors in the input of accounts\n        for account in accounts:\n            if not isinstance(account, Account):\n                raise TypeError(str(account) + \" is not of type or subtype Account\")\n\n        if name is None:\n            self._name = 'Simulation ' + str(datetime.now())\n        else:\n            self._name = name\n            \n        # a simuation can also store meta information \n        self._meta = meta\n\n        self._report = Report(self._name)\n        self._report.add_semantics('from_acc', 'none')\n        self._report.add_semantics('to_acc', 'none')\n        self._report.add_semantics('value', 'input_cum')\n        self._report.add_semantics('kind', 'none')\n        self._report.add_semantics('name', 'none')\n        self._report.add_semantics('code', 'none')\n        self._report.add_semantics('message', 'none')\n\n        self._payments = PaymentList()\n        self._payments_iter = None\n        self._next_pay = None\n\n        self._date_start = validate.valid_date(date)\n        self._day = 0\n        self._current_date = self._date_start\n\n        # list of accounts to manage\n        self._accounts = list(accounts)\n\n        # list of controller-functions executed before day-simulation.\n        # controller functions are executed before the day to check custom\n        # states of the accounts and perform actions\n        self._controller = []\n\n\n    @property\n    def name(self):\n        return self._name\n\n    @name.setter\n    def name(self, name):\n        self._name = name\n        \n    @property\n    def meta(self):\n        return self._meta\n\n    @property\n    def accounts(self):\n        return self._accounts\n    \n    @property\n    def current_date(self):\n        return self._current_date\n\n    @property\n    def report(self):\n        return self._report\n\n    def as_df(self):\n        df = self.report.as_df()\n        df = df[['from_acc', 'to_acc', 'value', 'kind', 'name', ]]\n        return df\n\n    def get_report_jinja(self, interval=\"yearly\"):\n        \"\"\" creates a data-structure of the report data that can be used for\n        displaying the report as table in html files (in jinja2 templates)\n        interval can be one of the common intervals of the report class (e.g.\n        yearly or monthly) or None. In this case the raw data are exported \"\"\"\n        if interval is None:\n            report = self._report\n        else:\n            report = self._report.create_report(interval)\n\n        header = ['date', 'from', 'to', 'value', 'kind', 'name', 'code', 'message']\n        rows = []\n        for status in report._statuses:\n            item = [status.strdate,\n                    status._data['from_acc'].name,\n                    status._data['to_acc'].name,\n                    '%.02f EUR' % status._data['value'],\n                    status._data['kind'],\n                    status._data['name'],\n                    status._data['code'],\n                    status._data['message'],\n                    ]\n            rows.append(item)\n        return {'header': header, 'rows': rows}\n\n    def get_payments_unique_json(self):\n        \"\"\" returns a list of all unique payments in json format for\n        html rendering \"\"\"\n        return {'payments_unique': [u.json for u in self._payments.uniques]}\n\n    def get_payments_regular_json(self):\n        \"\"\" returns a list of all unique payments in json format for\n        html rendering \"\"\"\n        return {'payments_regular': [\n                                     {\n                                      'from_acc': r['from_acc'].name,\n                                      'to_acc': r['to_acc'].name,\n                                      'interval': r['interval'],\n                                      'day': r['day'],\n                                      'date_start': r['date_start'].date(),\n                                      'date_stop': r['date_stop'].date() if isinstance(r['date_stop'], datetime) else '',\n                                      'payment': r['payment'].name,\n                                      'name': r['name'],\n                                      'fixed': r['fixed'],\n                                      } for r in self._payments.regular]\n                }\n\n    def get_accounts_json(self):\n        return {'accounts': [\n                             {\n                              'index': i,\n                              'name': a.name,\n                              'type': a.__class__.__name__,\n                              'start_value': a._account / 100.,\n                              'start_date': a.date_start.date()\n                              }\n                             for i, a in enumerate(self.accounts)]\n                }\n\n    def add_unique(self, from_acc, to_acc, payment,\n                   date, name = '',\n                   fixed = False,\n                   meta = {}\n                   ):\n        \"\"\" Transfers money from one account to the other \"\"\"\n        from_acc, to_acc = valid_account_type(from_acc, to_acc)\n        date = validate.valid_date(date)\n        self._payments.add_unique(\n            from_acc, to_acc, payment, date, name, fixed, meta)\n        self.update_payment_iterators()\n\n    def add_regular(self, from_acc, to_acc, payment, interval,\n                    date_start=datetime(1971,1,1),\n                    day=1, name = '',\n                    date_stop = None,\n                    fixed = False,\n                    meta = {}\n                    ):\n        \"\"\" Transfers money from one account to the other on regular basis\n        date_stop can be a function of the form lambda x: x > datetime(...)\n        If it returns true, the payment is stopped\n        \"\"\"\n        from_acc, to_acc = valid_account_type(from_acc, to_acc)\n        date_start = validate.valid_date(date_start)\n        if date_stop is not None:\n            date_stop = validate.valid_stop_date(date_stop)\n        self._payments.add_regular(\n            from_acc, to_acc, payment, interval,\n            date_start, day, name, date_stop, fixed, meta)\n        self.update_payment_iterators()\n        \n    def update_payment_iterators(self):\n        \"\"\" Whenever a new payment is added via add_unique or add_regular,\n        this function is triggered to update the payment iterator. This is \n        necessary, as payments could be dynamically added during the \n        simulation as well \"\"\"\n        self._payments_iter = self._payments.payment(self._current_date)\n\n        try:\n            self._next_pay = next(self._payments_iter, C_default_payment)\n        except StopIteration:\n            # if there are no payments, create a date for a payment\n            # that lies in the distant future\n            self._next_pay = [{'date': Bank_Date.max}]\n\n    def add_account(self, account):\n        \"\"\" adds an account to the simulation and returns it to the\n        user so that he/she can proceed with it \"\"\"\n        if isinstance(account, Account):\n            self._accounts.append(account)\n        else:\n            raise TypeError((\"account must be of type Account but is of type \" +\n                            str(type(account))))\n        return account\n\n    def add_controller(self, controller):\n        if isinstance(controller, Callable):\n            self._controller.append(controller)\n        else:\n            raise TypeError((\"controller must be of type Callable but is of type \" +\n                            str(type(controller))))\n\n    def get_payment(self, payment):\n        \"\"\" functions that returns the amount of payment for the current day.\n        it handles the distinction between variables that represent just numbers\n        and variables that represent functions to be executed \"\"\"\n        payed = 0\n        if isinstance(payment['payment'], int) or isinstance(payment['payment'], float):\n            payed = payment['payment']\n        elif isinstance(payment['payment'], Callable):\n            payed = payment['payment']()\n        else:\n            raise TypeError(\"payment must be int, float or Callable but is \" + str(type(payment['payment'])))\n        return payed\n\n    def make_report(self, from_acc, to_acc, value, kind,\n                    name, code, message, meta):\n        self._report.append(\n                            date = self._current_date,\n                            from_acc = from_acc,\n                            to_acc = to_acc,\n                            value = value / 100,\n                            kind = kind,\n                            name = name,\n                            code = code,\n                            message = message,\n                            meta = meta\n                            )\n\n    def make_transfer(self, payment):\n        \"\"\" Transfers money from one account to the other and tries to assure\n        full consistency.\n\n        The idea is that a payments gets started by the sender. If this succeeds,\n        the money is tried to move on the account of the receiver. If this fails,\n        the money is transfered back to the sender.\n\n        If the money to be transfered is zero, no payment procedure will be\n        initiated\n        \"\"\"\n        if not (isinstance(payment['from_acc'], DummyAccount)):\n            assert payment['from_acc']._date_start <= self._current_date, (str(payment['from_acc']) + ' has a later creation date than the payment ' + payment['name'])\n        if not (isinstance(payment['to_acc'], DummyAccount)):\n            assert payment['to_acc']._date_start <= self._current_date, (str(payment['to_acc']) + ' has a later creation date than the payment ' + payment['name'])\n        try:\n            # this is now the money that will be transfered, if there is\n            # a receiver. this amount of money remains fixed for the transfer\n            money = self.get_payment(payment)\n            if money == 0:\n                self.make_report(\n                                from_acc = payment['from_acc'],\n                                to_acc = payment['to_acc'],\n                                value = 0,\n                                kind = payment['kind'],\n                                name = payment['name'],\n                                code = C_transfer_NA,\n                                message = \"Transfer with zero money will not be initiated\",\n                                meta = payment['meta']\n                                )\n                return False\n        except TypeError as e:\n            logger.debug(\"make_transfer: money of wrong type\")\n            self.make_report(\n                                from_acc = payment['from_acc'],\n                                to_acc = payment['to_acc'],\n                                value = 0,\n                                kind = payment['kind'],\n                                name = payment['name'],\n                                code = C_transfer_ERR,\n                                message = e.message(),\n                                meta = payment['meta']\n                                )\n            return False\n\n        # first, try to get the money from the sender account, tm = TransferMessage()\n        tm_sender = payment['from_acc'].payment_output(\n                                                       account_str = payment['to_acc'].name,\n                                                       payment = -money,\n                                                       kind = payment['kind'],\n                                                       description = payment['name'],\n                                                       meta = payment['meta']\n                                                       )\n\n        # if sending money succeeded, try the receiver side\n        if tm_sender.code == C_transfer_OK:\n            logger.debug(\"make_transfer: sender code is OK\")\n            # in the wired case that money is less than what has been returned by the sender,\n            # throw an error message\n            if money < (-tm_sender.money):\n                raise ValueError(\"%f was requested from account '%s' but %f returned\" % (money,\n                                                                                         payment['from_acc'].name,\n                                                                                         -tm_sender.money))\n            if money > (-tm_sender.money):\n                # if payment is fixed, throw an error, otherwise proceed\n                if payment['fixed']:\n                    raise ValueError(\"%f was requested from account '%s' but %f returned\" % (money,\n                                                                                         payment['from_acc'].name,\n                                                                                         -tm_sender.money))\n                else:\n                    money = -tm_sender.money\n\n            tm_receiver = payment['to_acc'].payment_input(\n                                                          account_str = payment['from_acc'].name,\n                                                          payment = money,\n                                                          kind = payment['kind'],\n                                                          description = payment['name'],\n                                                          meta = payment['meta']\n                                                          )\n            # if receiving succeeded, return success\n            if tm_receiver.code == C_transfer_OK:\n                # in the wired case that money is less than what has been returned by the sender,\n                # throw an error message\n                if money < tm_receiver.money:\n                    raise ValueError(\"%f was submitted to account '%s' but %f returned\" % (money,\n                                                                                           payment['to_acc'].name,\n                                                                                           tm_receiver.money))\n                # if the receiver does not accept the entir money\n                if money > tm_receiver.money:\n                    # check, whether payment is fixed\n                    if payment['fixed']:\n                        raise ValueError(\"%f was submitted to account '%s' but %f returned because it is fixed\" % (money,\n                                                                                               payment['to_acc'].name,\n                                                                                               tm_receiver.money))\n                    else:\n                        # if payment is not fixed, we need to transfer the difference back to\n                        # the sender account\n                        payment['from_acc'].return_money( money - tm_receiver.money)\n\n                logger.debug(\"make_transfer: receiver code is OK\")\n                self.make_report(\n                                    from_acc = payment['from_acc'],\n                                    to_acc = payment['to_acc'],\n                                    value = tm_receiver.money,\n                                    kind = payment['kind'],\n                                    name = payment['name'],\n                                    code = C_transfer_OK,\n                                    message = '',\n                                    meta = payment['meta']\n                                    )\n                return True\n            else:\n                # if an error on the receiver side happened,\n                # return the money back and report that\n                logger.debug(\"make_transfer: receiver code is not ok\")\n                payment['from_acc'].return_money(money)\n                self.make_report(\n                                    from_acc = payment['from_acc'],\n                                    to_acc = payment['to_acc'],\n                                    value = tm_sender.money,\n                                    kind = payment['kind'],\n                                    name = payment['name'],\n                                    code = tm_receiver.code,\n                                    message = tm_receiver.message,\n                                    meta = payment['meta']\n                                    )\n                return False\n        else:\n            # if an error occured on the sending side, report this and return false\n            logger.debug(\"make_transfer: sending code is not OK\")\n            self.make_report(\n                                from_acc = payment['from_acc'],\n                                to_acc = payment['to_acc'],\n                                value = money,\n                                kind = payment['kind'],\n                                name = payment['name'],\n                                code = tm_sender.code,\n                                message = tm_sender.message,\n                                meta = payment['meta']\n                                )\n            return False\n\n\n    def simulate(self, date_stop = None, delta = None, last_report = True):\n        \"\"\" Simulation routine for the entire simulation \"\"\"\n        # Initialization\n        date_stop = validate.valid_date_stop(date_stop)\n\n        if (not self._payments_iter):\n            self._payments_iter = self._payments.payment(self._current_date)\n\n        if (not self._next_pay):\n            try:\n                self._next_pay = next(self._payments_iter, C_default_payment)\n            except StopIteration:\n                # if there are no payments, create a date for a payment\n                # that lies in the distant future\n                self._next_pay = [{'date': Bank_Date.max}]\n\n        delta = validate.valid_delta(delta)\n\n        temp_delta = 0\n\n        while ((self._current_date < date_stop) and     # ...stop-date is reached\n            (temp_delta < delta.days) and           # and delta has not been exeeded\n            ((self._current_date - self._date_start).days < C_max_time)):  # ...number of simulated days exceeds max\n\n            # 0. set the current day\n            for account in self._accounts:\n                if account._date_start <= self._current_date:\n                    account.set_date(self._current_date)\n\n            # 1. execute start-of-day function\n            # everything that should happen before the money transfer\n            for account in self._accounts:\n                if account._date_start <= self._current_date:\n                    account.start_of_day()\n\n            # 2. execute all controller functions\n            for controller in self._controller:\n                controller(self)\n\n            # 3. apply all payments for the day in correct temporal order\n            if self._next_pay[0]['date'].date() == self._current_date.date():\n                for payment in self._next_pay:\n                    self.make_transfer(payment)\n                self._next_pay = next(self._payments_iter, C_default_payment)\n\n            # 4. execute end-of-day function\n            # everything that should happen after the money transfer\n            for account in self._accounts:\n                if account._date_start <= self._current_date:\n                    account.end_of_day()\n\n            # go to the next day within the simulation\n            self._day += 1\n            self._current_date = self._date_start + timedelta(days = self._day)\n            temp_delta += 1\n\n    def reports(self, interval='yearly'):\n        \"\"\" Returns a tuple of reports for a given interval \"\"\"\n        return (account.report.create_report(interval) for account in self._accounts)\n\n    def plt_summary(self, interval='yearly'):\n        \"\"\" plots a summary of the simulation \"\"\"\n        reports = self.reports(interval=interval)\n        plt.summary(*reports)\n\n    def report_sum_of(self, semantic):\n        \"\"\" creates the sum for every report.sum_of(semantic) of each account \"\"\"\n        return sum([a.report.sum_of(semantic) for a in self._accounts])\n\n    def print_reports(self, interval):\n        \"\"\" Creates for every account a report for a given interval \"\"\"\n        for a in self._accounts:\n            print(a.name)\n            print(a.report.create_report(interval))\n            print(' ')\n\n\nclass Account(object):\n    \"\"\" Basic class for all types of accounts with reporting and simulation\n    functionality\n\n    obligatory methods for each account to be part of a simulation\n    - set_date\n    - start_of_day\n    - end_of_day\n    - payment_output\n    - payment_input\n    - return_money\n    \"\"\"\n\n    def __init__(self, amount, interest, date=None, name = None, meta = {}):\n\n        self._date_start = validate.valid_date(date)\n        self._name = validate.valid_name(name)\n        self._meta = meta\n        \n        # check for problems\n        assert((isinstance(amount, int) or (isinstance(amount, float))))\n        if interest > 1.:\n            interest = interest / 100.\n\n        ## generic variables, which are basically used in any class ##\n        ## that inherits from account                               ##\n        \n        # setting up the report and the semantics\n        self._report = Report(name = self._name)\n\n        self._account = int(amount * 100)               # amount of money to start with\n        self._interest = interest                       # interest rate\n\n        self._current_date = self._date_start           # current date of the simulation\n        self._caccount = self._account                  # current account, this variable\n                                                        # is used in all subclasses\n        \n        # sum helper variables for interest calculation and keep\n        self._sum_interest = 0\n\n    def __str__(self):\n        return self._name\n\n    @property\n    def date(self):\n        return self._date\n    \n\n    @property\n    def date_start(self):\n        return self._date_start\n\n    @property\n    def name(self):\n        return self._name\n\n    @name.setter\n    def name(self, name):\n        self._name = name\n        self._report.name = self._name + ' - ' + str(self._date_start.strftime(C_format_date))\n        \n    @property\n    def meta(self):\n        return self._meta        \n\n    @property\n    def account(self):\n        return self._caccount / 100\n\n    def get_account(self):\n        \"\"\" alternative method to get the current account value. this method\n        can be used, e.g. in payment-definitions to transfer the amount of\n        money that a specific account has in the moment this payment is done.\n        Instead of using an actual value, this method is called, evaluated and\n        the return value is used \"\"\"\n        return self.account\n\n    @property\n    def interest(self):\n        return self._interest / 100\n\n    @property\n    def payments(self):\n        return self._payments\n\n    @property\n    def current_date(self):\n        return self._current_date\n\n    @property\n    def report(self):\n        return self._report\n\n    def as_df(self):\n        return self.report.as_df()\n\n    def report_time(self, date):\n        \"\"\" returns true, if the requirements for a report are met \"\"\"\n        return True\n\n    def get_table_json(self, report):\n        \"\"\" Creates a table for a given report \"\"\"\n        return {'header': [], 'rows': []}\n\n    def get_all_tables_json(self):\n        \"\"\" Creates tables for all intervals in report \"\"\"\n        # create all intervals\n        daily = self._report\n        monthly = daily.create_report(interval='monthly')\n        yearly = monthly.create_report(interval='yearly')\n        return [{'category': 'Yearly',\n                 'data': self.get_table_json(yearly)},\n                {'category': 'Monthly',\n                 'data': self.get_table_json(monthly)},\n                {'category': 'Daily',\n                 'data': self.get_table_json(daily)} ]\n\n    def get_report_json(self, interval=\"yearly\"):\n        \"\"\" creates a data-structure of the report data that can be used for\n        displaying the report as table in html files (in jinja2 templates).\n        interval can be one of the common intervals of the report class (e.g.\n        yearly, monthly, daily) or None. If None, thee raw data are exported.\n\n        If interval is 'all', all intervals will be returned with a\n        different json structure \"\"\"\n        if interval is 'all':\n            # create all intervals\n            return self.get_all_tables_json()\n        else:\n            if interval is None:\n                report = self._report\n            else:\n                report = self._report.create_report(interval)\n\n            return self.get_table_json(report)\n\n\n    def payment_input(self, account_str, payment, kind, description, meta):\n        \"\"\" Input function for payments. This account is the receiver\n        of a transfer. This function, if derived from,\n        can account for special checks for input operations \"\"\"\n        return TransferMessage(C_transfer_OK, money = payment)\n\n    def payment_output(self, account_str, payment, kind, description, meta):\n        \"\"\" Output function for payments. This account is the sender\n        of a transfer. This function, if derived from,\n        can account for special checks for output operations \"\"\"\n        return TransferMessage(C_transfer_OK, money = payment)\n\n    def return_money(self, money):\n        \"\"\" this is a hard return of transfer-money, in case the receiving side\n        rejected the transfer \"\"\"\n        pass\n\n    def set_date(self, date):\n        \"\"\" This function is called by the simulation class to set the current date\n        for the simulation \"\"\"\n        # if there is an inconsistency in the date progression, report\n        # a warning on the command line\n        delta = (date - self._current_date).days\n        if delta != 1:\n            warnings.warn('Difference between current date and next date is %i and not 1' % delta)\n        if date < self._date_start:\n            warnings.warn('Date is before start date of account.')\n\n        self._current_date = date\n\n    def start_of_day(self):\n        \"\"\" Things that should happen on the start of the day, before any money\n        transfer happens \"\"\"\n        pass\n\n    def end_of_day(self):\n        \"\"\" Things that should happen at the end of the day, after all money\n        transfers have been accomplished \"\"\"\n        pass\n\n\nclass DummyAccount(Account):\n    \"\"\" This account is used when the user creates a Transfer using a\n    String as the from-account or to-account. This account basically agrees\n    to everything. It can be used to create payments for loans or for\n    outgoing costs \"\"\"\n\n    def __init__(self, name):\n        \"\"\" Creates a dummy account class \"\"\"\n        self._name = validate.valid_name(name)\n\n\n\n# now the implementation of the real, usable classes begins. In contrast to the account class,\n# in these classes, report gets some semantic information about how to handle different\n# properties of the class\n\nclass Bank_Account(Account):\n    \"\"\" This is a normal bank account that can be used to manage income and\n    outgoings within a normal household \"\"\"\n\n    def __init__(self, amount, interest, date = None, name = None, meta = {}):\n        \"\"\" Creates a bank account class \"\"\"\n        # call inherited method __init__\n        super().__init__(\n            amount = amount, interest = interest, date = date, name = name, meta = meta)\n\n        self._report_input = 0\n        self._report_output = 0\n\n        self._report.add_semantics('account', 'saving_abs')\n        self._report.add_semantics('interest', 'win_cum')\n        self._report.add_semantics('input', 'input_cum')\n        self._report.add_semantics('output', 'output_cum')\n        self._report.add_semantics('foreign_account', 'none')\n        self._report.add_semantics('kind', 'none')\n        self._report.add_semantics('description', 'none')\n\n        self._interest_paydate = {'month': 12, 'day': 31}\n        # reporting functionality\n        self._report_interest = 0\n\n        self.make_report()\n\n\n    # overwriting function\n    def make_report(self, interest=0, input=0, output=0,\n                    foreign_account = '', kind = '', description = '',\n                    meta = {}):\n        \"\"\" creates a report entry and resets some variables \"\"\"\n        self._report.append(date = self._current_date,\n                            account = self._caccount / 100,\n                            interest = float('%.2f' % (interest / 100)),\n                            input = input / 100,\n                            output = output / 100,\n                            foreign_account = foreign_account,\n                            kind = kind,\n                            description = description,\n                            meta = meta\n                            )\n\n    def exec_interest_time(self):\n        \"\"\" Does all things, when self.interest_time() returns true (like adding\n        interests to the account \"\"\"\n        self._caccount = int(round(self._caccount + self._sum_interest))\n        self.make_report(\n                         interest = self._sum_interest,\n                         kind = 'yearly interest'\n                         )\n        self._sum_interest = 0\n\n    def as_df(self):\n        df = self.report.as_df()\n        df = df[['foreign_account', 'description', 'input', 'output', 'interest', 'account']]\n        return df\n\n    def get_table_json(self, report):\n        \"\"\" Creates a table for a given report \"\"\"\n        rows = []\n        if report.precision is 'daily':\n            header = ['date', 'from', 'description', 'input', 'output', 'interest', 'account']\n\n            for status in report._statuses:\n                item = [status.strdate, status._status['foreign_account'],\n                    status._status['description'],\n                    '%.02f EUR' % status._status['input'],\n                    '%.02f EUR' % status._status['output'],\n                    '%.02f EUR' % status._status['interest'],\n                    '%.02f EUR' % status._status['account']]\n                rows.append(item)\n        else:\n            header = ['date', 'input', 'output', 'interest', 'account']\n\n            for status in report._statuses:\n                item = [status.strdate,\n                    '%.02f EUR' % status._status['input'],\n                    '%.02f EUR' % status._status['output'],\n                    '%.02f EUR' % status._status['interest'],\n                    '%.02f EUR' % status._status['account']]\n                rows.append(item)\n\n        return {'header': header, 'rows': rows}\n\n    def interest_time(self):\n        \"\"\" Checks, whether it is time to book the interests to the account \"\"\"\n        return ((self._current_date.day == self._interest_paydate['day']) and\n                (self._current_date.month == self._interest_paydate['month']))\n\n    def payment_input(self, account_str, payment, kind, description, meta):\n        \"\"\" Input function for payments. This account is the receiver\n        of a transfer. This function, if derived from,\n        can account for special checks for input operations \"\"\"\n        return self.payment_move(account_str, payment, kind, description, meta)\n\n    def payment_output(self, account_str, payment, kind, description, meta):\n        \"\"\" Output function for payments. This account is the sender\n        of a transfer. This function, if derived from,\n        can account for special checks for output operations \"\"\"\n        return self.payment_move(account_str, payment, kind, description, meta)\n\n    def payment_move(self, account_str, payment, kind, description, meta):\n        \"\"\" in the base class, payment_input and payment_output have almost\n        the same behavior. Only the type of reporting differs\n\n        account_str : the opposite account, sender or receiver\n        payment : the int or function which includes the payment\n        kind : whether this is a regular payment or a unique one\n        description: description of the payment (usually its name)\n        move_type: \"input\" or \"output\" for indicating the direction of movement \"\"\"\n\n        move_type = 'input'\n        if payment < 0:\n            move_type = 'output'\n\n        self._caccount = int(self._caccount + payment)\n        report = {'foreign_account': account_str,\n                  move_type: payment,\n                  'kind': kind,\n                  'description': description,\n                  'meta': meta}\n        self.make_report(**report)\n        return TransferMessage(C_transfer_OK, money = payment)\n\n    def return_money(self, money):\n        \"\"\" this is a hard return of transfer-money, in case the receiving side\n        rejected the transfer \"\"\"\n        self._caccount = int(self._caccount + money)\n        report = {\n                  'input': money,\n                  'kind': 'storno',\n                  'description': 'transfer did not succeeded'}\n        self.make_report(**report)\n\n\n    def start_of_day(self):\n        \"\"\" Things that should happen on the start of the day, before any money\n        transfer happens \"\"\"\n        pass\n\n    def end_of_day(self):\n        \"\"\" Things that should happen at the end of the day, after all money\n        transfers have been accomplished \"\"\"\n        # TODO: needs to be replaced by a mechanism that checks not every day\n        days_per_year = get_days_per_year(self._current_date.year)\n\n        # calculate interest for this day\n        interest = self._caccount * (self._interest / days_per_year)\n\n        # store interest for later calculations\n        self._sum_interest += interest\n\n        # if paydate is there, add the summed interest to the account\n        if self.interest_time():\n            self.exec_interest_time()\n\n\nclass Loan(Account):\n    \"\"\"\n    This is the default account class that should capture the essential\n    functionalities of account models\n    \"\"\"\n\n    def __init__(self, amount, interest, date = None, name = None, meta = {}):\n        \"\"\"\n        Creates the data for a basic account model\n        \"\"\"\n        # call inherited method __init__\n        super().__init__(\n            amount = -amount, interest = interest, date = date, name = name, meta = meta)\n\n        # reporting functionality\n        self._report_payment = 0\n\n        self._report.add_semantics('account', 'debt_abs')\n        self._report.add_semantics('interest', 'cost_cum')\n        self._report.add_semantics('payment', 'debtpayment_cum')\n        self._report.add_semantics('foreign_account', 'none')\n        self._report.add_semantics('kind', 'none')\n        self._report.add_semantics('description', 'none')\n\n        self._interest_paydate = {'month': 12, 'day': 31}\n\n        self.make_report()\n\n    def as_df(self):\n        df = self.report.as_df()\n        df = df[['foreign_account', 'description', 'payment', 'interest', 'account']]\n        return df\n\n    def get_table_json(self, report):\n        rows = []\n        if report.precision is 'daily':\n            header = ['date', 'from', 'description', 'payment', 'interest', 'account']\n            for status in report._statuses:\n                item = [status.strdate,\n                        status._status['foreign_account'],\n                        status._status['description'],\n                        '%.02f EUR' % status._status['payment'],\n                        '%.02f EUR' % status._status['interest'],\n                        '%.02f EUR' % status._status['account']]\n                rows.append(item)\n        else:\n            header = ['date', 'payment', 'interest', 'account']\n            for status in report._statuses:\n                item = [status.strdate,\n                        '%.02f EUR' % status._status['payment'],\n                        '%.02f EUR' % status._status['interest'],\n                        '%.02f EUR' % status._status['account']]\n                rows.append(item)\n        return {'header': header, 'rows': rows}\n\n    def is_finished(self):\n        \"\"\" Returns true, if the loan has been payed back, including\n        interest for the current year \"\"\"\n        return (self._caccount + self._sum_interest) >= 0.\n\n    def make_report(self, payment = 0, interest = 0,\n                    foreign_account = '', kind = '', description = '',\n                    meta = {}):\n        \"\"\" creates a report entry and resets some variables \"\"\"\n        self._report.append(\n                            date = self._current_date,\n                            account = self._caccount / 100,\n                            payment = payment / 100,\n                            interest = float('%.2f' % (interest / 100)),\n                            foreign_account = foreign_account,\n                            kind = kind,\n                            description = description,\n                            meta = meta\n                            )\n\n    @property\n    def account(self):\n        return (self._caccount + self._sum_interest) / 100\n\n    def get_account(self):\n        return self.account\n\n    def exec_interest_time(self):\n        \"\"\" Does all things, when self.interest_time() returns true (like adding\n        interests to the account \"\"\"\n        self._caccount = int(round(self._caccount + self._sum_interest))\n        self.make_report(\n                         interest = self._sum_interest,\n                         kind = 'yearly interest'\n                         )\n        self._sum_interest = 0\n\n    def interest_time(self):\n        \"\"\" Checks, whether it is time to book the interests to the account \"\"\"\n        return (((self._current_date.day == self._interest_paydate['day']) and\n                (self._current_date.month == self._interest_paydate['month'])) or\n                (self._caccount > 0))\n\n    def payment_input(self, account_str, payment, kind, description, meta):\n        \"\"\" Input function for payments. This account is the receiver\n        of a transfer. This function, if derived from,\n        can account for special checks for input operations \"\"\"\n        if ((self._caccount + self._sum_interest) >= 0):\n            return TransferMessage(C_transfer_NA, money = 0, message = \"No credit to pay for\")\n\n        payed = min(-(self._caccount + self._sum_interest), payment)\n        if payed == payment:\n            self._caccount = int(self._caccount + payed)\n            report = {'payment': payed,\n                      'foreign_account': account_str,\n                      'kind': kind,\n                      'description': description,\n                      'meta': meta}\n            self.make_report(**report)\n        else:\n            self._caccount = int(self._caccount + self._sum_interest + payed)\n            report = {'payment': payed,\n                      'interest': self._sum_interest,\n                      'foreign_account': account_str,\n                      'kind': kind,\n                      'description': description + ' + Interests',\n                      'meta': meta}\n            self.make_report(**report)\n            self._sum_interest = 0\n        return TransferMessage(C_transfer_OK, money = payed)\n\n    def payment_output(self, account_str, payment, kind, description, meta):\n        \"\"\" Output function for payments. This account is the sender\n        of a transfer. This function, if derived from,\n        can account for special checks for output operations \"\"\"\n        return TransferMessage(C_transfer_NA, money = 0, message = \"Credit cannot be increased\")\n\n    def return_money(self, money):\n        \"\"\" this is a hard return of transfer-money, in case the receiving side\n        rejected the transfer \"\"\"\n        self._caccount = int(self._caccount + money)\n        report = {'date': self._current_date,\n                  'account': self._caccount,\n                  'payment': money,\n                  'kind': 'storno',\n                  'description': 'transfer did not succeeded'}\n        self._report.append(**report)\n\n    def start_of_day(self):\n        \"\"\" Things that should happen on the start of the day, before any money\n        transfer happens \"\"\"\n        pass\n\n    def end_of_day(self):\n        \"\"\" Things that should happen at the end of the day, after all money\n        transfers have been accomplished \"\"\"\n        # TODO: needs to be replaced by a mechanism that checks not every day\n        days_per_year = get_days_per_year(self._current_date.year)\n\n        # calculate interest for this day\n        interest = self._caccount * (self._interest / days_per_year)\n\n        # store interest for later calculations\n        self._sum_interest += interest\n\n        # if paydate is there, add the summed interest to the account\n        if self.interest_time():\n            self.exec_interest_time()\n\nclass Property(Account):\n    \"\"\"\n    This class can be used to reflect the amount of property that is gained\n    from filling up a loan. This account does nothing else than adjusting the\n    amount of property depending on the payments transfered to the loan class\n    \"\"\"\n\n    def __init__(self, property_value, amount, loan, date = None, name = None, meta = {}):\n        \"\"\"\n        For a property with a given value (property_value), the current amount\n        that is transfered to the owner (amount) is reflected by the amount of\n        money that has been transfered to the loan. Loan must here of class\n        loan\n        property_value : value of the property \n        amount         : amount of money that represents the ownership. if \n                         amount=property_value, the property totally belongs to the owner, if\n                         amount<property_value, the property partly belongs to the loan holder\n        loan           : object of type loan, that is linked to this property\n        date           : date, for which this property starts to exist\n        name           : name of this property\n        meta           : meta-information        \n        \"\"\"\n        \n        assert isinstance(loan, Loan), 'loan must be of type Loan, but is in fact of type ' + str(type(loan))\n        assert property_value >= amount, 'property_value must be greater than amount'\n\n        self._name = validate.valid_name(name)\n        self._date_start = validate.valid_date(date)\n        self._meta = meta\n\n        self._property_value = int(property_value * 100)\n        self._account = int(amount*100)     # amount of money already invested\n        self._caccount = self._account\n        self._loan = loan\n\n        # setting up the report and the semantics\n        self._report = Report(name = self._name)\n\n        self._report.add_semantics('account', 'saving_abs')\n        self._report.add_semantics('property_value', 'none')\n\n        self._current_date = self._date_start\n\n        self.make_report()\n\n    def make_report(self):\n        \"\"\" creates a report entry and resets some variables \"\"\"\n        self._report.append(\n                            date = self._current_date,\n                            account = self._caccount / 100,\n                            property_value = self._property_value / 100\n                            )\n\n    def get_table_json(self, report):\n        \"\"\" Creates a table for a given report \"\"\"\n        header = ['date', 'account']\n        rows = []\n        for status in report._statuses:\n            item = [status.strdate,\n                    '%.02f' % status._status['account']\n                    ]\n            rows.append(item)\n\n        return {'header': header, 'rows': rows}\n\n    def get_account(self):\n        return self._caccount / 100\n\n\n    def payment_input(self, account_str, payment, kind, description, meta):\n        \"\"\" Input function for payments. This account is the receiver\n        of a transfer. This function, if derived from,\n        can account for special checks for input operations \"\"\"\n        return TransferMessage(C_transfer_ERR, money = payment, message=\"Properties cannot be involved in transfers\")\n\n    def payment_output(self, account_str, payment, kind, description, meta):\n        \"\"\" Output function for payments. This account is the sender\n        of a transfer. This function, if derived from,\n        can account for special checks for output operations \"\"\"\n        return TransferMessage(C_transfer_ERR, money = payment, message=\"Properties cannot be involved in transfers\")\n\n    def return_money(self, money):\n        \"\"\" this is a hard return of transfer-money, in case the receiving side\n        rejected the transfer \"\"\"\n        pass\n\n    def end_of_day(self):\n        \"\"\" Things that should happen at the end of the day, after all money\n        transfers have been accomplished \"\"\"\n        new_caccount = self._account + (1- (self._loan._caccount / self._loan._account)) * (self._property_value - self._account)\n        # this if-clause is included to avoid daily reporting. Reports are\n        # just updates, if account volume changes or if if is the end of a year\n        if ((new_caccount != self._caccount) or\n           ((self._current_date.day == 31) and\n             (self._current_date.month == 12))):\n            self._caccount = new_caccount\n            self.make_report()\n"
  },
  {
    "path": "financial_life/financing/colors.py",
    "content": "'''\nCreated on 30.03.2016\n\n@author: martin\n'''\n\nimport xml.etree.ElementTree as ET\nimport re\n\ndef import_colors():\n    group_tag = '{http://www.w3.org/2000/svg}g'\n    rect_tag = '{http://www.w3.org/2000/svg}rect'\n    \n    color_file = '/home/martin/ownCloud/Software/credit/misc/colors.svg'\n    \n    tree = ET.parse(color_file)\n    root = tree.getroot()\n    layer = root.find(group_tag)\n    groups = layer.findall(group_tag)\n    \n    colorgroups = []\n    \n    for group in groups:\n        schemas = group.findall(group_tag)\n        colorgroup = []\n        for schema in schemas:\n            rects = schema.findall(rect_tag)\n            colors = []\n            for rect in rects:\n                colors.append(re.search('#[0-9a-f]{6}', rect.attrib['style']).group())\n            colorgroup.append(colors)\n        colorgroups.append(colorgroup)\n    \n    print(colorgroups)\n\ncolors = [\n          [\n           ['#9e63e7', '#b283ed', '#cbaaf5', '#e0ccf9', '#884dd7', '#6925c5', '#5512b0', '#430596'], \n           ['#6463e6', '#8483ec', '#aaa9f5', '#cccbf9', '#4c4dd7', '#2526c4', '#1113b0', '#050596'], \n           ['#629ce6', '#83b1eb', '#a8caf5', '#cbdef9', '#4b8bd7', '#256dc3', '#105ab0', '#054696'], \n           ['#62cae5', '#82d5eb', '#a7e4f5', '#cbeef9', '#4bbbd6', '#25a4c3', '#0f92b0', '#057896'], \n           ['#62e4df', '#82eae6', '#a6f5f2', '#cbf9f8', '#4ad6ce', '#25c3ba', '#0fb0a5', '#05968f']\n          ], \n          [\n           ['#c6e662', '#d2eb83', '#e2f5a8', '#edf9cb', '#b7d74b', '#a0c325', '#8cb010', '#749605'], \n           ['#e6e462', '#ebeb83', '#f5f4a8', '#f8f9cb', '#d7d34b', '#c3c025', '#b0ab10', '#969305'], \n           ['#e5a962', '#ebbb82', '#f5d1a7', '#f9e5cb', '#d6944b', '#c37825', '#b0630f', '#965205'], \n           ['#e47a62', '#ea9682', '#f5b6a6', '#f9d5cb', '#d6634a', '#c34125', '#b02a0f', '#962005'], \n           ['#e36269', '#e98287', '#f4a6aa', '#f9cbcc', '#d64954', '#c32530', '#b00f1c', '#96050e']\n          ], \n          [\n           ['#a2a2a2', '#b5b5b5', '#cdcdcd', '#e2e2e2', '#8f8f8f', '#747474', '#5f5f5f', '#4d4d4d']\n          ], \n          [\n           ['#7a68b3', '#9080c1', '#aa9dd3', '#c1b7df', '#6b5e97', '#4f4378', '#392b68', '#261953'], \n           ['#6876b2', '#808dc0', '#9ca6d3', '#b6bddf', '#5d6997', '#434e77', '#2a3768', '#192553'], \n           ['#6797b2', '#81a8be', '#9bbfd3', '#b6cfdf', '#5d8396', '#426577', '#295368', '#193f53'], \n           ['#68b0b0', '#80bdbe', '#9ad2d3', '#b6dddf', '#5d9595', '#427777', '#286867', '#195353'], \n           ['#68af9e', '#81bdae', '#99d3c5', '#b6dfd6', '#5d9486', '#427769', '#286857', '#195345']\n          ], \n          [\n           ['#aeb267', '#bcbe81', '#d1d39b', '#dcdfb6', '#95965d', '#767742', '#676829', '#515319'], \n           ['#b2a267', '#beb281', '#d3c79b', '#dfd8b6', '#96895d', '#776b42', '#685a29', '#534619'], \n           ['#b08168', '#be9580', '#d3ad9a', '#dfc5b6', '#956f5d', '#775342', '#683d28', '#532c19'], \n           ['#af6869', '#bd8181', '#d39999', '#dfb7b6', '#945d5e', '#774244', '#68282a', '#53191a'], \n           ['#ad697a', '#bb818f', '#d19aa8', '#dfb6bf', '#945c6c', '#774251', '#68283a', '#531928']\n          ]\n         ]\n\nno_colors = len(colors[0])"
  },
  {
    "path": "financial_life/financing/identity.py",
    "content": "'''\nCreated on 18.11.2016\n\nSome basic classes for generating and managing identities\n\n@author: martin\n'''\n# standard libraries\nimport string\nimport random\n\ndef id_generator(\n                 size=6, \n                 chars=string.ascii_uppercase + string.ascii_lowercase + string.digits\n                 ):\n    \"\"\" Creates a unique ID consisting of a given number of characters with \n    a given set of characters \"\"\"\n    return ''.join(random.choice(chars) for _ in range(size))"
  },
  {
    "path": "financial_life/financing/plotting.py",
    "content": "'''\nCreated on 30.03.2016\n\n@author: martin\n'''\n#standard libraries\nfrom datetime import timedelta\n\n# custom libraries\nfrom matplotlib.pyplot import *\nfrom matplotlib import pyplot as plt\nimport matplotlib.patches as mpatches\nimport numpy as np\n\n# own libraries\nfrom .colors import colors, no_colors\nfrom financial_life.calendar_help import Bank_Date\n\n#ion()\n\n# indices for colors in the colors-list\nC_cold_colors = 0\nC_warm_colors = 1\nC_format_date = '%d.%m.%Y'\n\ndef bank_account(*reports):\n    fig = figure(figsize=(16, 13))\n    plot_stack_mult_abs(['input_cum', 'output_cum'], *reports, \n                    color_themes = [C_warm_colors, C_cold_colors], \n                    color_offset = 1)\n    title('Demands on the account')\n    return fig\n\ndef summary(*reports):\n    \"\"\" Summary plot for all reports \"\"\"\n    \n    fig = figure(figsize=(16, 13))\n    ax_date = subplot(3,2,1)\n    plot_stack_abs('saving_abs', *reports, color_theme = C_warm_colors, color_offset = 0)\n    title('Wealth')\n    \n    subplot(3,2,2, sharex = ax_date)\n    plot_stack_abs('debt_abs', *reports, color_theme = C_cold_colors, color_offset = 0)\n    title('Debts')\n    \n    subplot(3,2,3, sharex = ax_date)\n    plot_stack_mult_abs(['input_cum', 'output_cum'], *reports, \n                        color_themes = [C_warm_colors, C_cold_colors], \n                        color_offset = 1)\n    title('Input and output')\n    \n    subplot(3,2,4, sharex = ax_date)\n    plot_stack_abs('debtpayment_cum', *reports, color_offset = 1)\n    title('Yearly payments')\n    \n    ax_winloss = subplot(3,2,5, sharex = ax_date)\n    plot_stack_cum('win_cum', *reports, color_theme = C_warm_colors, color_offset = 2)\n    title('Cumulated win')\n    \n    subplot(3,2,6, sharex = ax_date, sharey = ax_winloss)\n    plot_stack_cum('cost_cum', *reports, color_offset = 2)\n    title('Cumulated costs')\n    plt.tight_layout()\n    return fig\n\ndef summary_img(*reports, target = './', figsize = (10, 5), dpi = 100, prefix=''):\n    \"\"\" creates a series of images and stores them in the target directory \"\"\"\n    \n    data = {}\n    \n    fig = figure(figsize = figsize)\n    plot_stack_abs('saving_abs', *reports, color_theme = C_warm_colors, color_offset = 0)\n    title('Wealth')\n    fig.savefig(target + prefix + 'wealth.png', dpi = dpi)\n    plt.close(fig)\n    data['img_wealth'] = target + prefix + 'wealth.png'\n    \n    fig = figure(figsize = figsize)\n    plot_stack_abs('debt_abs', *reports, color_theme = C_cold_colors, color_offset = 0)\n    title('Debts')\n    fig.savefig(target + prefix + 'debts.png', dpi = dpi)\n    plt.close(fig)\n    data['img_debts'] = target + prefix + 'debts.png'\n    \n    fig = figure(figsize = figsize)\n    plot_stack_mult_abs(['input_cum', 'output_cum'], *reports, \n                        color_themes = [C_warm_colors, C_cold_colors], \n                        color_offset = 1)\n    title('Input and output')\n    fig.savefig(target + prefix + 'io_money.png', dpi = dpi)\n    plt.close(fig)\n    data['img_io_money'] = target + prefix + 'io_money.png'\n\n    fig = figure(figsize = figsize)\n    plot_stack_abs('debtpayment_cum', *reports, color_offset = 1)\n    title('Yearly payments')\n    fig.savefig(target + prefix + 'debtpayment.png', dpi = dpi)\n    plt.close(fig)\n    data['img_debtpayment'] = target + prefix + 'debtpayment.png'\n    \n    fig = figure(figsize = figsize)\n    plot_stack_cum('win_cum', *reports, color_theme = C_warm_colors, color_offset = 2)\n    title('Cumulated win')\n    fig.savefig(target + prefix + 'win_cum.png', dpi = dpi)\n    plt.close(fig)\n    data['img_win_cum'] = target + prefix + 'win_cum.png'\n    \n    fig = figure(figsize = figsize)\n    plot_stack_cum('cost_cum', *reports, color_offset = 2)\n    title('Interests')\n    fig.savefig(target + prefix + 'cost_cum.png', dpi = dpi)\n    plt.close(fig)\n    data['img_cost_cum'] = target + prefix + 'cost_cum.png'\n    \n    return data\n     \n\ndef extract_data(semantic, *reports, color_theme = C_cold_colors, color_offset = 0):\n    \"\"\" helping function that extracts the dates and data from the reports \"\"\"\n    X = []\n    Y = []\n    c = []\n    # create cost plots\n    for j, r in enumerate(reports):\n        X = X + [[d.timestamp() for d in r.date]]\n        Y = Y + [[r.get(k) for k in r.semantics(semantic)]]\n        \n        c = c + [colors[color_theme][j % no_colors][i+color_offset] for i, k in enumerate(r.semantics(semantic))]\n    \n    return X, Y, c\n\ndef add_labels(semantic, *reports, color_theme = C_cold_colors, color_offset = 0):\n    \"\"\" routine that adds labels to the plot in order to add a legend \"\"\"\n    # these empty plots are needed as stack plot does not support labels for legends. bit of \n    # a hack from http://stackoverflow.com/questions/14534130/legend-not-showing-up-in-matplotlib-stacked-area-plot\n    for j, r in enumerate(reports):\n        for i, k in enumerate(r.semantics(semantic)):\n            plot([], [], \n                 color=colors[color_theme][j % no_colors][i+color_offset], \n                 label=r.name + ': ' + k,\n                 linewidth=10)\n            \ndef plot_stack_generic(X, Y, c, semantic, *reports, color_theme = C_cold_colors, color_offset = 0):\n    \"\"\" generic function for plotting stacks \"\"\"\n    # bring the data together\n    dates, data = join_data(X, Y)\n    \n    # format the dates to a readable format\n    str_dates = [Bank_Date.fromtimestamp(d).strftime(C_format_date) for d in dates]\n    \n    add_labels(semantic, *reports, color_theme = color_theme, color_offset = color_offset)\n\n    if len(data) > 0:\n        stackplot(dates, data, colors = c)\n    else:\n        # if data is empty, plot at least zeros to make this plot more complete\n        plot(dates, np.zeros(len(dates)))\n    \n    xticks(dates, str_dates, rotation=45)\n\ndef remove_nones(X):\n    \"\"\" Removes all Nones from a list and replaces them by zero \"\"\"\n    return [[[0. if v is 'None' else v for v in d] for d in data] for data in X]\n\ndef add_zeros(X, Y):\n    \"\"\" add zeros at the beginning and end to prevent interpolation in join_data\n    from stacking to much up \"\"\"\n    for x in X:\n        x.insert(0, x[0] - 1)\n        x.append(x[-1] + 1)\n\n    for data in Y:\n        for d in data:\n            d.insert(0, 0.)\n            d.append(0.)\n    \n    return X, Y\n\ndef plot_stack_abs(semantic, *reports, color_theme = C_cold_colors, color_offset = 0):\n    \"\"\" Creates a stacked plot with cumulated sums for a given semantic \"\"\" \n    X, Y, c = extract_data(semantic, *reports, color_theme = color_theme, color_offset = color_offset)\n    Y = remove_nones(Y)\n    X, Y = add_zeros(X, Y)\n\n    # create the generic plot\n    plot_stack_generic(X, Y, c, semantic, *reports, color_theme = color_theme, color_offset = color_offset)\n    legend(loc = 'upper right', fancybox=True, framealpha=0.4, prop={'size':10})\n    \ndef plot_stack_mult_abs(semantics, *reports, color_themes, color_offset = 0):\n    \"\"\" Creates a stacked plot with cumulated sums for a given semantic \"\"\" \n    for semantic, color_theme in zip(semantics, color_themes):\n        plot_stack_abs(semantic, *reports, color_theme = color_theme, color_offset = color_offset)\n\ndef plot_stack_cum(semantic, *reports, color_theme = C_cold_colors, color_offset = 0):\n    \"\"\" Creates a stacked plot with cumulated sums for a given semantic \"\"\" \n    X, Y, c = extract_data(semantic, *reports, color_theme = color_theme, color_offset = color_offset)\n    Y = remove_nones(Y)\n    # add zeros only at the beginning    \n    for x in X:\n        x.insert(0, x[0] - 1)\n    \n    for data in Y:\n        for d in data:\n            d.insert(0, 0.)\n                \n    # create the cumulated sum\n    Y = [np.cumsum(np.array(yi), 1) if yi else [] for yi in Y]\n\n    plot_stack_generic(X, Y, c, semantic, *reports, color_theme = color_theme, color_offset = color_offset)\n    legend(loc = 'upper left', fancybox=True, framealpha=0.4, prop={'size':10})    \n    \ndef join_data(dates_list, data_list):\n    \"\"\" This functions makes heterogenous time series data align\n    with one time series axis \n    dates   : list of date-lists\n    data    : list of data-lists_lock\n    \n    Returns:\n        dates, and data, but this time, data shares the same\n        date-points\n    \"\"\"\n    # first get all unique dates from every sublist and make one list out of them\n    rdates = sorted(list(set([date for sublist in dates_list for date in sublist])))\n    rdata = []\n    \n    # go through each vector and interpolate data if necessary\n    for dates, data_vecs in zip(dates_list, data_list):\n        for data in data_vecs:\n            if len(data) > 0:\n                rdata.append(np.interp(rdates,dates, data).tolist())\n            else:    # if data is empty, then just create a zero-length vector\n                rdata.append(np.zeros(len(rdates)))\n    return rdates, rdata\n                    "
  },
  {
    "path": "financial_life/financing/test_financing.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Thu Jun 23 21:40:47 2016\n\n@author: martin\n\"\"\"\n\nimport unittest\nimport financial_life.financing as financing\nfrom financial_life.calendar_help import Bank_Date\nfrom datetime import datetime\n\nclass Test_Create_Stop_Criteria(unittest.TestCase):\n\n    def test_date(self):\n        t = datetime(2016,10,15)\n        foo = financing.create_stop_criteria(t)\n        self.assertTrue(foo(datetime(2016,10,14)))\n        self.assertFalse(foo(datetime(2016,10,16)))\n\n    def test_callable(self):\n        t = lambda x: x < datetime(2016,11,18)\n        foo = financing.create_stop_criteria(t)\n        self.assertFalse(foo(datetime(2016,10,14)))\n        self.assertFalse(foo(datetime(2016,11,14)))\n        self.assertTrue(foo(datetime(2016,11,19)))\n\nclass TestRegular_Month_Payment(unittest.TestCase):\n\n\n    def setUp(self):\n        self.regular =  {'from_acc': 'Dummy',\n                         'to_acc': 'Dummy',\n                         'interval': 'month',\n                         'day' : 15,\n                         'date_start': Bank_Date(2015, 3, 15),\n                         'date_stop': Bank_Date(2015, 6, 15),\n                         'payment': 3000,\n                         'name' : 'Test',\n                         'fixed': True,\n                         'meta': {}\n                         }\n        self.infinite = {'from_acc': 'Dummy',\n                         'to_acc': 'Dummy',\n                         'interval': 'month',\n                         'day' : 15,\n                         'date_start': Bank_Date(2015, 3, 15),\n                         'date_stop': Bank_Date.max,\n                         'payment': 3000,\n                         'name' : 'Test',\n                         'fixed': True,\n                         'meta': {}\n                         }\n        self.lastcal =  {'from_acc': 'Dummy',\n                         'to_acc': 'Dummy',\n                         'interval': 'month',\n                         'day' : 31,\n                         'date_start': Bank_Date(2015, 1, 31),\n                         'date_stop': Bank_Date(2015, 6, 15),\n                         'payment': 3000,\n                         'name' : 'Test',\n                         'fixed': True,\n                         'meta': {}\n                         }\n\n    def test_begin_payment_next_month(self):\n        iterator = financing.iter_regular_month(self.regular, date_start = datetime(2015,3, 16))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2015,4,15))\n\n        iterator = financing.iter_regular_month(self.regular, date_start = datetime(2016,3, 16))\n        self.assertRaises(StopIteration, next, iterator)\n\n    def test_begin_payment_next_year(self):\n        iterator = financing.iter_regular_month(self.infinite, date_start = datetime(2016,3, 16))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2016, 4, 15))\n\n    def test_begin_payment_this_month(self):\n        iterator = financing.iter_regular_month(self.regular, date_start = datetime(2015,3, 15))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2015,3,15))\n\n    def test_proper_sequence(self):\n        iterator = financing.iter_regular_month(self.regular, date_start = datetime(2015,3, 20))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2015,4,15))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2015,5,15))\n        self.assertRaises(StopIteration, next, iterator)\n\n    def test_last_calender_day(self):\n        iterator = financing.iter_regular_month(self.lastcal, date_start = datetime(2015,2, 28))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2015,2,28))\n\n\nclass TestRegular_Year_Payment(unittest.TestCase):\n\n\n    def setUp(self):\n        self.regular =  {'from_acc': 'Dummy',\n                         'to_acc': 'Dummy',\n                         'interval': 'yearly',\n                         'day' : 15,\n                         'date_start': Bank_Date(2015, 3, 15),\n                         'date_stop': Bank_Date(2018, 3, 14),\n                         'payment': 3000,\n                         'name' : 'Test',\n                         'fixed': True,\n                         'meta': {}\n                         }\n        self.infinite = {'from_acc': 'Dummy',\n                         'to_acc': 'Dummy',\n                         'interval': 'yearly',\n                         'day' : 15,\n                         'date_start': Bank_Date(2015, 3, 15),\n                         'date_stop': Bank_Date.max,\n                         'payment': 3000,\n                         'name' : 'Test',\n                         'fixed': True,\n                         'meta': {}\n                         }\n        self.lastcal =  {'from_acc': 'Dummy',\n                         'to_acc': 'Dummy',\n                         'interval': 'yearly',\n                         'day' : 31,\n                         'date_start': Bank_Date(2015, 1, 31),\n                         'date_stop': Bank_Date(2017, 6, 15),\n                         'payment': 3000,\n                         'name' : 'Test',\n                         'fixed': True,\n                         'meta': {}\n                         }\n\n    def test_begin_payment_same_year(self):\n        iterator = financing.iter_regular_year(self.regular, date_start = datetime(2015,3, 15))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2015,3,15))\n\n\n    def test_begin_payment_next_year(self):\n        iterator = financing.iter_regular_year(self.infinite, date_start = datetime(2015,3, 16))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2016, 3, 15))\n\n    def test_proper_sequence(self):\n        iterator = financing.iter_regular_year(self.regular, date_start = datetime(2015,3, 20))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2016,3,15))\n        payment = next(iterator)\n        self.assertEqual(payment['date'], datetime(2017,3,15))\n        self.assertRaises(StopIteration, next, iterator)\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "financial_life/financing/test_meta.py",
    "content": "'''\nCreated on 04.01.2017\n\n@author: martin\n'''\n# standard libraries\nfrom datetime import datetime, timedelta\nimport unittest\n\n# own libraries\nfrom financial_life.examples import meta_data\n\nclass Test(unittest.TestCase):\n\n\n    def setUp(self):\n        # run a simulation that makes use of meta-information\n        self.s = meta_data.example_meta_controller(print_it=False)\n\n\n    def tearDown(self):\n        pass\n\n\n    def test_account_and_simulation(self):\n        \"\"\" Test that meta-information are in the account class and\n        in the simulation class \"\"\"\n        accounts = self.s.accounts[0]\n        s_income_report = self.s.report.subset(\n            lambda st: st.meta.get('type','') == 'income')\n        a_income_report = accounts.report.subset(\n            lambda st: st.meta.get('type','') == 'income')\n        \n        s_interests = sum(s_income_report.value)\n        a_interests = sum(a_income_report.input)\n        \n        self.assertTrue(len(s_income_report) == len(a_income_report), \n            'Reprots in Simulation and Account class with meta-information have not the same length')\n        self.assertTrue(s_interests == a_interests, \n            'Values in the account and simulation class are not the same')\n\nif __name__ == \"__main__\":\n    #import sys;sys.argv = ['', 'Test.testName']\n    unittest.main()"
  },
  {
    "path": "financial_life/financing/test_status.py",
    "content": "'''\nCreated on 15.12.2016\n\n@author: martin\n'''\n# standard libraries\nfrom datetime import datetime, timedelta\nimport unittest\n\n# own libraries\nfrom financial_life.financing import accounts as a\nfrom financial_life.financing import Status\n\n\nclass Test(unittest.TestCase):\n\n\n    def setUp(self):\n        \"\"\" Create setup for making sure that meta-data are\n        transfered to the status of payments \"\"\"\n        account = a.Bank_Account(amount = 1000, interest = 0.001, name = 'Main account', date=datetime(2016,9, 1))\n        savings = a.Bank_Account(amount = 5000, interest = 0.013, name = 'Savings', date=datetime(2016,9, 1))\n        loan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit', date=datetime(2016,9, 1))\n\n        simulation = a.Simulation(account, savings, loan, name = 'Testsimulation', date=datetime(2016,9, 1))\n        simulation.add_regular(from_acc = 'Income',\n                               to_acc = account,\n                               payment = 2000,\n                               interval = 'monthly',\n                               date_start = datetime(2016,9,15),\n                               day = 15,\n                               name = 'Income')\n\n        simulation.add_regular(from_acc = account,\n                               to_acc = savings,\n                               payment = 500,\n                               interval = 'monthly',\n                               date_start = datetime(2016,9,30),\n                               day = 30,\n                               name = 'Savings',\n                               meta = {'tax': 100})\n\n        simulation.simulate(delta=timedelta(days=2000))\n        self.simulation = simulation\n\n\n    def test_InitStatus(self):\n        # initialize without data field\n        s = Status(datetime(2016,9,1), a=2, b=3, ceta=4)\n        self.assertDictEqual(s._status, {'a':2, 'b':3, 'ceta':4})\n        self.assertDictEqual(s._meta, {})\n\n        # initialize with data field\n        s = Status(datetime(2016,9,1), a=2, b=3, meta={'test': 3})\n        self.assertDictEqual(s._status, {'a':2, 'b':3})\n        self.assertDictEqual(s._meta, {'test': 3})\n\n    def test_str(self):\n        s = Status(datetime(2016,9,1), a=2, b=3, ceta=4)\n        str(s)\n\n    def test_SimulationReport(self):\n        # the report of the simulation-class itself should be displayable\n        status1 = self.simulation.report._statuses[0]\n        status2 = self.simulation.report._statuses[1]\n        self.assertDictEqual(status1._meta, {})\n        print(status2._meta)\n        self.assertDictEqual(status2._meta, {'tax': 100})\n\n\nif __name__ == \"__main__\":\n    #import sys;sys.argv = ['', 'Test.testName']\n    unittest.main()\n"
  },
  {
    "path": "financial_life/financing/validate.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Mon Jun 27 21:27:23 2016\n\nCollection of validation messages\n\n@author: martin\n\"\"\"\n# standard libraries\nfrom datetime import datetime, timedelta\nfrom collections import Callable\n\n# custom libraries\n\n# own libraries\nfrom financial_life.financing.identity import id_generator\nfrom financial_life.calendar_help import Bank_Date\n\n\ndate_formats = [\n                '%d.%m.%Y',\n                '%d.%m.%y',\n                '%m/%d/%Y',\n                '%m/%d/%y',\n                '%Y-%m-%d',\n                '%y-%m-%d',\n                ]\n\ndef parse_datestring(datestr):\n    \"\"\" Tries to parse the datestring against a few common formats \"\"\"\n    for format in date_formats:\n        try:\n            date = Bank_Date.strptime(datestr, format)\n            return date\n        except ValueError:\n            pass\n\ndef valid_date(date):\n    \"\"\" routine for making a date out of anything that the user might\n    have given to the function \"\"\"\n    if date is None:\n        return Bank_Date.today()\n    if isinstance(date, Bank_Date):\n        return date\n    if isinstance(date, datetime):\n        return Bank_Date.fromtimestamp(date.timestamp())\n    if isinstance(date, str):\n        return parse_datestring(date)\n    raise TypeError(\"Date must be from type datetime, callable or a parseable string\")\n\ndef valid_stop_date(date):\n    \"\"\" routine for makig a date out of anything that the user might\n    have given to the function \"\"\"\n    if isinstance(date, Callable):\n        return date\n    else:\n        return valid_date(date)\n    raise TypeError(\"Date must be at least from type datetime or callable\")\n\n    \ndef valid_name(name):\n    if not name:\n        name = id_generator(8)\n    return name\n    \ndef valid_date_stop(date_stop):\n    \"\"\" checks, whether date_stop has a valid value \"\"\"\n    if not date_stop:\n        date_stop = Bank_Date.max\n    return date_stop\n    \ndef valid_delta(delta):\n    \"\"\" converts delta, if necessary, into a timedelta instance \"\"\"\n    if not delta:\n        delta = timedelta.max\n    if isinstance(delta, int):\n        delta = timedelta(days = delta)\n    assert isinstance(delta, timedelta), 'delta must be of type timedelta or int'\n    assert delta.days > 0, 'delta must be positive'\n    return delta\n"
  },
  {
    "path": "financial_life/products/__init__.py",
    "content": ""
  },
  {
    "path": "financial_life/products/germany/__init__.py",
    "content": ""
  },
  {
    "path": "financial_life/products/germany/lbs/__init__.py",
    "content": "''' \n\nThis is a reconstruction of the LBS Bauspar Tarife \n\nWARNING: This is a manual attempt to simualate some of the LBS products. This is by no means\na reliable and accurate simulation! For a realistic simulation of your financial plans, please\nconsult a pofessional assistent from the company selling these products.\n\n'''\n\n# standard libraries\nfrom datetime import datetime, timedelta\nfrom calendar import monthrange\nfrom decimal import *\n\n# custom libraries\nimport numpy as np\n\n# own libraries\nfrom financial_life.financing import Report, Payments\nfrom financial_life.financing import C_default_payment, id_generator\nfrom financial_life.financing.colors import colors\nfrom financial_life.financing import validate\nfrom financial_life.financing.accounts import Account, C_format_date, C_max_time\nfrom financial_life.calendar_help import Bank_Date\n\nflex_l5 = {\n           'C_POINT_PER_DAY': 0.0563,\n           'C_POINT_PER_EUR': 1 / 750.,\n           'C_POINT_LIMIT': 171,\n           'guthabenzins' : 0.0025,\n           'entgelt' : 7.2,\n           'bausparanteil': 0.4,\n           'darlehenszins': 0.0215,\n           'wartemonate': 4,\n           'agio': 0.02,\n           'versicherung': 0.003,\n           'name' : 'Flex L5',\n    }\n\ndirekt_10 = {\n           'C_POINT_PER_DAY': 0.0403,\n           'C_POINT_PER_EUR': 1 / 650.,\n           'C_POINT_LIMIT': 176,\n           'guthabenzins' : 0.001,\n           'entgelt' : 7.2,\n           'bausparanteil': 0.4,\n           'darlehenszins': 0.0195,\n           'wartemonate': 3,\n           'agio': 0.02,\n           'versicherung': 0.003,\n           'name' : 'Direkt 10',\n    }\n\ndirekt_15 = {\n           'C_POINT_PER_DAY': 0.0403,\n           'C_POINT_PER_EUR': 1 / 650.,\n           'C_POINT_LIMIT': 176,\n           'guthabenzins' : 0.001,\n           'entgelt' : 7.2,\n           'bausparanteil': 0.4,\n           'darlehenszins': 0.0175,\n           'wartemonate': 3,\n           'agio': 0.02,\n           'versicherung': 0.003,\n           'name' : 'Direkt 15',\n    }\n\nalternative = {\n           'C_POINT_PER_DAY': 0.0403,\n           'C_POINT_PER_EUR': 1 / 650.,\n           'C_POINT_LIMIT': 176,\n           'guthabenzins' : 0.001,\n           'entgelt' : 7.2,\n           'bausparanteil': 0.4,\n           'darlehenszins': 0.025,\n           'wartemonate': 3,\n           'agio': 0.02,\n           'versicherung': 0.003,\n           'name' : 'Direkt 15',\n    }\n\ntarife = {\n          'flex_l5': flex_l5,\n          'direkt_10': direkt_10,\n          'direkt_15': direkt_15,\n          'alternative': alternative\n          }\n\nclass Bauspar(Account):\n    \"\"\" This is a generic class for the german LBS Bauspar product \"\"\"\n    \n    def __init__(self, guthaben, bausparsumme, punkte, tarif, date = None, name = None):\n        if tarif not in tarife:\n            raise TypeError(\"Contract type not found in contract list: {}\".format(tarif))\n\n        self._tarif = tarife[tarif]\n        \n        self._date_start = self.valid_date(date)\n        self._name = self.valid_name(name)\n        \n        self._report = Report(\n            name = self._name + ' - ' + str(self._date_start.strftime(C_format_date)))\n        self._report.add_semantics('account', 'saving_abs')\n        self._report.add_semantics('loan', 'debt_abs')\n        self._report.add_semantics('loan_interest', 'cost_cum')\n        self._report.add_semantics('account_interest', 'win_cum')\n        self._report.add_semantics('points', 'none')\n        self._report.add_semantics('payments', 'debtpayment_cum')\n        self._report.add_semantics('agio', 'cost_cum')\n        self._report.add_semantics('insurance', 'cost_cum')\n        self._report.add_semantics('entgelt', 'cost_cum')\n        \n        self._guthaben = int(guthaben * 100)\n        self._bausparsumme = int(bausparsumme * 100)\n        self._darlehen = self._bausparsumme - np.max(\n            [\n             self._bausparsumme * self._tarif['bausparanteil'],\n             self._guthaben\n            ]\n        )\n        self._punkte = punkte\n        \n        self._payments = Payments()\n        self._payments_iter = None\n        \n        self._sum_interest = 0          # this value is used because it is inherited from account\n        self._sum_loan_interest = 0     # but we need an extra variable for the loan interest\n        self._sum_loan_insurance = 0\n        self._day = -1\n        self._current_date = self._date_start\n        self._caccount = self._guthaben\n        self._cdarlehen = self._darlehen\n        self._next_pay = None\n        \n        self._interest_paydate = {'month': 12, 'day': 31}\n        \n        # this function determines, in which phase of the product we are\n        self._phase = self.saving_phase\n        \n        # reporting functionality\n        self._record = {\n                        'loan_interest': 0,\n                        'account_interest': 0,\n                        'payments' : 0,\n                        'entgelt' : 0,\n                        'insurance': 0,\n                        'agio': 0\n                        }\n    \n\n    @property\n    def loan(self):\n        return self._cdarlehen / 100\n        \n    def get_loan(self):\n        \"\"\" alternative method to get the current loan value. this method \n        can be used, e.g. in payment-definitions to transfer the amount of \n        money that a specific account has in the moment this payment is done. \n        Instead of using an actual value, this method is called, evaluated and\n        the return value is used \"\"\"\n        return self.loan\n\n    def simulate(self, date_stop = None, delta = None, last_report = True):\n        \"\"\" Simulates the state of the account to the end-date.\n        If there is no end_date, the simulation will run until account is either \n        zero or the account continuously increases 10 times in a row\n        \n            delta:\n                Time (e.g. days) to simulate. This argument can be used along\n                with date_stop. Whatever comes first, aborts the while-loop\n            last_report: \n                if True, after the while-loop a report will be added. For \n                simulations along with other products, this can be omitted by\n                setting this argument to False\n        \"\"\"\n        date_stop = validate.valid_date_stop(date_stop)\n        delta = validate.valid_delta(delta)\n        # if this is not the time for this product, abort\n        if date_stop < self._current_date:\n            return\n        \n        if (not self._payments_iter):\n            self._payments_iter = self._payments.payment(self._current_date)\n        \n        if (not self._next_pay):\n            self._next_pay = next(self._payments_iter, C_default_payment)\n        \n        self._phase(date_stop, delta, last_report)\n        \n    def get_credit(self):\n        \"\"\" Switches to a new modus in this product, in which the loan is given to the \n        customer. Depending on the amount of points and the account, the customer \n        needs to enter the so-called \"zwischenfinanzierung\"\n        \"\"\"\n        self._cdarlehen = self._bausparsumme - self._caccount\n        \n        if (self._punkte < self._tarif['C_POINT_LIMIT'] or \n            self._caccount < (self._tarif['bausparanteil'] * self._bausparsumme)):\n            self._phase = self.zwischen_phase\n        else:\n            self._phase = self.loan_phase\n            agio = self._cdarlehen * self._tarif['agio']\n            self._cdarlehen += agio\n            self._report.append(date = self._current_date,\n                                agio = (agio / 100))\n        \n    def loan_track_data(self, loan_interest, loan_insurance, payed):\n        self._record['loan_interest'] += loan_interest\n        self._record['insurance'] += loan_insurance\n        self._record['payments'] += payed\n    \n    def loan_make_report(self):\n        self._report.append(date = self._current_date,\n                            loan_interest = self._record['loan_interest'] / 100,\n                            insurance = self._record['insurance'] / 100,\n                            payments = self._record['payments'] / 100,\n                            loan = self._cdarlehen / 100,\n                            )\n        \n        # set everything to zero\n        self._record = dict.fromkeys(self._record, 0)        \n        \n    def loan_exec_interest_time(self):\n        self._cdarlehen = int(round(self._cdarlehen + self._sum_loan_interest + self._sum_loan_insurance))\n        self._sum_loan_interest = 0\n        self._sum_loan_insurance = 0\n        \n    def loan_phase(self, date_stop = None, delta = None, last_report = True):\n        \"\"\" Routine for the payment of the loan \"\"\"\n        temp_delta = 0\n        while ((self._current_date < date_stop) and     # ...stop-date is reached\n            (temp_delta < delta.days) and               # and delta has not been exeeded\n            ((self._current_date - self._date_start).days < C_max_time) and\n            (self._cdarlehen > 0)):  # ...number of simulated days exceeds max\n            \n            # go to next day\n            self._day += 1\n            self._current_date = self._date_start + timedelta(days = self._day)\n            temp_delta += 1\n            \n            # calculate the day\n            self._cdarlehen, loan_interest, loan_insurance, payed = self.loan_simulate_day()\n            \n            # store interest for later calculations\n            self._sum_loan_interest += loan_interest\n            self._sum_loan_insurance += loan_insurance\n            \n            # if paydate is there, add the summed interest to the account\n            if self.interest_time():\n                self.loan_exec_interest_time()\n            \n            # tracking for reports\n            self.loan_track_data(loan_interest, loan_insurance, payed)\n            \n            # make a report\n            if self.report_time(self._current_date):\n                self.loan_make_report()\n                \n            \n        # create report at the end of the simulation\n        if last_report:\n            # as the simulation might not end at the end of the year,\n            # we need to apply exec_interest_time() one last time\n            self.exec_interest_time()\n            self.loan_make_report()        \n        \n    def loan_simulate_day(self):\n        days_per_year = self.get_days_per_year()\n        \n        payed = self.get_payments()\n        payed = min(self._cdarlehen + self._sum_loan_interest + self._sum_loan_insurance, payed)\n        new_darlehen = int(self._cdarlehen - payed)\n\n        loan_interest = new_darlehen * (self._tarif['darlehenszins'] / days_per_year)\n        loan_insurance = new_darlehen * (self._tarif['versicherung'] / days_per_year)\n                \n        return new_darlehen, loan_interest, loan_insurance, payed        \n        \n    def zwischen_track_data(self, account_interest, loan_interest, payed, entgelt):\n        \"\"\" tracks data during saving phase \"\"\"\n        self._record['account_interest'] += account_interest\n        self._record['loan_interest'] += loan_interest\n        self._record['payments'] += payed\n        self._record['entgelt'] += entgelt\n        \n    def zwischen_make_report(self):\n        self._report.append(date = self._current_date,\n                            account = self._caccount / 100,\n                            account_interest = self._record['account_interest'] / 100,\n                            loan_interest = self._record['loan_interest'] / 100,\n                            payments = self._record['payments'] / 100,\n                            entgelt = self._record['entgelt'] / 100,\n                            loan = self._cdarlehen / 100,\n                            points = self._punkte\n                            )\n        \n        # set everything to zero\n        self._record = dict.fromkeys(self._record, 0)\n        \n    def zwischen_exec_interest_time(self):\n        self._caccount = int(round(self._caccount + self._sum_interest - self._sum_loan_interest))\n        self._sum_interest = 0\n        self._sum_loan_interest = 0     \n        \n    def zwischen_phase(self, date_stop = None, delta = None, last_report = True):\n        \"\"\" Routine for the phase called 'Zwischenfinanzierung' \"\"\"\n        temp_delta = 0\n        while ((self._current_date < date_stop) and     # ...stop-date is reached\n            (temp_delta < delta.days) and               # and delta has not been exeeded\n            ((self._current_date - self._date_start).days < C_max_time) and\n            (self._punkte < self._tarif['C_POINT_LIMIT'] or \n             self._caccount < (self._tarif['bausparanteil'] * self._bausparsumme) )):  # ...number of simulated days exceeds max\n            \n            # go to next day\n            self._day += 1\n            self._current_date = self._date_start + timedelta(days = self._day)\n            temp_delta += 1\n            \n            # calculate the day\n            self._caccount, account_interest, loan_interest, payed, entgelt = self.zwischen_simulate_day()\n            \n            # store interest for later calculations\n            self._sum_interest += account_interest\n            self._sum_loan_interest += loan_interest\n            \n            # if paydate is there, add the summed interest to the account\n            if self.interest_time():\n                self.zwischen_exec_interest_time()\n            \n            self._cdarlehen = self._bausparsumme - self._caccount\n            \n            # tracking for reports\n            self.zwischen_track_data(account_interest, loan_interest, payed, entgelt)\n            \n            # make a report\n            if self.report_time(self._current_date):\n                self.zwischen_make_report()\n                \n        # when the while loop ended because the points are above the limit, then we can\n        # switch to the next phase\n        if (self._punkte >= self._tarif['C_POINT_LIMIT']):\n            self.get_credit()            \n            \n        # if simulation time is not over yet, continue with simulating the loan_phase\n        if ((self._current_date < date_stop) and \n            (temp_delta < delta.days) and\n            ((self._current_date - self._date_start).days < C_max_time)):\n            self.loan_phase(date_stop, delta, last_report)\n        else:\n            # create report at the end of the simulation\n            if last_report:\n                # as the simulation might not end at the end of the year,\n                # we need to apply exec_interest_time() one last time\n                self.exec_interest_time()\n                self.zwischen_make_report()\n            \n    def zwischen_simulate_day(self):\n        days_per_year = self.get_days_per_year()\n        \n        new_account = self._caccount\n        entgelt = 0\n        \n        if (self._current_date.day == 1) and (self._current_date.month == 1):\n            entgelt = self._tarif['entgelt'] * 100\n            new_account -= entgelt\n        \n        payed = self.get_payments()            \n        new_account = int(new_account + payed)            \n        self._punkte += (payed / 100) * self._tarif['C_POINT_PER_EUR']\n        \n        account_interest = new_account * (self._tarif['guthabenzins'] / days_per_year)\n        loan_interest = self._bausparsumme * (self._tarif['darlehenszins'] / days_per_year)\n        self._punkte += self._tarif['C_POINT_PER_DAY']\n            \n        return new_account, account_interest, loan_interest, payed, entgelt                 \n        \n    def saving_track_data(self, interest, payed, entgelt):\n        \"\"\" tracks data during saving phase \"\"\"\n        self._record['account_interest'] += interest\n        self._record['payments'] += payed\n        self._record['entgelt'] += entgelt\n\n    def saving_make_report(self):\n        self._report.append(date = self._current_date,\n                            account = self._caccount / 100,\n                            account_interest = self._record['account_interest'] / 100,\n                            payments = self._record['payments'] / 100,\n                            entgelt = self._record['entgelt'] / 100,\n                            points = self._punkte\n                            )\n        \n        # set everything to zero\n        self._record = dict.fromkeys(self._record, 0)\n\n    def saving_phase(self, date_stop = None, delta = None, last_report = True):\n        temp_delta = 0\n        while ((self._current_date < date_stop) and     # ...stop-date is reached\n            (temp_delta < delta.days) and               # and delta has not been exeeded\n            ((self._current_date - self._date_start).days < C_max_time)):  # ...number of simulated days exceeds max\n            \n            # go to next day\n            self._day += 1\n            self._current_date = self._date_start + timedelta(days = self._day)\n            temp_delta += 1\n            \n            # calculate the day\n            self._caccount, interest, payed, entgelt = self.saving_simulate_day()\n            \n            # store interest for later calculations\n            self._sum_interest += interest\n            \n            # if paydate is there, add the summed interest to the account\n            if self.interest_time():\n                self.exec_interest_time()\n            \n            # tracking for reports\n            self.saving_track_data(interest, payed, entgelt)\n            \n            # make a report\n            if self.report_time(self._current_date):\n                self.saving_make_report()\n    \n        # create report at the end of the simulation\n        if last_report:\n            # as the simulation might not end at the end of the year,\n            # we need to apply exec_interest_time() one last time\n            self.exec_interest_time()\n            self.saving_make_report()\n\n    def saving_simulate_day(self):\n        days_per_year = self.get_days_per_year()\n        \n        new_account = self._caccount\n        entgelt = 0\n        \n        if (self._current_date.day == 1) and (self._current_date.month == 1):\n            entgelt = self._tarif['entgelt'] * 100\n            new_account -= entgelt\n                \n        payed = self.get_payments()\n        new_account = int(new_account + payed)\n        self._punkte += (payed / 100) * self._tarif['C_POINT_PER_EUR']\n        \n        interest = new_account * (self._tarif['guthabenzins'] / days_per_year)\n        self._punkte += self._tarif['C_POINT_PER_DAY']\n            \n        return new_account, interest, payed, entgelt"
  },
  {
    "path": "financial_life/reports/__init__.py",
    "content": "import platform\n\nsl = '/'\nif platform.system() == 'Windows':\n    sl = '\\\\'"
  },
  {
    "path": "financial_life/reports/excel.py",
    "content": "'''\nCreated on 29.05.2017\n\n@author: martin\n'''\n# standard libraries\nimport os\n\n# third-party libraries\nimport pandas as pd\n\n# own libraries\nfrom financial_life.reports import sl\n\ndef report(simulation, filename='report.xls'):\n    \"\"\" This function generates a report as an excel sheet.\n    \n    simulation      the simualation that should be exported to excel\n    filename        filename of the excel file\n    \"\"\"\n    \n    writer = pd.ExcelWriter(filename)\n    for account in simulation.accounts:\n        df = account.report.as_df()\n        df.to_excel(writer, sheet_name=account.name)\n    writer.save()"
  },
  {
    "path": "financial_life/reports/html.py",
    "content": "'''\nCreated on 03.10.2016\n\n@author: martin\n'''\n# standard libraries\nimport os\nimport imp\nimport platform\n\n# third-party libraries\nfrom  jinja2 import Template\n\n# own libraries\nfrom financial_life.reports import sl\n\npath_template = '..{sl}templates{sl}html'.format(sl=sl)\n\n\ndef report(simulation, style = 'standard', output_dir = 'report'):\n    \"\"\" This is a generic report function that renders html-templates\n    defined by the style-argument. \n    \n    'style' refers to a template-folder in '../templates/html'. A\n    file render.py must be included in the template-folder with a method\n    'render(simulation, output_dir)' that does the job. New html-templates\n    can be easily added by creating new subfolders in '../templates/html/'\n    with html files and a render.py\n    \"\"\"    \n    cwd = os.path.dirname(os.path.realpath(__file__))\n    template_folder = cwd + sl + path_template + sl + style + sl\n    \n    render_module = imp.load_source('render', template_folder + 'render.py')\n    render_module.render(simulation, output_dir)"
  },
  {
    "path": "financial_life/tax/__init__.py",
    "content": ""
  },
  {
    "path": "financial_life/tax/germany/__init__.py",
    "content": "\"\"\" help functions for german tax declaration \"\"\"\n\ndef tax_to_pay(year, *args, **kwargs):\n    \"\"\" generic functions to call the tax-function for any year. \n    This functions simply calls the year-dependent function with \n    the given parameters, but this function don't really care about\n    the content of the other arguments \"\"\"\n    return tax_functions[year](*args, **kwargs)\n\ndef tax_to_pay_2016(tax_relevant_money, splitting = False):\n    \"\"\" calculates the tax for year 2016 \n    \n    Returns the tax and the percentage of the tax \"\"\"\n    if tax_relevant_money <= 0:\n        return 0, 0\n    \n    if splitting:\n        trm = tax_relevant_money / 2.\n    else:\n        trm = tax_relevant_money\n    \n    if trm > 250730:\n        tax = trm * 0.45 - 15783.19\n    elif trm > 52881:\n        tax = trm * 0.42-8261.29\n    elif trm > 13469:\n        tax = (trm-13469)*((trm-13469)*0.0000022874+0.2397)+948.68\n    elif trm > 8472:\n        tax = (trm-8472)*((trm-8472)*0.000009976+0.14)\n    else: \n        tax = 0\n    \n    if splitting:\n        return tax*2, ((tax*2) / tax_relevant_money)\n    else:\n        return float(int(tax*100)/100), (tax / tax_relevant_money)\n\ntax_functions = {2016: tax_to_pay_2016}"
  },
  {
    "path": "financial_life/templates/__init__.py",
    "content": ""
  },
  {
    "path": "financial_life/templates/html/__init__.py",
    "content": ""
  },
  {
    "path": "financial_life/templates/html/standard/__init__.py",
    "content": ""
  },
  {
    "path": "financial_life/templates/html/standard/account_details.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <title>Account Detail: {{account_name}}</title>\n    <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\" integrity=\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u\" crossorigin=\"anonymous\">\n</head>\n<body>\n<h1>{{account_name}}</h1>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_wealth}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_debts}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_io_money}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_debtpayment}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_win_cum}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_cost_cum}}\">\n</div>\n<p><a href=\"{{backlink}}\">back to overview</a></p>\n{% for table in tables %}\n\t<div class=\"col-md-12\">\n\t\t<div class=\"row\">\n\t\t<h3>{{ table.category }}</h3>\n\t\t\t<table class=\"table\">\n\t\t\t\t<tr>\n\t\t\t\t\t{% for head in table.data.header %}\n\t\t\t\t\t<th>{{head}}</th>\n\t\t\t\t\t{% endfor %}\n\t\t\t\t</tr>\n\t\t\t\t{% for row in table.data.rows %}\n\t\t\t\t<tr>\n\t\t\t\t\t{% for item in row %}\n\t\t\t\t\t<td>{{item}}</td>\n\t\t\t\t\t{% endfor %}\t\n\t\t\t\t</tr>\n\t\t\t\t{% endfor %}\n\t\t\t</table>\n\t\t</div>\t\n\t</div>\n{% endfor %}\t\n</body>\n</html>"
  },
  {
    "path": "financial_life/templates/html/standard/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <title>Finanzuebersicht</title>\n    <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\" integrity=\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u\" crossorigin=\"anonymous\">\n</head>\n<body>\n\n<h2>{{title}}</h2>\n<p>{{date}}</p>\n\n\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_wealth}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_debts}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_io_money}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_debtpayment}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_win_cum}}\">\n</div>\n<div class=\"col-md-6\">\n<img alt=\"\" width=\"100%\" src=\"{{img_cost_cum}}\">\n</div>\n\n\n<div class=\"col-md-8\">\n\t<h3>Accounts</h3>\n\t<table class=\"table\">\n\t  <tr>\n\t  \t  <th>No.</th>\n\t\t  <th>name</th>\n\t\t  <th>type</th>\n\t\t  <th>start value</th>\n\t\t  <th>start date</th>\n\t  </tr>\n\n\t  {% for a in accounts %}\n\t  <tr>\n\t  \t  <td>{{ a.index }}</td>\n\t\t  <td><a href=\"{{a.link}}\">{{ a.name }}</a></td>\n\t\t  <td>{{ a.type }}</td>\n\t\t  <td>{{ \"{:,.2f}\".format(a.start_value) }}</td>\n\t\t  <td>{{ a.start_date }}</td>\n\t  </tr>\n\t  {% endfor %} \n\t</table>\n</div>\n<div class=\"col-md-8\">\n\t<h3>Regular Payments</h3>\n\t<table class=\"table\">\n\t  <tr>\n\t\t  <th>from</th>\n\t\t  <th>to</th>\n\t\t  <th>interval</th>\n\t\t  <th>day</th>\n\t\t  <th>start date</th>\n\t\t  <th>stop date</th>\n\t\t  <th>payment</th>\n\t\t  <th>name</th>\n\t\t  <th>fixed</th>\n\t  </tr>\n\n\t  {% for r in payments_regular %}\n\t  <tr>\n\t\t  <td>{{ r.from_acc }}</td>\n\t\t  <td>{{ r.to_acc }}</td>\n\t\t  <td>{{ r.interval }}</td>\n\t\t  <td>{{ r.day }}</td>\n\t\t  <td>{{ r.date_start }}</td>\n\t\t  <td>{{ r.date_stop }}</td>\n\t\t  <td>{{ r.payment }}</td>\n\t\t  <td>{{ r.name }}</td>\n\t\t  <td>{{ r.fixed }}</td>\n\t  </tr>\n\t  {% endfor %} \n\t</table>\n</div>\n<div class=\"col-md-8\">\n\t<h3>Unique Payments</h3>\n\t<table class=\"table\">\n\t  <tr>\n\t\t  <th>from</th>\n\t\t  <th>to</th>\n\t\t  <th>date</th>\n\t\t  <th>payment</th>\n\t\t  <th>name</th>\n\t\t  <th>fixed</th>\n\t  </tr>\n\t  {% for u in payments_unique %}\n\t  <tr>\n\t\t  <td>{{ u.from_acc }}</td>\n\t\t  <td>{{ u.to_acc }}</td>\n\t\t  <td>{{ u.date }}</td>\n\t\t  <td>{{ u.payment }}</td>\n\t\t  <td>{{ u.name }}</td>\n\t\t  <td>{{ u.fixed }}</td>\n\t  </tr>\n\t  {% endfor %}\n\t</table>\n</div>\n\n</body>\n</html>"
  },
  {
    "path": "financial_life/templates/html/standard/render.py",
    "content": "'''\nCreated on 03.10.2016\n\n@author: martin\n'''\n\n# standard libraries\nimport os\nfrom datetime import datetime\n\n# third-party libraries\nfrom  jinja2 import Template\n\n# own libraries\nfrom financial_life.financing import plotting as plt\nfrom financial_life.reports import sl\n\npath_img = 'img'\npath_accounts = 'accounts'\n\ndef render(simulation, output_dir = 'report'):\n    print(\"Calling render function\")\n    template_folder = os.path.dirname(os.path.realpath(__file__))\n    img_folder = output_dir + sl + path_img + sl\n    accounts_folder = path_accounts + sl\n    \n    print('Template Folder: %s' % template_folder)\n    # makedirs creates also all intermediate folders, therefore, we don't need\n    # to create result_folder explicitely\n    if not os.path.exists(img_folder):\n        os.makedirs(img_folder)\n    if not os.path.exists(output_dir + sl + accounts_folder):\n        os.makedirs(output_dir + sl + accounts_folder)\n            \n    img_data = plt.summary_img(*simulation.reports('yearly'), target = img_folder)\n    data = {}\n    data['title'] = 'Kalkulation: ' + output_dir\n    data['date'] = datetime.now().strftime(\"%d.%m.%Y - %H:%M:%S\")\n    \n    data.update(img_data)\n    data.update(simulation.get_payments_unique_json())\n    data.update(simulation.get_payments_regular_json())\n    \n    accounts = simulation.get_accounts_json()\n    links = render_accounts(simulation, template_folder, output_dir, accounts_folder)\n    \n    # get_accounts_json and render_accounts iterate through simulation.account\n    # therefore, the order in both is equal and we can add the link to the \n    # accounts json. this is not the safest way but I did not wanted to put\n    # the get_accounts_json routine into this render-function \n    for a, l in zip(accounts['accounts'], links):\n        a['link'] = l\n    data.update(accounts)\n    \n    index_file = \"index.html\"\n    with open(template_folder + sl + index_file, 'r') as f:\n        content = f.read()\n        t = Template(content)\n        with open(output_dir + sl + index_file, 'w') as o:\n            o.write(t.render(**data))\n            \ndef render_accounts(simulation, template_folder, output_dir = 'report', accounts_folder = path_accounts):\n    \"\"\" Renders for each account a detailed page with all account-specific\n    data \"\"\"\n    accounts = simulation.accounts\n    links = []\n    \n    # image folder for the account related pictures\n    img_folder = output_dir + sl + accounts_folder + 'img' + sl\n    if not os.path.exists(img_folder):\n        os.makedirs(img_folder)    \n    \n    for (i, a) in enumerate(accounts):\n        account_name = a.name\n        account_name.replace(' ', '_')\n        prefix = 'account_details_%03i_%s' % (i, account_name)\n        account_link = accounts_folder + prefix + '.html'\n        print('Render %s' % account_link)\n        links.append(account_link)\n        \n        data = {}\n        data['account_name'] = a.name\n        data['tables'] = a.get_report_json(interval='all')\n        data['backlink'] = '..' + sl + 'index.html'\n        img_data = plt.summary_img(a.report.yearly(), target = img_folder, prefix = prefix)\n        data.update(img_data) \n        \n        template_file = 'account_details.html'\n        with open(template_folder + sl + template_file, 'r') as f:\n            content = f.read()\n            t = Template(content)\n            with open(output_dir + sl + account_link, 'w') as o:\n                o.write(t.render(**data))\n    return links"
  },
  {
    "path": "financial_life/test_general.py",
    "content": "'''\nCreated on 12.12.2016\n\n@author: martin\n'''\n# standard libraries\nfrom datetime import timedelta, datetime\nimport os\nimport unittest\n\n# own libraries\nfrom financial_life.financing import accounts as a\nfrom financial_life.reports import html\n\n\nclass Test(unittest.TestCase):\n\n\n    def setUp(self):\n        \"\"\" Mostly taken from examples/simple_example.py \"\"\"\n        account = a.Bank_Account(amount = 1000, interest = 0.001, name = 'Main account', date=datetime(2016,9, 1))\n        savings = a.Bank_Account(amount = 5000, interest = 0.013, name = 'Savings', date=datetime(2016,9, 1))\n        loan = a.Loan(amount = 100000, interest = 0.01, name = 'House Credit', date=datetime(2016,9, 1))\n    \n        simulation = a.Simulation(account, savings, loan, name = 'Testsimulation', date=datetime(2016,9, 1))\n        simulation.add_regular(from_acc = 'Income',\n                               to_acc = account,\n                               payment = 2000,\n                               interval = 'monthly',\n                               date_start = datetime(2016,9,15),\n                               day = 15,\n                               name = 'Income')\n    \n        simulation.add_regular(from_acc = account,\n                               to_acc = savings,\n                               payment = 500,\n                               interval = 'monthly',\n                               date_start = datetime(2016,9,30),\n                               day = 30,\n                               name = 'Savings')\n    \n        simulation.add_regular(from_acc = account,\n                               to_acc= loan,\n                               payment = 1000,\n                               interval = 'monthly',\n                               date_start = datetime(2016,9,15),\n                               day = 15,\n                               name = 'Debts',\n                               fixed = False,\n                               date_stop = lambda cdate: loan.is_finished())\n    \n        simulation.add_regular(from_acc = account,\n                               to_acc= loan,\n                               payment = lambda : min(8000, max(0,account.get_account()-4000)),\n                               interval = 'yearly',\n                               date_start = datetime(2016,11,20),\n                               day = 20,\n                               name = 'Debts',\n                               fixed = False,\n                               date_stop = lambda cdate: loan.is_finished())\n    \n        simulation.simulate(delta=timedelta(days=2000))\n    \n        self.simulation = simulation\n        self.account = account\n        self.loan = loan\n\n\n    def tearDown(self):\n        pass\n\n\n    def testGeneral(self):\n        interests = sum(self.account.report.yearly().interest)+sum(self.loan.report.yearly().interest)\n        self.assertTrue(abs(interests - (-3086.08)) < 0.00001)\n\n\nif __name__ == \"__main__\":\n    #import sys;sys.argv = ['', 'Test.testName']\n    unittest.main()"
  },
  {
    "path": "requirements.txt",
    "content": "Jinja2>=2.7.2\nmatplotlib>=1.5.3\nnumpy>=1.11.2\npandas>=0.25.3\ntabulate>=0.7.5\nxlwt"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Tue Nov  8 21:26:07 2016\n\n@author: martin\n\"\"\"\ntry:\n    from setuptools import setup\n    have_setuptools = True\nexcept ImportError:\n    from distutils.core import setup\n    have_setuptools = False\n\nskw = dict(\n    name='financial_life',\n    version='0.9.4',\n    description='A framework for analysing financial products in personalized contexts',\n    author='Martin Pyka',\n    author_email='martin.pyka@gmail.com',\n    maintainer='Martin Pyka',\n    maintainer_email='martin.pyka@gmail.com',\n    url='https://github.com/MartinPyka/financial_life',\n    keywords=[\"finance\", \"analysis\", \"simulation\", \"loan\", \"bank\"],\n    license=\"Apache License, Version 2.0\",\n    packages=['financial_life',\n              'financial_life.calendar_help',\n      \t \t  'financial_life.examples',\n      \t \t  'financial_life.financing',\n              'financial_life.products.germany.lbs',\n              'financial_life.reports',\n              'financial_life.tax.germany',\n              'financial_life.templates.html.standard',\n    ],\n    package_data={'financial_life': ['templates/html/standard/*.html']}\n)\n\nif have_setuptools is True:\n\tskw['install_requires'] = [\n\t\t'Jinja2>=2.7.2,<3',\n\t\t'matplotlib>=1.3.1,<2',\n\t\t'numpy>=1.8.1,<2',\n\t\t'pandas>=0.18.1,<1',\n\t\t'tabulate>=0.7.5,<1',\n        'xlwt>=1.2.0',\n\t]\n\nsetup(**skw)\n"
  },
  {
    "path": "unittests.sh",
    "content": "#!/bin/bash\n\n# run all unittests in this package\n python3 -m unittest discover -v\n"
  }
]