Skip to main content

Overview

SpendWisely George tracks your mutual fund portfolio by fetching real-time NAV (Net Asset Value) data from MFapi.in, a free public API for Indian mutual funds. Users manually add holdings (scheme code + units), and the system calculates current portfolio value automatically.

How It Works

1

User adds holdings

Enter scheme code and units in Settings panel
2

Saved to JSON file

Holdings stored in holdings.json on server
3

Fetch latest NAV

Backend calls MFapi.in for each scheme’s current NAV
4

Calculate value

Portfolio value = Σ(units × NAV) for all holdings
5

Display in UI

Total portfolio value shown in dashboard card

Adding Holdings

Frontend UI

Settings panel provides input for scheme code and units:
index.html
<div class="p-4 bg-green-50 rounded-2xl border border-green-100">
    <h3 class="font-bold text-green-900 mb-2">Mutual Fund Holdings</h3>
    
    <div id="mfList" class="space-y-2 mb-2"></div>
    
    <div class="flex gap-2">
        <input type="text" id="mfCode" placeholder="Scheme Code"
            class="flex-1 p-3 bg-white rounded-xl text-sm">
        <input type="number" id="mfUnits" placeholder="Units"
            class="w-20 p-3 bg-white rounded-xl text-sm">
    </div>
    
    <button onclick="addHolding()"
        class="w-full mt-2 bg-green-600 text-white p-3 rounded-xl font-bold text-sm">
        Add Holding
    </button>
    
    <button onclick="saveHoldings()"
        class="w-full mt-2 bg-slate-900 text-white p-3 rounded-xl font-bold text-sm">
        Save & Update
    </button>
</div>

JavaScript Implementation

index.html
let currentHoldings = [];

// Fetch existing holdings from backend
async function fetchHoldings() {
    try {
        const res = await fetch('/api/holdings');
        currentHoldings = await res.json();
        renderHoldings();
    } catch (e) { }
}

// Render holdings list
function renderHoldings() {
    const list = document.getElementById('mfList');
    list.innerHTML = currentHoldings.map((h, i) =>
        `<div class="flex justify-between text-xs bg-white p-2 rounded border">
            <span class="font-mono">${h.scheme_code}</span>
            <span>${h.units} units</span>
            <button onclick="removeHolding(${i})" class="text-red-500">x</button>
        </div>`
    ).join('');
}

// Add new holding to local array
function addHolding() {
    const code = document.getElementById('mfCode').value;
    const units = parseFloat(document.getElementById('mfUnits').value);
    
    if (code && units) {
        currentHoldings.push({ scheme_code: code, units: units });
        renderHoldings();
        document.getElementById('mfCode').value = '';
        document.getElementById('mfUnits').value = '';
    }
}

// Remove holding from array
function removeHolding(i) {
    currentHoldings.splice(i, 1);
    renderHoldings();
}

// Save all holdings to backend
async function saveHoldings() {
    await fetch('/api/holdings', {
        method: 'POST', 
        body: JSON.stringify(currentHoldings),
        headers: { 'Content-Type': 'application/json' }
    });
    alert("Holdings Saved!");
    fetchPortfolio();  // Recalculate portfolio value
}

// Initialize on page load
fetchHoldings();

Backend Storage

Holdings Data Model

server.py
from pydantic import BaseModel
from typing import List
import json
import os

HOLDINGS_FILE = "./holdings.json"

class Holding(BaseModel):
    scheme_code: str
    units: float

def load_holdings():
    if os.path.exists(HOLDINGS_FILE):
        try:
            with open(HOLDINGS_FILE, "r") as f:
                return json.load(f)
        except:
            return []
    return []

def save_holdings(holdings):
    with open(HOLDINGS_FILE, "w") as f:
        json.dump(holdings, f)

API Endpoints

server.py
@app.get("/api/holdings")
def get_holdings_api():
    return load_holdings()

@app.post("/api/holdings")
def set_holdings_api(holdings: List[Holding]):
    data = [{"scheme_code": h.scheme_code, "units": h.units} for h in holdings]
    save_holdings(data)
    return {"status": "updated"}

Portfolio Endpoint

Fetches NAV for each holding and calculates total value:
server.py
import requests

@app.get("/api/portfolio")
def get_portfolio():
    total_value = 0
    portfolio = []
    
    holdings = load_holdings()
    
    for holding in holdings:
        code = holding["scheme_code"]
        units = float(holding["units"])
        
        try:
            # Call MFapi.in for this scheme
            resp = requests.get(f"https://api.mfapi.in/mf/{code}")
            data = resp.json()
            
            if data and "data" in data and len(data["data"]) > 0:
                nav = float(data["data"][0]["nav"])  # Latest NAV
                fund_name = data["meta"]["scheme_name"]
                value = units * nav
                
                portfolio.append({
                    "scheme_code": code,
                    "scheme_name": fund_name,
                    "units": units,
                    "nav": nav,
                    "current_value": round(value, 2)
                })
                total_value += value
        except Exception as e:
            print(f"Error fetching {code}: {e}")
            
    return {
        "portfolio": portfolio,
        "total_value": round(total_value, 2)
    }

