feat: Add CI/CD setup guide with Gitea Actions for trading analysis application

feat: Implement multi-user support with separate brokerage accounts and user authentication

feat: Configure SSO authentication setup using Google OAuth 2.0 for secure access

refactor: Update index page to reflect new Trading Analysis Dashboard features and descriptions

docs: Enhance quickstart guide for deploying Trading Analysis Dashboard with detailed steps

chore: Add runner configuration for Gitea Actions with logging and container settings
This commit is contained in:
Peter Wood
2025-11-14 12:43:09 -05:00
parent 2f5e59b40f
commit c6eb26037b
24 changed files with 3594 additions and 169 deletions

141
api-reference/auth.mdx Normal file
View File

@@ -0,0 +1,141 @@
---
title: 'Authentication'
description: 'OAuth 2.0 authentication endpoints and flow'
---
## Overview
The Trading Analysis Dashboard uses Google OAuth 2.0 for secure authentication. All API endpoints require an authenticated session.
## Authentication Flow
<Steps>
<Step title="User visits application">
Unauthenticated users are redirected to the login page
</Step>
<Step title="Click Sign in with Google">
User initiates OAuth flow by clicking the Google sign-in button
</Step>
<Step title="Google authorization">
User is redirected to Google to authorize the application
</Step>
<Step title="Callback">
Google redirects back to the application with authorization code
</Step>
<Step title="Session created">
Application exchanges code for tokens and creates a secure session
</Step>
<Step title="Access granted">
User is redirected to the dashboard with an authenticated session
</Step>
</Steps>
## Endpoints
### Login Page
```
GET /auth/login
```
Displays the login page for unauthenticated users.
### Initiate OAuth
```
GET /login
```
Redirects user to Google OAuth authorization page.
### OAuth Callback
```
GET /auth/callback
```
Handles the OAuth callback from Google and creates user session.
**Query Parameters:**
- `code` (string): Authorization code from OAuth provider
- `state` (string): State parameter for security
### Logout
```
GET /logout
```
Clears user session and logs out the user.
### User Profile
```
GET /auth/profile
```
Displays user profile information (requires authentication).
## Session Management
- Sessions are stored server-side using Flask sessions
- Session cookies are HTTP-only and secure (in production)
- Sessions expire after a period of inactivity
- Users must re-authenticate after session expiration
## User Authorization
Access is controlled by the `AUTHORIZED_USERS` environment variable:
```env
AUTHORIZED_USERS=user1@example.com,user2@example.com,user3@example.com
```
Only users with email addresses in this list can access the application after authenticating with Google.
## Error Responses
### 401 Unauthorized
```json
{
"success": false,
"error": "Authentication required",
"redirect_to_login": true
}
```
### 403 Forbidden
```json
{
"success": false,
"error": "Access denied. User not authorized."
}
```
## Security Best Practices
<CardGroup cols={2}>
<Card title="HTTPS Only" icon="lock">
Always use HTTPS in production for OAuth callbacks
</Card>
<Card title="Secure Sessions" icon="shield-check">
Session cookies are HTTP-only and secure
</Card>
<Card title="User Whitelist" icon="users">
Only authorized email addresses can access the application
</Card>
<Card title="Token Security" icon="key">
OAuth tokens are never exposed to the client
</Card>
</CardGroup>
## Configuration
See the [SSO Setup Guide](/guides/setup/sso) for detailed configuration instructions.

View File

