Coder Social home page Coder Social logo

madeinpierre / finalynx Goto Github PK

View Code? Open in Web Editor NEW
58.0 3.0 11.0 5.05 MB

A minimalistic companion (CLI & web) to organize your investment portfolio, simulate its future, and reach your life goals.

Home Page: https://finalynx.readthedocs.io

License: GNU General Public License v3.0

Python 100.00%
investing portfolio simulation finary command-line optimization python dashboard web invest

finalynx's People

Contributors

adamou02 avatar gcoue avatar madeinpierre avatar nmathey avatar sebfar9172 avatar strawbang avatar varal7 avatar yovanoc avatar zulufoxtrot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

finalynx's Issues

Ability to customize the output format

It would be nice to have more flexibility in the way the tree is rendered, currently the default would correspond to:

python config.py --format="console"

Colors could still follow the rich format just like in the example above. Since targets have adaptive colors and symbols, we could create format variants, like target for the default behavior which is an alias to:

[target_color]target_symbol amount_aligned currency_symbol[/]

A dedicated abstract class could be overridden by subclasses like Node and Target for their own aliases:

class Node(Render):
    _render_aliases: Dict[str, str] = {
        "console": "target [grey](prehint) [white]name [grey]- hint[/]"
        ...
    }

    _render_agents: Dict[str, Callable[str])] = {
        "target": self._render_target,
        ...
    }

    ...

    # Defined in Render
    def render(self, format: str = "console"):
        ... # replace all aliases recursively, then call renderers

    def _render_target(self, format: str = "target"):
        return self.target.render(format)

This would be useful to render the tree for other outputs such as PDFs and the web dashboard.

Implementation

  • Implement the base Render class
  • Change the usage
  • Use the command-line format option and send it to the tree function
  • Change all subclasses to follow this render strategy
  • Pass any argument to the agents (e.g. hide amounts)
  • Write documentation about format guidelines

How to check if the PyPI package is complete?

I'd like to write tests in the CI/CD pipeline to make sure a new user will run pip install finalynx and have everything working.

This would imply running the demo.py file in CI/CD (and maybe full tests in the future), hence also checking that the fetching process to Finary also works.

However, I don't know how to test it without having to log in with my personal account in the CI/CD. This doesn't seem 'clean' for a public repo.

Any ideas on how to properly check if the package is ready to deploy before publishing to PyPI?

Fetching issue

I don't know since when this error happen but I can't fetch cryptos (I think) anymore

image

Values are wrong for non-EUR holdings

Problem: The value returned by the Finary API in the field "current_value" corresponds to the currency under ["fiat"]["symbol"] in the same object.

If a user has non-EUR lines, the values displayed will be wrong.

Proposed solution:

Use display_current_value instead of current_value.

One option is to query the user's preferences at /users/me, i.e. ff.get_user_me, under 'ui_configuration.display_currency'.

For example:

python -m finary_api me | jq '.result["ui_configuration"]["display_currency"]'

{
  "id": 1,
  "name": "Euro",
  "code": "EUR",
  "symbol": "€",
  "correlation_id": "6246d83d2a89de8c1452c622",
  "logo_url": "https://cdn.finary.com/000terminal/currencies/logos/Euro EUR.png"
}

Then, instead of using current_value, use display_current_value.

For example, line 222 of fetch_finary.py becomes

        _process("Checkings", ff.get_checking_accounts, lambda e: (e["name"], e["id"], e["fiats"][0]["display_current_value"]))

And _render_currency uses the currency symbol from the user preferences.

Alternatives:

Keep track of the tuple (amount, current) for each line and apply a conversion rate.

Currency display for targets

Currently the render code for targets hardcodes .

Could let the user define which currency to use (or acquire it directly from corresponding Node).

Crypto Missing

Il n'y a pas du tout la partie crypto dans les keys proposées, est-ce normal ?

Key override issue

Il serait bien de séparer les key par supports. car j’ai un CTO et un PEA sur Boursorama par exemple et je ne peut pas changer le nom des champs « liquidités » du coup dans le code actuel il y en a qu’un seul qui ressort car ca doit écraser l’autre

Enveloppe avec balancing

FEATURE REQUEST

A moins que je me trompe il n'y a pas moyen de définir des target a l'intérieur meme des enveloppes.
J'aimerais pouvoir à la fois utiliser target et perf.

Enable disabling the "render_delta" for targets

It seems like Finalynx allows customizing which info to display the --format argument. However, even with the barebones python demo.py --format "[name]", I can't seem to deactivate render_delta for targets.