MFapi.in Response Format

{
  "meta": {
    "fund_house": "Axis Mutual Fund",
    "scheme_type": "Open Ended Schemes",
    "scheme_category": "Equity Scheme - Flexi Cap",
    "scheme_code": 120503,
    "scheme_name": "Axis Bluechip Fund - Direct Plan - Growth"
  },
  "data": [
    {
      "date": "05-03-2026",
      "nav": "84.5231"
    },
    {
      "date": "04-03-2026",
      "nav": "84.1234"
    }
  ],
  "status": "SUCCESS"
}

Frontend Display

Portfolio Card

index.html
<div class="card p-4 bg-white">
    <p class="text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-1">
        Investments
    </p>
    <h3 id="uiPortfolio" class="text-xl font-black text-slate-800">₹0</h3>
    <p class="text-[10px] text-green-500 font-bold mt-1">MFapi.in Linked</p>
</div>

Fetching Portfolio Value

index.html
async function fetchPortfolio() {
    try {
        const res = await fetch('/api/portfolio');
        const data = await res.json();
        
        if (data.total_value) {
            appData.portfolioValue = data.total_value;
            renderUI();  // Updates the card display
        }
    } catch (e) {
        console.error("Portfolio Fetch Error", e);
    }
}

// Called on page load and after saving holdings
fetchPortfolio();

Total Balance Integration

index.html
function renderUI() {
    const showBal = appData.showBalance || !appData.privacy;
    
    // Total balance includes sheet balance + bank + portfolio
    const totalBal = appData.balance + 
                     (appData.bankBalance || 0) + 
                     (appData.portfolioValue || 0);
    
    const updateDisplay = (id, val) => {
        const el = document.getElementById(id);
        if (showBal && el) 
            el.innerText = `₹${Number(val).toLocaleString('en-IN')}`;
        else if (el) 
            el.innerText = "••••••";
    };
    
    updateDisplay('uiBalance', totalBal);
    updateDisplay('uiPortfolio', appData.portfolioValue || 0);
}

Finding Scheme Codes

Where to find scheme codes: Visit MFapi.in and search for your fund. The scheme code is shown in the URL and API response.

Example Scheme Codes

Axis Bluechip - Direct Growth

Code: 120503

SBI Bluechip - Regular Growth

Code: 100042

HDFC Flexi Cap - Direct Growth

Code: 119591

Parag Parikh Flexi Cap - Direct Growth

Code: 122639

Searching for Codes

# Search by fund name
curl "https://api.mfapi.in/mf/search?q=axis%20bluechip"

# Get details by code
curl "https://api.mfapi.in/mf/120503"

Data Flow Diagram

Advanced Features

Portfolio Breakdown

While the current UI only shows total value, the backend returns detailed portfolio data:
{
  "portfolio": [
    {
      "scheme_code": "120503",
      "scheme_name": "Axis Bluechip Fund - Direct Plan - Growth",
      "units": 150.5,
      "nav": 84.5231,
      "current_value": 12720.73
    },
    {
      "scheme_code": "119591",
      "scheme_name": "HDFC Flexi Cap Fund - Direct Plan - Growth",
      "units": 200.0,
      "nav": 125.45,
      "current_value": 25090.00
    }
  ],
  "total_value": 37810.73
}
This can be used to build:
  • Individual fund cards
  • Pie charts showing allocation
  • Gains/loss tracking (if purchase NAV is stored)

Limitations

Manual Updates Required: Unlike bank sync, holdings are not auto-updated. Users must manually add new purchases or update units after redemptions.
No Purchase Tracking: The system doesn’t track purchase date or NAV, so gains/losses cannot be calculated. Only current value is shown.
NAV Freshness: MFapi.in updates NAV data after market close (~6 PM IST). Intraday values are not available.

Future Enhancements

Potential improvements to portfolio tracking:
Store purchase date and NAV to calculate:
  • Absolute gains/losses
  • XIRR (annualized returns)
  • Days held
Integrate with CAS (Consolidated Account Statement) to automatically fetch holdings
  • Allocation pie chart (equity vs debt)
  • Value over time graph
  • Fund-wise performance comparison
Associate holdings with financial goals (retirement, house, etc.)

Best Practices

Update after SIP

Add new units immediately after each SIP investment

Use Direct Plans

Direct plan codes have lower expense ratios than regular plans

Review quarterly

Check portfolio allocation and rebalance if needed

Backup holdings

Keep a copy of holdings.json as backup

Troubleshooting

Check:
  • Holdings are saved (look for holdings.json file)
  • Scheme codes are valid (test on mfapi.in)
  • Internet connection is active
Debug:
curl http://localhost:8000/api/portfolio
MFapi.in returns the latest available NAV, which might be T-1 (previous day) if today’s NAV isn’t published yet.
Verify code:
curl "https://api.mfapi.in/mf/120503"
If it returns an error, the code is invalid. Search for the correct code on mfapi.in.
Ensure holdings.json has write permissions:
ls -la holdings.json
chmod 644 holdings.json

Build docs developers (and LLMs) love