@@ -0,0 +1,124 @@
---
title: 'Get Month Data'
api: 'GET /api/month/{month}'
description: 'Retrieves detailed trading data for a specific month'
---
## Endpoint
```
GET /api/month/{month}
```
## Path Parameters
<ParamField path="month" type="string" required>
Month in YYYY-MM format (e.g., "2024-08")
</ParamField>
## Authentication
Requires OAuth 2.0 authentication via session cookies.
## Response
Returns detailed trading data including summary, trades, and dividends.
<ResponseField name="success" type="boolean" required>
Request success status
</ResponseField>
<ResponseField name="summary" type="object" required>
Monthly summary statistics
<Expandable title="Summary Fields">
<ResponseField name="month" type="string">
Month in YYYY-MM format
</ResponseField>
<ResponseField name="total_trades" type="number">
Total number of completed trades
</ResponseField>
<ResponseField name="winning_trades" type="number">
Number of profitable trades
</ResponseField>
<ResponseField name="win_rate" type="number">
Win rate percentage
</ResponseField>
<ResponseField name="trading_profit_loss" type="number">
Total profit/loss from trades
</ResponseField>
<ResponseField name="total_dividends" type="number">
Total dividend income
</ResponseField>
<ResponseField name="total_return_with_dividends" type="number">
Combined trading P&L and dividends
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="trades" type="array" required>
List of completed trades
</ResponseField>
<ResponseField name="dividends" type="array" required>
List of dividend payments
</ResponseField>
## Example
<CodeGroup>
```bash cURL
curl -X GET https://your-domain.com/api/month/2024-08 \
-H "Cookie: session=your_session_cookie"
```
```javascript JavaScript
const month = '2024-08';
const response = await fetch(`/api/month/${month}`);
const data = await response.json();
if (data.success) {
console.log(`P/L: $${data.summary.trading_profit_loss}`);
console.log(`Trades: ${data.trades.length}`);
}
```
</CodeGroup>
## Response Example
```json
{
"success": true,
"summary": {
"month": "2024-08",
"total_trades": 15,
"winning_trades": 9,
"win_rate": 60.0,
"trading_profit_loss": 850.75,
"total_dividends": 125.50,
"total_return_with_dividends": 976.25
},
"trades": [
{
"symbol": "AAPL",
"buy_date": "2024-08-01",
"sell_date": "2024-08-15",
"buy_price": 195.50,
"sell_price": 198.75,
"volume": 100,
"total_profit_loss": 325.00,
"return_percentage": 1.66,
"trade_result": "Win"
}
],
"dividends": [
{
"transaction_date": "2024-08-15",
"symbol": "MSFT",
"action": "Cash Dividend",
"amount": 75.50
}
],
"data_source": "postgresql"
}
```

116
api-reference/months.mdx Normal file
View File

@@ -0,0 +1,116 @@
---
title: 'Get Available Months'
api: 'GET /api/months'
description: 'Retrieves a list of all months that have trading data available'
---
## Endpoint
```
GET /api/months
```
## Authentication
Requires OAuth 2.0 authentication via session cookies.
## Parameters
None
## Response
<ResponseField name="success" type="boolean" required>
Indicates if the request was successful
</ResponseField>
<ResponseField name="months" type="array" required>
List of month objects
<Expandable title="Month Object">
<ResponseField name="month" type="string">
Month in YYYY-MM format
</ResponseField>
<ResponseField name="total_return_with_dividends" type="number">
Total return including dividends for that month
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="data_source" type="string" required>
Database source (always "postgresql")
</ResponseField>
## Example
<CodeGroup>
```bash cURL
curl -X GET https://your-domain.com/api/months \
-H "Cookie: session=your_session_cookie"
```
```javascript JavaScript
const response = await fetch('/api/months');
const data = await response.json();
if (data.success) {
console.log('Available months:', data.months);
}
```
```python Python
import requests
response = requests.get('http://localhost:5000/api/months')
data = response.json()
if data['success']:
for month in data['months']:
print(f"{month['month']}: ${month['total_return_with_dividends']}")
```
</CodeGroup>
## Response Example
```json
{
"success": true,
"months": [
{
"month": "2024-08",
"total_return_with_dividends": 1250.75
},
{
"month": "2024-07",
"total_return_with_dividends": -320.50
},
{
"month": "2024-06",
"total_return_with_dividends": 890.25
}
],
"data_source": "postgresql"
}
```
## Error Responses
<ResponseExample>
```json Database Connection Failed
{
"success": false,
"error": "Database connection failed"
}
```
</ResponseExample>
<ResponseExample>
```json Unauthorized
{
"success": false,
"error": "Authentication required",
"redirect_to_login": true
}
```
</ResponseExample>

View File

