AI-Powered, Risk-Adjusted Mutual Fund Recommendations
Built with NestJS + TypeScript + MongoDB
PlutoMoney Quant is a sophisticated portfolio allocation algorithm that:
- Scores 1,800+ mutual funds monthly using 27 quantitative & qualitative parameters
- Normalizes scores within categories using Z-score methodology
- Weights parameters by risk profile (Safety Seeker to Aggressive Explorer)
- Constructs personalized portfolios based on SIP amount and risk tolerance
- Rebalances monthly to maintain optimal allocation
Phase 1: Data Ingestion - โ COMPLETE
- REST API with 4 endpoints
- MongoDB Collections 1 & 2 operational
- Morningstar data ingestion (AS-IS format)
- Monthly cron scheduler configured
- 8 funds, 13 snapshots ingested
Next: Phase 2 - Scoring Engine (Collections 3 & 4, Z-scores, Risk weighting)
# 1. Install dependencies
npm install
# 2. Setup environment
cp sample.env .env
# Edit .env: Set MONGODB_URI=mongodb://localhost:27017/plutomoney_quant
# 3. Start MongoDB (choose one)
brew services start mongodb-community # macOS
docker run -d -p 27017:27017 --name mongodb mongo:latest # Docker
# 4. Start the API server
npm run start:dev
# Server runs on: http://localhost:3000
# 5. Test via REST API
curl http://localhost:3000/api/mutual-funds | python3 -m json.tool
# 6. Or test via script
npm run test:ingestion
# 7. Verify in MongoDB
mongosh plutomoney_quant --eval "db.mfSchemeTrackRecord.find().pretty()"
See QUICK_API_REFERENCE.md
for complete API documentation
src/
โโโ app/ # Application root
โ โโโ app.module.ts
โ
โโโ modules/ # Feature modules
โ โโโ mutual-fund/ # Collections 1 & 2
โ โ โโโ schemas/ # MongoDB models
โ โ โโโ dao/ # Data Access Objects (Repository pattern)
โ โ โโโ dtos/ # Validation
โ โ โโโ services/ # Business logic
โ โ โโโ mutual-fund.module.ts
โ โ
โ โโโ morningstar/ # Data source integration
โ โ โโโ services/
โ โ โ โโโ morningstar-parser.service.ts
โ โ โ โโโ morningstar-api.service.ts
โ โ โโโ morningstar.module.ts
โ โ
โ โโโ cron/ # Scheduled tasks
โ โโโ services/
โ โ โโโ data-ingestion-cron.service.ts
โ โโโ cron.module.ts
โ
โโโ config/ # Configuration
โโโ database/ # Database layer
โโโ enums/ # Global type definitions
โโโ utils/ # Utilities
โโโ main.ts
- Module Pattern: Clear feature boundaries
- DAO Pattern: Repository for data access
- Service Layer: Business logic separation
- Dependency Injection: NestJS DI container
- SOLID Principles: All 5 applied
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MONTHLY DATA INGESTION (1st of Month) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
1. TRIGGER (Cron: 1st of month, 2 AM IST)
CronModule.DataIngestionCronService
โ
2. FETCH DATA
MorningstarModule.MorningstarApiService (future)
OR Read JSON file (current)
โ
3. PARSE & VALIDATE
MorningstarModule.MorningstarParserService
- Validate 27 parameters
- Check required fields
- Detect outliers
โ Returns: FundDataDto[]
โ
4. INGEST TO DATABASE
MutualFundModule.MutualFundService
- Upsert master record (Collection 1)
- Insert/update snapshot (Collection 2)
- Update reference list
โ
5. RESULT
IngestionResult {
success, fundsProcessed, fundsAdded, fundsUpdated, errors
}
Purpose: Master registry of all mutual funds
{
_id: ObjectId(),
fundName: "Axis Bluechip Fund",
amc: "Axis Mutual Fund",
schemeCode: "AXIS001",
status: "Active",
schemeMonthTrackList: [
{
timestamp: ISODate("2025-10-01"),
mfDataId: ObjectId() // Reference to Collection 2
}
]
}
Purpose: Monthly snapshots with 27 parameters
{
_id: ObjectId(),
timestamp: ISODate("2025-10-01"),
fundId: ObjectId(), // Reference to Collection 1
fundName: "Axis Bluechip Fund",
fundCategory: "Large Cap Equity",
// 17 Quantitative Parameters
fiveYearCagrEquity: 13.8,
sharpeRatio: 1.12,
alpha: 2.3,
beta: 0.88,
maxDrawdown: -11.2,
expenseRatioEquity: 0.9,
aumEquity: 18000,
// ... 10 more
// 5 Qualitative Parameters
fundHouseReputation: 5,
fundManagerTenure: 8,
fundManagerTrackRecord: 4.7,
amcRiskManagement: 4.5,
esgGovernance: 3.9,
// 5 Forward-Looking Parameters
benchmarkConsistency: 4.6,
peerComparison: 4.3,
taxEfficiency: 3.8,
fundInnovation: 4.2,
forwardRiskMitigation: 4.5
}
# Clear database
mongosh
use plutomoney_quant
db.dropDatabase()
# Run test
npm run test:ingestion
Expected: fundsAdded = 3, fundsUpdated = 0
npm run test:ingestion
Expected: fundsAdded = 0, fundsUpdated = 3
Edit src/scripts/test-data-ingestion.ts
line 165:
const timestamp = new Date('2025-11-01T00:00:00Z'); // November
Run: npm run test:ingestion
Expected: New snapshots added, track list grows
Error: Cannot connect to MongoDB
# Check if MongoDB is running
brew services list | grep mongodb
# Start MongoDB
brew services start mongodb-community
Error: Module not found
npm install
npm run start:dev # Development mode with watch
npm run start:prod # Production mode
npm run build # Build for production
npm run test:ingestion # Test data ingestion
npm run lint # Lint code
npm run format # Format code
Example: Add Scoring Engine Module
Step 1: Create structure
mkdir -p src/modules/scoring-engine/{schemas,dao,services}
Step 2: Create service
// src/modules/scoring-engine/services/z-score-calculator.service.ts
@Injectable()
export class ZScoreCalculatorService {
async calculateForCategory(category: string, timestamp: Date) {
// Implementation
}
}
Step 3: Create module
// src/modules/scoring-engine/scoring-engine.module.ts
@Module({
imports: [MutualFundModule],
providers: [ZScoreCalculatorService],
exports: [ZScoreCalculatorService],
})
export class ScoringEngineModule {}
Step 4: Import in AppModule
// src/app/app.module.ts
@Module({
imports: [
// ...
ScoringEngineModule,
],
})
export class AppModule {}
Data access is separated from business logic.
// โ
Good - Use DAO
const fund = await this.fundDao.findByFundName('Axis');
// โ Bad - Direct model access
const fund = await this.fundModel.findOne({ fundName: 'Axis' });
Business logic in services, not controllers or DAOs.
// MutualFundService orchestrates workflow
async ingestMonthlyData(funds, timestamp) {
// 1. Upsert master
// 2. Insert/update snapshot
// 3. Update references
}
All dependencies injected via constructor.
constructor(
private readonly trackRecordDao: MfSchemeTrackRecordDao,
private readonly monthwiseDao: MfSchemeDataMonthwiseDao,
) {}
Scheduled tasks for monthly ingestion.
@Cron('0 2 1 * *') // 1st of month, 2 AM
async handleMonthlyIngestion() {
// Fetch, parse, ingest
}
- Collections 1 & 2
- Morningstar parser
- Monthly cron job
- Z-score calculation (category-wise)
- Group score aggregation (8 parameter groups)
- Composite score (risk profile weighted)
- Collections 3 & 4
- Allocation matrix loader
- SIP bucket determination
- Fund selection (top-N by composite score)
- Constraint application (SIP mins, AMC diversity)
- BSE Star MFD API client
- SIP registration
- Order tracking
- Framework: NestJS 10.x
- Language: TypeScript 5.x
- Database: MongoDB with Mongoose
- Validation: class-validator, class-transformer
- Scheduling: @nestjs/schedule
- Statistics: simple-statistics
# Node Environment
NODE_ENV=development
# Server
PORT=3000
ALLOWED_ORIGINS=http://localhost:3000
# MongoDB
MONGODB_URI=mongodb://localhost:27017/plutomoney_quant
# Morningstar API (future)
# MORNINGSTAR_API_URL=https://api.morningstar.com
# MORNINGSTAR_API_KEY=your_key
Ingest fund data directly (perfect for testing before Morningstar is configured)
curl -X POST http://localhost:3000/api/mutual-funds/ingest \
-H "Content-Type: application/json" \
-d @test-api-payload.json | python3 -m json.tool
# All funds
curl http://localhost:3000/api/mutual-funds | python3 -m json.tool
# Filter by category
curl "http://localhost:3000/api/mutual-funds?category=Large%20Cap%20Equity" | python3 -m json.tool
curl http://localhost:3000/api/mutual-funds/LC001 | python3 -m json.tool
curl http://localhost:3000/api/mutual-funds/LC001/history | python3 -m json.tool
Complete API documentation: QUICK_API_REFERENCE.md
README.md
(this file) - Overview & Quick StartQUICK_API_REFERENCE.md
- API endpoints referencePHASE_1_COMPLETE.md
- Phase 1 implementation summary
PRD.md
- Product Requirements DocumentDocs/Implementation.md
- Step-by-step implementation guideDocs/FINAL_ARCHITECTURE.md
- System architectureDocs/DATA_FLOW_PRODUCTION.md
- Production data flowDocs/MORNINGSTAR_DATA_ANALYSIS.md
- Data format analysisDocs/SCORING_ENGINE_DEEP_DIVE.md
- Complete scoring engine explanation (4-step process, risk-profile-specific weights)
.cursor/rules/*.mdc
- AI context for Phase 2 developmentscoring_context.mdc
- Scoring engine logicschema_context.mdc
- Database schema definitionsallocation_context.mdc
- Portfolio allocation logic
- Build Schemas - Collections 3 & 4 (with risk_profile_scores array)
- Build DAOs - Data access layer for scoring collections
- Step 1: Points Assignment Service - Convert raw values โ points (0/3/5/7/10)
- Step 2: Category Stats Service - Calculate mean & stdev of points
- Step 3: Z-Score Calculator - Normalize points within category
- Step 4: Composite Scorer - Apply risk-profile-specific weights (4 scores per fund)
- Build Cron Job - Orchestrate scoring after data ingestion
๐ Read first: Docs/SCORING_ENGINE_DEEP_DIVE.md
for complete understanding
- Phase 3: Portfolio construction & allocation
- Phase 4: API endpoints for recommendations
- Phase 5: Frontend integration (see
Docs/UI_UX_doc.md
)
- Follow NestJS best practices
- Use DAO pattern for data access
- Write unit tests for services
- Document complex logic
- Follow SOLID principles
UNLICENSED - Private project
PlutoMoney Team
Built with โค๏ธ for intelligent portfolio management