This is a FastAPI-based backend service that provides financial data, including stock ticker information, historical data, news, and financial reports. It also features analysis features (coming soon) and user authentication with a personalized watchlist functionality.
The project is organized into several key files:
main.py: The main application file that initializes the FastAPI app, includes middleware, and mounts the API routers.database.py: Handles database connection (SQLite), session management (sync and async), and initialization.models.py: Contains all SQLAlchemy ORM models, defining the database table structures.schemas.py: Includes all Pydantic models used for data validation, serialization, and API request/response schemas.services.py: Holds the business logic, including fetching data from external APIs likeyfinanceand caching results in the database.auth.py: Manages user authentication, registration, and user management usingfastapi-users.routers/: A package containing the API routers for different parts of the application.ticker.py: Contains all API endpoints related to ticker data (/ticker/...).watchlist.py: Contains all API endpoints for the user watchlist (/users/me/watchlist/...).forex.py: Contains the API endpoint for foreign exchange rates (/forex).
Equisight backend follows a layered architecture common in modern web apps:
- Presentation Layer (
main.py,routers/): Build with FastAPI, responsible for handling HTTP requests, routing them to appropriate handlers, and managing request/response validation using Pydantic schemas fromschemas.py - Business Logic Layer (
services.py,auth.py): Contains core application logic.services.pyhandles fetching and caching of data from external sources.auth.pymanages user authentication and session management usingfastapi-users - Data Access Layer (
database.py,models.py): Database interactions using SQLAlchemy as the ORM.models.pydefines the database schema, whiledatabase.pymanages database connection. - External Services: The application integrates
yfinanceto retrieve real-time and historical financial data.
The following diagram illustrates the primary dependencies between the modules:
+----------------------------------+
| main.py (FastAPI App) |
| - Initializes DB |
| - Mounts Routers |
+----------------------------------+
|
v
+----------------------------------+
| routers/ (API Endpoints) |
| - watchlist.py, ticker.py, ... |
+----------------------------------+
| | |
| | +--------------------+
| | |
v v v
+----------------+ +----------------------+
| services.py | | auth.py |
| (Business Logic) | | (Auth & User Mgmt) |
+----------------+ +----------------------+
| | |
| +-----------------+--------------+
| |
v v
+----------------+ +----------------+ +----------------+
| yfinance | | database.py | | models.py |
| (External) | | (DB Session) | | (ORM Tables) |
+----------------+ +----------------+ +----------------+
^ ^
| |
+--------------------+
|
+----------------+
| schemas.py |
| (Pydantic Models)|
+----------------+
GET /ticker/{ticker}/info
Returns basic information about a ticker.
Parameters:
- 'ticker' (str): The stock symbol (e.g., 'AAPL').
Response Example:
{
"symbol": "AAPL",
"fullExchangeName": "NasdaqGS",
"shortName": "Apple Inc.",
"regularMarketPrice": 200.85,
"marketState": "CLOSED",
"region": "US",
"currency": "USD",
"previousClose": 199.95
}GET /ticker/{ticker}/history
Parameters:
start(str, optional): Start date inYYYY-MM-DD(default: 30 days ago)end(str, optional): End date inYYYY-MM-DD(default: today)
Usage Example: /ticker/{ticker}/history?start=2025-01-01&end=2025-05-05
Response Example:
{
"history": [
{
"ticker": "AAPL",
"timestamp": 1748577600,
"close": 200.85000610351562,
"volume": 70753100
},
...
]
} GET /ticker/{ticker}/all-time
Description: All time historical data in daily resolution
Response Example:
{
"ticker": "AAPL",
"totalDays": 11232,
"earliestDate": 345445200,
"latestDate": 1751947200,
"allTimeHistory": [
{
"ticker": "AAPL",
"timestamp": 345445200,
"close": 0.09859661757946014,
"volume": 469033600
},
...
]
}GET /ticker/{ticker}/news
Parameters:
count(int, optional): Number of articles to return (default: 10)
Usage Example: /ticker/{ticker}/news?count=20
Response Example:
{
"ticker": "AAPL",
"articles": [
{
"id": "55f60082-b396-3a53-a6ab-f66df41d6fa1",
"title": "Smartphone Sales Growth Hit by Tariff ‘Whirlwind of Uncertainty’",
"providerDisplayName": "Bloomberg",
"summary": "(Bloomberg) -- Sales of Apple Inc.’s iPhone and its closest rivals are expected to take a significant blow...",
"canonicalUrl": "https://finance.yahoo.com/news/smartphone-sales-growth-hit-tariff-091553675.html",
"thumbnailUrl": "https://media.zenfs.com/en/bloomberg_holding_pen_162/382f1e8fa70082fcb704824f50dbd2c8",
"timestamp": 1748596553,
"alternateThumbnailUrl": "https://s.yimg.com/uu/api/res/1.2/Et.aYmbkVIT_thsf4n06_g--~B/Zmk9c3RyaW07aD0xMjg7dz0xNzA7YXBwaWQ9eXRhY2h5b24-/https://media.zenfs.com/en/bloomberg_holding_pen_162/382f1e8fa70082fcb704824f50dbd2c8",
"clickThroughUrl": "https://finance.yahoo.com/news/smartphone-sales-growth-hit-tariff-091553675.html"
},
...
]
}GET /ticker/{ticker}/intraday
Response Example:
{
"marketOpen": 1749130200,
"marketClose": 1749153600,
"exchangeRate": 0.7772907018661499,
"intraday": [
{
"ticker": "AAPL",
"timestamp": 1749153540,
"close": 200.5500030517578
},
...
]
}Note:
- If market is closed, it will return the intraday data from the latest trading day
- Currently only tickers on US, Singapore, Hong Kong, London, and Tokyo Stock Exchanges are supported
GET /ticker/{ticker}/intraweek
Response Example:
{
"oldestOpen": 1749216600,
"latestClose": 1749758400,
"intraday": [
{
"ticker": "AAPL",
"timestamp": 1749742200,
"close": 198.22000122070312
},
...
]
}Note:
- If market is closed, it will return data for the past 5 trading days in 1h resolution
- Currently only tickers on US, Singapore, Hong Kong, London, and Tokyo Stock Exchanges are supported
GET /ticker/{ticker}/quarterly-reports
Response Example:
{
"ticker": "AAPL",
"quarterlyReports": [
{
"ticker": "AAPL",
"quarterEndDate": 1743379200,
"revenue": 95359000000.0,
"eps": 1.65,
"ebitda": 32250000000.0,
"netIncome": 24780000000.0,
"totalAssets": 331233000000.0,
"totalLiabilities": 264437000000.0,
"shareholderEquity": 66796000000.0,
"longTermDebt": 78566000000.0,
"cashAndEquivalents": 28162000000.0,
"operatingCashFlow": 23952000000.0,
"freeCashFlow": 20881000000.0,
"grossMargin": 0.47050619238876246,
"roe": null,
"roa": null,
"debtToEquity": 3.958874782921133
},
...
]
}GET /ticker/{ticker}/annual-reports
Response Example:
{
"ticker": "AAPL",
"annualReports": [
{
"ticker": "AAPL",
"yearEndDate": 1727654400,
"revenue": 391035000000.0,
"eps": 6.08,
"ebitda": 134661000000.0,
"netIncome": 93736000000.0,
"totalAssets": 364980000000.0,
"totalLiabilities": 308030000000.0,
"shareholderEquity": 56950000000.0,
"longTermDebt": 85750000000.0,
"cashAndEquivalents": 29943000000.0,
"operatingCashFlow": 118254000000.0,
"freeCashFlow": 108807000000.0,
"grossMargin": 0.4620634981523393,
"roe": 1.6459350307287095,
"roa": 0.25682503150857583,
"debtToEquity": 5.408779631255487
},
...
]
}GET /users/me/watchlist (UNFINISHED)
Description: Returns positions and other details that user specifies about a watched ticker.
Response Example:
{
"identifier": "[email protected]",
"tickers": [
"MA",
"UNH",
"V"
]
}POST /users/me/watchlist/{ticker}
Description: Adds a ticker to the user's watchlist. Empty request body.
Response: 201 Created on success.
DELETE /users/me/watchlist/{ticker}
Description: Removes a ticker and all its associated positions from the watchlist.
Response: 204 No Content on success.
GET /users/me/watchlist/{ticker}
Usage Example: /users/me/watchlist/UNH
Response Example:
{
"ticker": "UNH",
"positions": [
{
"id": 18,
"direction": "BUY",
"quantity": 2.0,
"unitCost": 300.0,
"createdAt": 1750696547
},
...
]
}POST /users/me/watchlist/{ticker}/positions
Description: Adds one or more new positions for a ticker that is already in the user's watchlist.
Request Example:
{
"direction": "SELL",
"quantity": 5,
"unitCost": 500.00
}Response Example:
{
"id": 2,
"direction": "SELL",
"quantity": 5,
"unitCost": 500.00,
"createdAt": 1750044120
}GET /users/me/watchlist/{ticker}/positions
Description: Returns all positions that a user has in a watched ticker.
Usage Example: /users/me/watchlist/UNH
Response Example:
{
"ticker": "UNH",
"positions": [
{
"id": 18,
"direction": "BUY",
"quantity": 2.0,
"unitCost": 300.0,
"createdAt": 1750696547
},
...
]
}PUT /users/me/watchlist/{ticker}/positions/{positionId}
Description: Updates/overrides an existing position, identified by its unique positionId.
Request Example:
{
"direction": "BUY",
"quantity": 20,
"unitCost": 825.50
}Response Example:
{
"id": 1,
"direction": "BUY",
"quantity": 20,
"unitCost": 825.50,
"createdAt": 1750043545
}DELETE /users/me/watchlist/{ticker}/positions/{positionId}
GET /users/me/watchlist/{ticker}/positions/{positionId}
Description: Outputs a particular position in a watched ticker by position ID.
Response Example:
{
"id": 1,
"direction": "BUY",
"quantity": 20,
"unitCost": 825.50,
"createdAt": 1750043545
}Description: Deletes a specific position by its positionId.
Response: 204 No Content on success.
GET /users/me/preferences
Description: User specified preferences (currently just base currency preference)
Response Example:
{
"currency": "USD"
}PUT /users/me/preferences
Description: Set own user preferences
Request Example:
{
"currency": "SGD"
}Response Example:
{
"currency": "SGD"
}GET /backtester/calculate-return/{ticker}
Description: Obtain theoretical gains based on investment strategy on a ticker with historical data.
Parameters:
purchaseDate(str): Initial purchase datesellDate(str): Sell dateinvestmentType(str, Literal): Either lump sum, dollar-cost average, or lump sum and dollar-cost averagelumpSumAmount(str, Optional): Defaults to 1000dcaAmount(str, Optional): Defaults to 100 per paymentdcaFrequency(str, Optional Literal): Weekly, Monthly, or Yearly
Usage Example:
/backtester/calculate-return/AAPL?purchaseDate=2024-01-01&sellDate=2025-07-06&investmentType=lumpSum&lumpSumAmount=10000/backtester/calculate-return/AAPL?purchaseDate=2024-01-01&sellDate=2025-07-06&investmentType=dca&dcaAmount=1000&dcaFrequency=monthly/backtester/calculate-return/aapl?purchaseDate=2021-01-01&sellDate=2025-01-01&investmentType=lumpSumDca&lumpSumAmount=1000&dcaAmount=100&dcaFrequency=monthly
Response Example:
Lump Sum:
{
"ticker": "AAPL",
"currency": "USD",
"purchaseDate": "2024-01-01",
"sellDate": "2025-07-06",
"investmentType": "lumpSum",
"lumpSumAmount": 10000.0,
"dcaAmount": 100.0,
"dcaFrequency": null,
"totalInvested": 10000.0,
"totalSharesPurchased": 54.2622,
"averagePurchasePrice": 184.29,
"sellPrice": 213.55,
"sellValue": 11587.69,
"totalReturn": 1587.69,
"totalReturnPercentage": 15.88,
"annualizedReturn": 10.24,
"daysHeld": 552,
"numberOfPurchases": 1,
"timestamp": 1753259500
}DCA:
{
"ticker": "AAPL",
"currency": "USD",
"purchaseDate": "2024-01-01",
"sellDate": "2025-07-06",
"investmentType": "dca",
"lumpSumAmount": 1000.0,
"dcaAmount": 1000.0,
"dcaFrequency": "monthly",
"totalInvested": 19000.0,
"totalSharesPurchased": 91.4815,
"averagePurchasePrice": 207.69,
"sellPrice": 213.55,
"sellValue": 19535.87,
"totalReturn": 535.87,
"totalReturnPercentage": 2.82,
"annualizedReturn": 1.86,
"daysHeld": 552,
"numberOfPurchases": 19,
"timestamp": 1753259460
}Lump Sum + DCA:
{
"ticker": "AAPL",
"currency": "USD",
"purchaseDate": "2021-01-01",
"sellDate": "2025-01-01",
"investmentType": "lumpSumDca",
"lumpSumAmount": 1000,
"dcaAmount": 100,
"dcaFrequency": "monthly",
"totalInvested": 5800,
"totalSharesPurchased": 37.4793,
"averagePurchasePrice": 154.75,
"sellPrice": 249.82,
"sellValue": 9362.97,
"totalReturn": 3562.97,
"totalReturnPercentage": 61.43,
"annualizedReturn": 12.72,
"daysHeld": 1461,
"numberOfPurchases": 49,
"timestamp": 1753259198
}GET /analysis/{ticker}/fairvalue
Description: Get calculated "fair value" of a ticker based on Damodaran's models
Parameters:
high(int): Assumed high growth period in years (default: 5)stable(int): Assumed stable growth period in years (default: 5)
Usage Example: /analysis/AAPL/fairvalue?high=5&stable=5
Response Example:
{
"symbol": "AAPL",
"costOfEquity": 9.216220049591065,
"costOfDebt": 4.354000049591065,
"wacc": 9.063600258375969,
"roic": 55.53285888608812,
"expectedGrowthRate": 13.448637307417242,
"fairValue": 194.36087124469228
}GET /analysis/{ticker}/grahamvalue
Description: Gets current market price to get implied growth rate, and uses that growth rate with "Graham Formula" to obtain a fair value estimate
Parameters:
terminal(float): Assumed terminal growth rate of company in percentage (default: 5)growth(int): Assumed continuous growth period (default: 5)
Usage Example: /analysis/AAPL/grahamvalue?terminal=5&growth=10
Response Example:
{
"symbol": "AAPL",
"wacc": 9.063600258375969,
"impliedGrowthRate": 17.588908743855477,
"grahamValue": 261.3025431202308
}GET /forex
Parameters:
fromCur(str, optional): Base Currency (default: USD)toCur(str, optional): New Currency (default: SGD)
Usage Example: /forex?fromCur=USD&toCur=JPY
Response Example:
{
"fromCurrency": "USD",
"toCurrency": "JPY",
"forexRate": 143.54200744628906
}The following authentication endpoints are available under the /auth prefix, largely provided by fastapi-users:
POST /auth/login: Logs in a user.- Request Body:
OAuth2PasswordRequestForm(expectsusernameandpasswordin form data). - Response: Sets an authentication cookie (
equisightauth) and returns user information (schemas.UserRead).
- Request Body:
POST /auth/logout: Logs out a user.- Response: Clears the authentication cookie.
POST /auth/register: Registers a new user.- Request Body:
schemas.UserCreate(email, password, username). - Response: User information (
schemas.UserRead).
- Request Body:
POST /auth/forgot-password: Requests a password reset token.- Request Body: Email of the user.
POST /auth/reset-password: Resets the password using a token.- Request Body: Token and new password.
POST /auth/request-verify-token: Requests an email verification token.- Request Body: Email of the user.
POST /auth/verify: Verifies the user's email using a token.- Request Body: Verification token.