@@ -0,0 +1,136 @@
---
title: 'Portfolio Holdings'
api: 'GET /api/portfolio/holdings'
description: 'Get, add, update, or delete portfolio holdings'
---
## Get All Holdings
```
GET /api/portfolio/holdings
```
Returns all holdings for the current user with current prices and calculated metrics.
### Response Example
```json
{
"success": true,
"holdings": [
{
"id": 1,
"symbol": "AAPL",
"holding_type": "stock",
"shares": 100,
"average_cost": 150.50,
"current_price": 175.25,
"total_cost": 15050.00,
"current_value": 17525.00,
"gain_loss": 2475.00,
"return_percentage": 16.44,
"last_updated": "2024-11-14T10:30:00Z"
}
]
}
```
## Add a Holding
```
POST /api/portfolio/holdings
```
### Request Body
<ParamField body="symbol" type="string" required>
Stock ticker symbol (e.g., "AAPL")
</ParamField>
<ParamField body="holding_type" type="string" required>
Type: "stock", "etf", or "mutual_fund"
</ParamField>
<ParamField body="shares" type="number" required>
Number of shares owned
</ParamField>
<ParamField body="average_cost" type="number" required>
Average cost per share
</ParamField>
<ParamField body="notes" type="string">
Optional notes about the holding
</ParamField>
### Example
<CodeGroup>
```bash cURL
curl -X POST https://your-domain.com/api/portfolio/holdings \
-H "Content-Type: application/json" \
-d '{
"symbol": "AAPL",
"holding_type": "stock",
"shares": 100,
"average_cost": 150.50,
"notes": "Tech holding"
}'
```
```javascript JavaScript
const response = await fetch('/api/portfolio/holdings', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
symbol: 'AAPL',
holding_type: 'stock',
shares: 100,
average_cost: 150.50,
notes: 'Tech holding'
})
});
const data = await response.json();
```
</CodeGroup>
## Update a Holding
```
PUT /api/portfolio/holdings/{id}
```
### Path Parameters
<ParamField path="id" type="integer" required>
Holding ID to update
</ParamField>
### Request Body
<ParamField body="shares" type="number">
Updated number of shares
</ParamField>
<ParamField body="average_cost" type="number">
Updated average cost per share
</ParamField>
<ParamField body="notes" type="string">
Updated notes
</ParamField>
## Delete a Holding
```
DELETE /api/portfolio/holdings/{id}
```
### Path Parameters
<ParamField path="id" type="integer" required>
Holding ID to delete
</ParamField>

View File

@@ -0,0 +1,104 @@
---
title: 'Refresh Portfolio Prices'
api: 'POST /api/portfolio/refresh-prices'
description: 'Fetch current market prices for all portfolio holdings'
---
## Endpoint
```
POST /api/portfolio/refresh-prices
```
## Description
Fetches the latest market prices from Finnhub API for all holdings in the user's portfolio.
## Authentication
Requires OAuth 2.0 authentication via session cookies.
## Rate Limiting
The free Finnhub API tier allows 60 requests per minute. The application intelligently manages API requests to stay within these limits.
## Response
<ResponseField name="success" type="boolean" required>
Indicates if the refresh was successful
</ResponseField>
<ResponseField name="updated_count" type="number">
Number of holdings successfully updated
</ResponseField>
<ResponseField name="errors" type="array">
List of any errors encountered during refresh
</ResponseField>
## Example
<CodeGroup>
```bash cURL
curl -X POST https://your-domain.com/api/portfolio/refresh-prices \
-H "Cookie: session=your_session_cookie"
```
```javascript JavaScript
const response = await fetch('/api/portfolio/refresh-prices', {
method: 'POST'
});
const data = await response.json();
if (data.success) {
console.log(`Updated ${data.updated_count} holdings`);
}
```
```python Python
import requests
response = requests.post('http://localhost:5000/api/portfolio/refresh-prices')
data = response.json()
if data['success']:
print(f"Updated {data['updated_count']} holdings")
```
</CodeGroup>
## Response Example
```json Success
{
"success": true,
"updated_count": 5,
"message": "Successfully updated prices for 5 holdings"
}
```
```json Partial Success
{
"success": true,
"updated_count": 4,
"errors": [
{
"symbol": "INVALID",
"error": "Symbol not found"
}
]
}
```
```json Error
{
"success": false,
"error": "Finnhub API key not configured"
}
```
## Notes
- Prices are automatically refreshed when viewing the portfolio page if last update was >15 minutes ago
- Use this endpoint to manually force a refresh at any time
- Mutual fund prices may be delayed 15-30 minutes

