Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 307f74b1d9 | |||
| 0c29393f3b | |||
| af3726cb91 | |||
| 0d40fd92f0 | |||
| 0f55d84355 | |||
| eafa889827 | |||
| 60cd880c2e | |||
| ea8de69863 | |||
| 11ef26e878 | |||
| 2770ebf958 | |||
| 536ca56c24 |
@@ -6,6 +6,17 @@ Investbrain is a smart open-source investment tracker that helps you manage, tra
|
||||
|
||||
<p align="center"><a href="https://investbra.in" target="_blank"><img src="https://raw.githubusercontent.com/investbrainapp/investbrain/main/screenshot.png" width="100%" alt="Investbrain Screenshot"></a></p>
|
||||
|
||||
## Table of contents
|
||||
- [Under the hood](#under-the-hood)
|
||||
- [Install (self hosting)](#self-hosting)
|
||||
- [Chat with your holdings](#chat-with-your-holdings)
|
||||
- [Market data providers](#market-data-providers)
|
||||
- [Configuration](#configuration)
|
||||
- [Updating](#updating)
|
||||
- [Command line utilities](#command-line-utilities)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Testing](#testing)
|
||||
|
||||
## Under the hood
|
||||
|
||||
Investbrain is a Laravel PHP web application that leverages Livewire and Tailwind for its frontend. Most databases should work, including MySQL and SQLite. Out of the box, we feature three market data providers: [Yahoo Finance](https://finance.yahoo.com/), [Finnhub](https://finnhub.io/pricing-stock-api-market-data), and [Alpha Vantage](https://www.alphavantage.co/support/). But we also offer an extensible market data provider interface for intrepid developers to create their own! We also offer an integration with OpenAI's LLMs for our ["chat with your holdings"](#chat-with-your-holdings) capability. Finally, of course we have robust support for i18n, a11y, and dark mode.
|
||||
@@ -44,7 +55,7 @@ Congrats! You've just installed Investbrain!
|
||||
|
||||
Investbrain offers an AI powered chat assistant that is grounded on *your* investments. This enables you to use AI as a thought partner when making investment decisions.
|
||||
|
||||
When self-hosting, you can enable the chat assstant by configuring your OpenAI Secret Key and Organization ID in your [.env](https://github.com/investbrainapp/investbrain/blob/main/.env.example) file. Navigate to OpenAI to [create your keys](https://platform.openai.com/api-keys).
|
||||
When self-hosting, you can enable the chat assistant by configuring your OpenAI Secret Key and Organization ID in your [.env](https://github.com/investbrainapp/investbrain/blob/main/.env.example) file. Navigate to OpenAI to [create your keys](https://platform.openai.com/api-keys).
|
||||
|
||||
Always keep in mind the limitations of large language models. When in doubt, consult a licensed investment advisor.
|
||||
|
||||
@@ -159,6 +170,31 @@ Just to be safe, we recommend backing up your portfolios before using these comm
|
||||
| sync:daily-change | Re-calculates daily snapshots of your portfolio's daily performance. Useful to fill in gaps in your portfolio charts. (Note: this is an extremely resource intensive query.) |
|
||||
| sync:holdings | Re-calculates performance of holdings with related transactions (i.e. dividends, realized gains, etc). |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you are facing issues with Investbrain, it can be handy to monitor the application's logs:
|
||||
|
||||
```bash
|
||||
docker exec -it investbrain-app cat storage/logs/laravel.log
|
||||
```
|
||||
or you can live monitor logs using `tail`:
|
||||
|
||||
```bash
|
||||
docker exec -it investbrain-app tail -f storage/logs/laravel.log
|
||||
```
|
||||
|
||||
### Common issues
|
||||
|
||||
<details>
|
||||
|
||||
**<summary>Market data not refreshing on fresh install</summary>**
|
||||
|
||||
If you're unable to refresh market data out of the box (i.e. your market data provider is set to Yahoo), there is a chance Yahoo is being blocked by a firewall or adblocker. Pihole is known to block `fc.yahoo.com` which is the domain used to query Yahoo.
|
||||
|
||||
Once you whitelist `fc.yahoo.com` in pihole, your market data should begin populating!
|
||||
|
||||
</details>
|
||||
|
||||
## Testing
|
||||
|
||||
Investbrain has a robus PHPUnit test suite that creates an in-memory SQLite database and runs any queued jobs synchronously using Laravel's array driver. You can run the entire Investbrain test suite from within the Docker container by running:
|
||||
|
||||
@@ -50,10 +50,8 @@ class Dividend extends Model
|
||||
/**
|
||||
* Grab new dividend data
|
||||
*
|
||||
* @param string $symbol
|
||||
* @return void
|
||||
*/
|
||||
public static function refreshDividendData(string $symbol)
|
||||
public static function refreshDividendData(string $symbol): void
|
||||
{
|
||||
$dividends_meta = self::where(['symbol' => $symbol])
|
||||
->selectRaw('COUNT(symbol) as total_dividends')
|
||||
@@ -68,7 +66,13 @@ class Dividend extends Model
|
||||
// nope, refresh forward looking only
|
||||
if ( $dividends_meta->total_dividends ) {
|
||||
|
||||
$start_date = $dividends_meta->last_date->addHours(48);
|
||||
$start_date = $dividends_meta->last_date->addHours(24);
|
||||
}
|
||||
|
||||
// skip refresh if there's already recent data
|
||||
if ($start_date >= $end_date) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// get some data
|
||||
@@ -99,8 +103,6 @@ class Dividend extends Model
|
||||
$market_data->last_dividend_amount = $dividend_data->sortByDesc('date')->first()['dividend_amount'];
|
||||
$market_data->save();
|
||||
}
|
||||
|
||||
return $dividend_data;
|
||||
}
|
||||
|
||||
public static function syncHoldings(string $symbol): void
|
||||
|
||||
@@ -125,7 +125,7 @@ class Transaction extends Model
|
||||
|
||||
public function scopeBeforeDate($query, $date)
|
||||
{
|
||||
return $query->whereDate('date', '<', $date);
|
||||
return $query->whereDate('date', '<=', $date);
|
||||
}
|
||||
|
||||
public function scopeMyTransactions()
|
||||
|
||||
@@ -50,7 +50,7 @@ class QuantityValidationRule implements ValidationRule
|
||||
|
||||
$maxQuantity = $purchase_qty - $sales_qty;
|
||||
|
||||
if ($value > $maxQuantity) {
|
||||
if (round($value, 3) > round($maxQuantity, 3)) {
|
||||
$fail(__('The quantity must not be greater than the available quantity.'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ new class extends Component {
|
||||
'symbol' => ['required', 'string', new SymbolValidationRule],
|
||||
'transaction_type' => 'required|string|in:BUY,SELL',
|
||||
'portfolio_id' => 'required|exists:portfolios,id',
|
||||
'date' => 'required|date_format:Y-m-d',
|
||||
'date' => ['required', 'date_format:Y-m-d', 'before_or_equal:' . now()->format('Y-m-d')],
|
||||
'quantity' => [
|
||||
'required',
|
||||
'numeric',
|
||||
@@ -83,14 +83,14 @@ new class extends Component {
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->authorize('fullAccess', $this->portfolio);
|
||||
|
||||
$validated = $this->validate();
|
||||
|
||||
if (!isset($this->portfolio)) {
|
||||
$this->portfolio = Portfolio::find($this->portfolio_id);
|
||||
}
|
||||
|
||||
$this->authorize('fullAccess', $this->portfolio);
|
||||
|
||||
$validated = $this->validate();
|
||||
|
||||
$transaction = $this->portfolio->transactions()->create($validated);
|
||||
$transaction->save();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user