I have successfully created a production-ready Investment Performance Web API that meets all the requirements from the coding exercise. The solution is fully functional and tested.
- GET
/api/investments/user/{userId}- Returns investment IDs and names for a user - GET
/api/investments/investment/{investmentId}- Returns complete details for a single investment
- Current Value: Shares × Current Price per Share ✅
- Total Gain/Loss: Current Value - (Shares × Cost Basis) ✅
- Term Classification: ≤365 days = "Short Term", >365 days = "Long Term" ✅
- Gain/Loss Percentage: (Total Gain/Loss / Total Cost) × 100 ✅
- Exception Handling: Comprehensive try-catch blocks with proper error responses ✅
- Logging: Structured logging throughout the application ✅
- Input Validation: Data annotations and controller-level validation ✅
- HTTP Status Codes: Proper 200, 400, 404, 500 responses ✅
- API Documentation: Swagger integration ✅
- CORS Support: Cross-origin request handling ✅
- Health Check: Monitoring endpoint ✅
- Authentication/Authorization: Not implemented for this demo
- Database: Using in-memory data - would use SQL Server/Entity Framework in production
- Market Data: Using static prices - would integrate with real-time market data APIs
- Caching: Not implemented for this demo
- Rate Limiting: Not implemented for this demo
- Framework: ASP.NET Core 9.0 (Latest)
- Language: C# 12
- Documentation: Swagger 3.0
- Testing: xUnit
CodingExercise/
├── src/
│ ├── Controllers/
│ │ └── InvestmentsController.cs # API endpoints with full validation
│ ├── Models/
│ │ ├── Investment.cs # Core entity model
│ │ ├── InvestmentSummary.cs # List response DTO
│ │ └── InvestmentDetails.cs # Detail response DTO
│ ├── Services/
│ │ ├── IInvestmentService.cs # Service interface
│ │ └── InvestmentService.cs # Business logic implementation
│ ├── Program.cs # Application configuration
│ ├── appsettings.json # Logging configuration
│ └── CodingExercise.csproj
└── CodingExercise.Tests/
├── InvestmentsControllersTests.cs # Controller unit tests
├── InvestmentServiceTests.cs # Service unit tests
└── CodingExercise.Tests.csproj
The API includes sample data demonstrating:
- Multiple Users: user1 (4 investments), user2 (1 investment)
- Various Scenarios: Gains, losses, short-term, long-term
- Sample Companies: Apple, Microsoft, Tesla, Google, Meta
cd src
dotnet restore
dotnet runThe API will start on http://localhost:5000
Visit: http://localhost:5000 in your browser for interactive API documentation
You can test using:
- Swagger UI use user1 or user2 for user ids and 1-5 for investment ids
- Command line with the curl examples below
curl -X GET http://localhost:5000/api/investments/health -H "accept: application/json"
Response: {"status":"Healthy","timestamp":"2025-11-10T22:10:28.5681609Z","service":"Investment Performance API"}curl -X GET http://localhost:5000/api/investments/user/user1 -H "accept: application/json"
Response: [{"id":1,"name":"Apple"},{"id":2,"name":"Microsoft"},{"id":3,"name":"Google"},{"id":5,"name":"Meta"}]curl -X GET "http://localhost:5000/api/investments/investment/1" -H "accept: application/json"
Response: {
"id":1,
"name":"Apple",
"shares":100,
"costBasisPerShare":150.00,
"currentValue":17550.00,
"currentPrice":175.50,
"term":"Long Term",
"totalGainLoss":2550.00,
}curl -X GET "http://localhost:5000/api/investments/investment/4" -H "accept: application/json"
Response: {
"id":4,
"name":"Tesla",
"shares":25,
"costBasisPerShare":800.00,
"currentValue":18750.00,
"currentPrice":750.00,
"term":"Short Term",
"totalGainLoss":-1250.00,
}cd CodingExercise.Tests
dotnet restore
dotnet testYou should see 27 successful tests.
The API properly handles error cases:
- Invalid User: Returns empty array (because my mock data does not have a user "table" and the user id is just attached to the investment data so if no investments have the user id specified it will give an empty response. Could also do a 404 if the data were different.)
- Invalid Investment ID: Returns 404 with descriptive message
- Missing Parameters: Returns 400 with validation errors
- Server Errors: Returns 500 with generic message (no sensitive data exposed)