105
api-reference/timeframe.mdx Normal file
View File

@@ -0,0 +1,105 @@
---
title: 'Get Timeframe Data'
api: 'GET /api/timeframe-data'
description: 'Retrieves trading analysis data for a custom timeframe or all-time data'
---
## Endpoint
```
GET /api/timeframe-data
```
## Query Parameters
<ParamField query="start" type="string">
Start date in YYYY-MM-DD format (optional if using all=true)
</ParamField>
<ParamField query="end" type="string">
End date in YYYY-MM-DD format (optional if using all=true)
</ParamField>
<ParamField query="all" type="string">
Set to "true" for all-time data (ignores start/end dates)
</ParamField>
<ParamField query="symbols" type="string">
Comma-separated list of stock symbols to filter by (optional)
</ParamField>
## Response
Returns summary, weekly breakdown, monthly breakdown, and open positions.
## Example
<CodeGroup>
```bash Date Range
curl -X GET "https://your-domain.com/api/timeframe-data?start=2024-01-01&end=2024-08-31"
```
```bash All Time
curl -X GET "https://your-domain.com/api/timeframe-data?all=true"
```
```bash With Symbols
curl -X GET "https://your-domain.com/api/timeframe-data?start=2024-06-01&end=2024-08-31&symbols=AAPL,TSLA,MSFT"
```
```javascript JavaScript
// Get YTD data
const start = '2024-01-01';
const end = new Date().toISOString().split('T')[0];
const response = await fetch(`/api/timeframe-data?start=${start}&end=${end}`);
const data = await response.json();
console.log('Total P/L:', data.summary.trading_profit_loss);
```
</CodeGroup>
## Response Example
```json
{
"success": true,
"data": {
"summary": {
"trading_profit_loss": 2450.75,
"total_dividends": 380.50,
"total_trades": 45,
"winning_trades": 28,
"win_rate_percentage": 62.22
},
"weekly_summary": [
{
"week_start": "2024-08-26",
"period": "2024-08-26",
"trading_profit_loss": 150.25,
"total_dividends": 25.00,
"total_trades": 3,
"winning_trades": 2,
"win_rate_percentage": 66.67
}
],
"monthly_summary": [
{
"month_start": "2024-08-01",
"period": "2024-08",
"trading_profit_loss": 850.75,
"total_dividends": 125.50,
"total_trades": 15,
"winning_trades": 9,
"win_rate_percentage": 60.0
}
],
"open_positions": [
{
"symbol": "NVDA",
"shares": 150
}
]
},
"data_source": "postgresql"
}
```

View File

@@ -0,0 +1,59 @@
---
title: 'Get Trade Details'
api: 'GET /api/trade-details/{month}'
description: 'Retrieves detailed trade information for a specific month'
---
## Endpoint
```
GET /api/trade-details/{month}
```
## Path Parameters
<ParamField path="month" type="string" required>
Month in YYYY-MM format (e.g., "2024-08")
</ParamField>
## Response
Returns detailed trade information with buy/sell prices, volumes, and profit/loss calculations.
## Example
<CodeGroup>
```bash cURL
curl -X GET https://your-domain.com/api/trade-details/2024-08
```
```javascript JavaScript
const response = await fetch('/api/trade-details/2024-08');
const data = await response.json();
console.log('Trades:', data.trades);
```
</CodeGroup>
## Response Example
```json
{
"success": true,
"trades": [
{
"symbol": "AAPL",
"buy_date": "2024-08-01",
"sell_date": "2024-08-15",
"buy_price": 195.50,
"sell_price": 198.75,
"volume": 100,
"profit_per_share": 3.25,
"total_profit_loss": 325.00,
"return_percentage": 1.66,
"trade_result": "Win"
}
],
"data_source": "postgresql"
}
```