My current solution is to do

class MyFolder(Folder):
    def _render_delta(self, *args, **kwargs):
        return ""

And then use MyFolder whenever.

Works ok enough I guess

Utilisation de la clé d'une ligne ne fonctionne pas

J'ai plusieurs lignes d'une même action dans plusieurs supports. J'ai compris qu'il fallait utiliser la clé pour les distinguer mais cela fonctionne pas. Si je met le libellé j'ai la somme de toutes les lignes et si je met la clé cela ne me remonte rien.

Line( "toto", key="24554")

Add asset class, envelope, expected return, and investment time

To generate analytics, it would be nice to specify several attributes on each Line, including :

  • Asset class with an enumeration AssetClass.STOCKS
  • Expected return (yearly returns for now, we'll see about simulation scenarios later)
  • Custom currency symbol
  • Recommended investment time
  • Define envelopes before the portfolio. Finalynx could provide pre-made common envelopes like PEA, PER, AV, ... and add availability information (we'll see about taxes later):
Envelope("PEA", time_blocked=5)
Envelope("AV",  time_blocked=0, time_taxed=8, unblock_events=["retirement, buy_home"])

In the end, a Line definition could look like this:

pea = Envelope("PEA", date_created=date(2023, 03, 21), ...)
return_stocks = Return(min=-2, mid=6, max=10, time_recommended=timedelta(years=8))

portfolio = Portfolio(children=[
    Line("ETF World", key="...", envelope=pea, class=AssetClass.STOCK, return=return_stocks)
])

Other ideas are welcome!

Add a mean yearly performance for the entire portfolio

Add an expected performance field to each line, and recursively aggregate them all the way up to the root Portfolio.

Then, show the expected global performance in the Panel title. This can also become an output format option called perf just like deltas.

Using JSON format file for portfolio definition instead of python code

Instead of using python code file (i.e. demo.py like) to define portfolio projects/goals, use JSON type file using similar folders/buckets structure (sample proposal):

{
"goal_name": "MyGoal",
"targets": {
      "targetMin": 2000,
      "targetRatio":10
 },
"products": {
       "product_name": {
             "name": "my_product_name",
             "key": "name_in_finary"
        }
 }
}

Support for loans and credit_accounts

Those correspond to

https://api.finary.com/users/me/views/loans

and

https://api.finary.com/users/me/views/credit_accounts

The latter is not yet supported by finary_api, cf lasconic/finary_uapi#68

It was not super obvious what structure finary is using to represent the data.

Ability to customize the delta column on the right with other information

For now, there is a right column that shows. It is possible to hide it using --hide-deltas.

Following #60, it would be nice to create a new --sidecar-format="..." option to render any information on the right side (targets, goals, performance, deltas, amount, ideal amount, ...)

Any other naming ideas than sidecar are welcome 😅

AttributeError: 'Folder' object has no attribute 'copy'

I'm trying to run a very basic sample but I'm getting this error.

Version of finalynx: 1.0.1 (installed via pip)
Version of Python: 3.9

My code:

from finalynx import Portfolio, Assistant, Folder, Line

portfolio = Portfolio(
    Folder('Short term',
           children=[
               Line("Some fund"),
           ]
           )
)
Assistant(portfolio).run()

The error:

[16:38:19] Done fetching Finary data.                        finary_fetch.py:125
╭───────────────────── Traceback (most recent call last) ──────────────────────╮
│                                                                              │
│ /Users/zulufoxtrot/Library/CloudStorage/[email protected]/My         │
│ Drive/docs/projets/dev/python/finary_assistant/main.py:10 in <module>        │
│                                                                              │
│    7 │   │      ]                                                            │
│    8 │   │      )                                                            │
│    9 )                                                                       │
│ ❱ 10 Assistant(portfolio).run()                                              │
│ /Users/zulufoxtrot/.local/share/virtualenvs/finary_assistant/lib/python3.9/site-pa │
│ ckages/finalynx/assistant.py:105 in run                                      │
│                                                                              │
│   102 │   │   ]                                                              │
│   103 │   │                                                                  │
│   104 │   │   # Display the entire portfolio and associated recommendations  │
│ ❱ 105 │   │   console.print("\n", Columns(panels, padding=(2, 10)))          │
│   106                                                                        │
│                                                                              │
│ /Users/zulufoxtrot/.local/share/virtualenvs/finary_assistant/lib/python3.9/site-pa │
│ ckages/rich/console.py:1634 in print                                         │
│                                                                              │
│   1631 │   │   │   render = self.render                                      │
│   1632 │   │   │   if style is None:                                         │
│   1633 │   │   │   │   for renderable in renderables:                        │
│ ❱ 1634 │   │   │   │   │   extend(render(renderable, render_options))        │
│   1635 │   │   │   else:                                                     │
│   1636 │   │   │   │   for renderable in renderables:                        │
│   1637 │   │   │   │   │   extend(                                           │
│                                                                              │
│ /Users/zulufoxtrot/.local/share/virtualenvs/finary_assistant/lib/python3.9/site-pa │
│ ckages/rich/console.py:1272 in render                                        │
│                                                                              │
│   1269 │   │   │   )                                                         │
│   1270 │   │   _Segment = Segment                                            │
│   1271 │   │   _options = _options.reset_height()                            │
│ ❱ 1272 │   │   for render_output in iter_render:                             │
│   1273 │   │   │   if isinstance(render_output, _Segment):                   │
│   1274 │   │   │   │   yield render_output                                   │
│   1275 │   │   │   else:                                                     │
│                                                                              │
│ /Users/zulufoxtrot/.local/share/virtualenvs/finary_assistant/lib/python3.9/site-pa │
│ ckages/rich/columns.py:79 in __rich_console__                                │
│                                                                              │
│    76 │   │   column_count = len(renderables)                                │
│    77 │   │                                                                  │
│    78 │   │   get_measurement = Measurement.get                              │
│ ❱  79 │   │   renderable_widths = [                                          │
│    80 │   │   │   get_measurement(console, options, renderable).maximum      │
│    81 │   │   │   for renderable in renderables                              │
│    82 │   │   ]                                                              │
│                                                                              │
│ /Users/zulufoxtrot/.local/share/virtualenvs/finary_assistant/lib/python3.9/site-pa │
│ ckages/rich/columns.py:80 in <listcomp>                                      │
│                                                                              │
│    77 │   │                                                                  │
│    78 │   │   get_measurement = Measurement.get                              │
│    79 │   │   renderable_widths = [                                          │
│ ❱  80 │   │   │   get_measurement(console, options, renderable).maximum      │
│    81 │   │   │   for renderable in renderables                              │
│    82 │   │   ]                                                              │
│    83 │   │   if self.equal:                                                 │
│                                                                              │
│ /Users/zulufoxtrot/.local/share/virtualenvs/finary_assistant/lib/python3.9/site-pa │
│ ckages/rich/measure.py:109 in get                                            │
│                                                                              │
│   106 │   │   │   ] = getattr(renderable, "__rich_measure__", None)          │
│   107 │   │   │   if get_console_width is not None:                          │
│   108 │   │   │   │   render_width = (                                       │
│ ❱ 109 │   │   │   │   │   get_console_width(console, options)                │
│   110 │   │   │   │   │   .normalize()                                       │
│   111 │   │   │   │   │   .with_maximum(_max_width)                          │
│   112 │   │   │   │   )                                                      │
│                                                                              │
│ /Users/zulufoxtrot/.local/share/virtualenvs/finary_assistant/lib/python3.9/site-pa │
│ ckages/rich/panel.py:214 in __rich_measure__                                 │
│                                                                              │
│   211 │   def __rich_measure__(                                              │
│   212 │   │   self, console: "Console", options: "ConsoleOptions"            │
│   213 │   ) -> "Measurement":                                                │
│ ❱ 214 │   │   _title = self._title                                           │
│   215 │   │   _, right, _, left = Padding.unpack(self.padding)               │
│   216 │   │   padding = left + right                                         │
│   217 │   │   renderables = [self.renderable, _title] if _title else [self.r │
│                                                                              │
│ /Users/zulufoxtrot/.local/share/virtualenvs/finary_assistant/lib/python3.9/site-pa │
│ ckages/rich/panel.py:108 in _title                                           │
│                                                                              │
│   105 │   │   │   title_text = (                                             │
│   106 │   │   │   │   Text.from_markup(self.title)                           │
│   107 │   │   │   │   if isinstance(self.title, str)                         │
│ ❱ 108 │   │   │   │   else self.title.copy()                                 │
│   109 │   │   │   )                                                          │
│   110 │   │   │   title_text.end = ""                                        │
│   111 │   │   │   title_text.plain = title_text.plain.replace("\n", " ")     │
╰──────────────────────────────────────────────────────────────────────────────╯
AttributeError: 'Folder' object has no attribute 'copy'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.