Pattern 18: Opportunity Mining
Intent
Systematically discover and quantify positive opportunities for expansion, deeper engagement, referrals, and revenue growth by identifying high-potential families, moments of receptivity, underutilized resources, and successful patterns that can be replicated at scale.
Also Known As
- Opportunity Detection
- Growth Mining
- Revenue Discovery
- Expansion Identification
- Positive Signal Detection
- Upsell Discovery
Problem
Organizations focus on problems, miss opportunities.
Sarah spends 80% of time on: - At-risk families (withdrawal prevention) - Payment issues (collections) - Low engagement (interventions)
But misses gold opportunities:
Opportunity 1: Hidden Champions - Chen family: 92% engagement, 3 referrals, volunteers 8 hours/month - Never asked to be mentor or board member - Missed opportunity: Could mentor 3-5 struggling families, recruit from network
Opportunity 2: Expansion Ready - Martinez family: Enrolled one child, has two more at home (ages eligible) - Financial capacity confirmed (always pay early) - Missed opportunity: $900/year additional revenue + deeper commitment
Opportunity 3: Receptivity Windows - Johnson family: Just volunteered for first time (after 8 months) - Engagement spiked 35 points in 2 weeks - Missed opportunity: Perfect moment to ask for referral or additional involvement
Opportunity 4: Underutilized Assets - Williams family: Professional photographer, never asked to help - Smith family: Owns community center, could host events - Missed opportunity: Free resources sitting idle
Without opportunity mining: - Reactive mindset (fix problems, not seize opportunities) - Miss revenue expansion - Underutilize champion families - Don't capitalize on receptivity windows - Resources wasted on low-potential activities
With opportunity mining: - Proactively identify high-value opportunities - Quantify potential impact ($, engagement, referrals) - Prioritize by ROI - Strike when receptivity is highest - Systematic approach to growth
Context
When this pattern applies:
- Want growth (not just retention)
- Have capacity for expansion activities
- Population shows variation (some high-potential)
- Data reveals signals of opportunity
- Can act on opportunities discovered
When this pattern may not be needed:
- Pure survival mode (retention is everything)
- No capacity for growth initiatives
- Population is homogeneous (no variation)
- Already at capacity (can't grow)
Forces
Competing concerns:
1. Problem Focus vs Opportunity Focus - Problems demand attention (urgent) - Opportunities require proactive effort (important) - Balance: 70% problem, 30% opportunity
2. Sure Bets vs Long Shots - Sure bets: Low impact, high probability - Long shots: High impact, low probability - Balance: Portfolio approach (mix of both)
3. Revenue vs Relationship - Revenue opportunities = transactional - Relationship opportunities = long-term - Balance: Both, but relationship-first
4. Individual vs Systematic - Individual: One-off opportunities - Systematic: Patterns that scale - Balance: Start individual, find patterns
5. Quantity vs Quality - Many small opportunities - Few large opportunities - Balance: Pareto principle (80/20)
Solution
Build systematic opportunity discovery across multiple dimensions:
Dimension 1: Expansion Opportunities - Additional services/products - Additional family members - Upgrade to premium tiers - Long-term commitments
Dimension 2: Referral Opportunities - High engagement + social connections - Recent positive experiences - Natural evangelists
Dimension 3: Champion Development - High performers willing to mentor - Board/leadership potential - Ambassador opportunities
Dimension 4: Engagement Deepening - Receptivity windows (recent positive events) - Underutilized family talents/resources - Volunteer potential
Dimension 5: Partnership Opportunities - Professional services - Physical assets (space, equipment) - Network access
Opportunity Scoring Formula:
Opportunity Score =
Potential_Value ×
Success_Probability ×
Receptivity_Level ×
Effort_Efficiency
Where:
Potential_Value = $ or engagement impact
Success_Probability = 0-1 (based on signals)
Receptivity_Level = 0-1 (timing factor)
Effort_Efficiency = 1/effort_required
Structure
Opportunity Mining Tables
-- Store discovered opportunities
CREATE TABLE opportunities (
opportunity_id INT PRIMARY KEY IDENTITY(1,1),
family_id INT NOT NULL,
-- Opportunity details
opportunity_type VARCHAR(100), -- 'expansion', 'referral', 'champion', 'engagement', 'partnership'
opportunity_category VARCHAR(100), -- 'additional_enrollment', 'mentor', 'board_member', 'ambassador'
opportunity_description NVARCHAR(1000),
-- Scoring
potential_value DECIMAL(10,2), -- $ or engagement points
success_probability DECIMAL(3,2), -- 0-1
receptivity_score DECIMAL(3,2), -- 0-1
effort_required INT, -- Minutes
opportunity_score DECIMAL(10,2), -- Composite score
-- Signals that triggered discovery
trigger_signals NVARCHAR(MAX), -- JSON
-- Timing
discovered_date DATETIME2 DEFAULT GETDATE(),
optimal_contact_window_start DATE,
optimal_contact_window_end DATE,
-- Execution
status VARCHAR(50) DEFAULT 'new', -- 'new', 'in_progress', 'pursued', 'converted', 'declined', 'expired'
assigned_to VARCHAR(100),
contacted_date DATETIME2,
-- Outcome
outcome VARCHAR(50), -- 'converted', 'declined', 'not_pursued', 'expired'
actual_value DECIMAL(10,2),
outcome_date DATETIME2,
outcome_notes NVARCHAR(MAX),
CONSTRAINT FK_opp_family FOREIGN KEY (family_id)
REFERENCES families(family_id)
);
-- Index for active opportunities
CREATE INDEX IX_opportunities_active ON opportunities(status, opportunity_score DESC)
WHERE status IN ('new', 'in_progress');
-- Track opportunity patterns
CREATE TABLE opportunity_patterns (
pattern_id INT PRIMARY KEY IDENTITY(1,1),
-- Pattern definition
pattern_name VARCHAR(200),
pattern_description NVARCHAR(1000),
opportunity_type VARCHAR(100),
-- Signals that indicate this opportunity
required_signals NVARCHAR(MAX), -- JSON
-- Historical performance
times_identified INT DEFAULT 0,
times_pursued INT DEFAULT 0,
times_converted INT DEFAULT 0,
conversion_rate DECIMAL(5,2),
avg_value DECIMAL(10,2),
active BIT DEFAULT 1,
created_date DATETIME2 DEFAULT GETDATE()
);
-- Track ROI of opportunity mining
CREATE TABLE opportunity_roi_tracking (
tracking_id INT PRIMARY KEY IDENTITY(1,1),
period_start DATE,
period_end DATE,
-- Opportunities
total_opportunities_identified INT,
opportunities_pursued INT,
opportunities_converted INT,
-- Value
total_potential_value DECIMAL(10,2),
total_actual_value DECIMAL(10,2),
-- Effort
total_effort_hours DECIMAL(8,2),
-- ROI
roi_percentage DECIMAL(8,2), -- (actual_value / effort_cost) * 100
created_date DATETIME2 DEFAULT GETDATE()
);
Implementation
Opportunity Mining Engine
class OpportunityMiningEngine {
constructor(db) {
this.db = db;
}
async mineOpportunities() {
const opportunities = [];
// Mine each opportunity type
const expansion = await this.mineExpansionOpportunities();
const referral = await this.mineReferralOpportunities();
const champion = await this.mineChampionOpportunities();
const engagement = await this.mineEngagementOpportunities();
const partnership = await this.minePartnershipOpportunities();
opportunities.push(...expansion, ...referral, ...champion, ...engagement, ...partnership);
// Score and rank
const scored = opportunities.map(opp => ({
...opp,
opportunity_score: this.calculateOpportunityScore(opp)
}));
scored.sort((a, b) => b.opportunity_score - a.opportunity_score);
// Save to database
for (const opp of scored) {
await this.saveOpportunity(opp);
}
return scored;
}
async mineExpansionOpportunities() {
const opportunities = [];
// Opportunity: Additional family members
const additionalMembers = await this.db.query(`
SELECT
f.family_id,
f.family_name,
f.enrolled_student_count,
f.total_eligible_children,
fem.engagement_score,
fem.financial_health_score,
p.avg_payment_amount
FROM families f
JOIN family_engagement_metrics fem ON f.family_id = fem.family_id
LEFT JOIN (
SELECT family_id, AVG(amount) as avg_payment_amount
FROM payments
GROUP BY family_id
) p ON f.family_id = p.family_id
WHERE f.enrolled_student_count < f.total_eligible_children
AND fem.engagement_score > 70
AND fem.financial_health_score > 70
`);
for (const family of additionalMembers) {
const additionalKids = family.total_eligible_children - family.enrolled_student_count;
const potentialRevenue = additionalKids * 450; // $450 per student per semester
opportunities.push({
family_id: family.family_id,
opportunity_type: 'expansion',
opportunity_category: 'additional_enrollment',
opportunity_description: `${family.family_name} has ${additionalKids} more eligible child(ren). High engagement and financial health.`,
potential_value: potentialRevenue,
success_probability: this.estimateExpansionProbability(family),
receptivity_score: family.engagement_score / 100,
effort_required: 30, // 30 minute conversation
trigger_signals: {
eligible_children: additionalKids,
engagement_score: family.engagement_score,
financial_health: family.financial_health_score
}
});
}
// Opportunity: Upgrade to year commitment (discount + stability)
const upgradeOpportunities = await this.db.query(`
SELECT
f.family_id,
f.family_name,
f.enrollment_type,
fem.engagement_score,
fem.tenure_score,
DATEDIFF(NOW(), f.enrollment_date) as days_enrolled
FROM families f
JOIN family_engagement_metrics fem ON f.family_id = fem.family_id
WHERE f.enrollment_type = 'semester'
AND fem.engagement_score > 75
AND DATEDIFF(NOW(), f.enrollment_date) > 180
`);
for (const family of upgradeOpportunities) {
opportunities.push({
family_id: family.family_id,
opportunity_type: 'expansion',
opportunity_category: 'annual_commitment',
opportunity_description: `${family.family_name} highly engaged and established. Offer annual enrollment with 10% discount.`,
potential_value: 810, // Annual: $900, discount: $90, net: $810 vs $450 semester
success_probability: 0.65,
receptivity_score: Math.min(1.0, family.days_enrolled / 365),
effort_required: 20,
trigger_signals: {
engagement_score: family.engagement_score,
tenure_days: family.days_enrolled,
enrollment_type: family.enrollment_type
}
});
}
return opportunities;
}
async mineReferralOpportunities() {
const opportunities = [];
// High engagement + social connections + recent positive experience
const referralCandidates = await this.db.query(`
SELECT
f.family_id,
f.family_name,
fem.engagement_score,
COUNT(DISTINCT re.edge_id) as connection_count,
MAX(il.interaction_timestamp) as last_positive_interaction
FROM families f
JOIN family_engagement_metrics fem ON f.family_id = fem.family_id
LEFT JOIN relationship_edges re ON f.family_id = re.source_entity_id
LEFT JOIN interaction_log il ON f.family_id = il.family_id
AND il.outcome_category = 'success'
AND il.interaction_timestamp >= DATE_SUB(NOW(), INTERVAL 30 DAY)
WHERE fem.engagement_score > 80
AND f.enrolled_current_semester = 1
GROUP BY f.family_id, f.family_name, fem.engagement_score
HAVING connection_count > 3
AND last_positive_interaction IS NOT NULL
`);
for (const family of referralCandidates) {
const daysSincePositive = Math.floor(
(Date.now() - new Date(family.last_positive_interaction)) / (1000 * 60 * 60 * 24)
);
// Receptivity highest within 7 days of positive experience
const receptivity = Math.max(0.3, 1.0 - (daysSincePositive / 30));
opportunities.push({
family_id: family.family_id,
opportunity_type: 'referral',
opportunity_category: 'peer_referral',
opportunity_description: `${family.family_name} is highly engaged with ${family.connection_count} connections. Recent positive experience ${daysSincePositive} days ago.`,
potential_value: 450, // Value of new enrollment
success_probability: 0.40, // 40% of asked champions refer someone
receptivity_score: receptivity,
effort_required: 15, // Quick ask
trigger_signals: {
engagement_score: family.engagement_score,
connection_count: family.connection_count,
days_since_positive: daysSincePositive
},
optimal_contact_window_start: new Date(),
optimal_contact_window_end: this.addDays(new Date(), 7)
});
}
return opportunities;
}
async mineChampionOpportunities() {
const opportunities = [];
// Mentor opportunities
const mentorCandidates = await this.db.query(`
SELECT
f.family_id,
f.family_name,
fem.engagement_score,
fem.participation_score,
COALESCE(vol.volunteer_hours, 0) as volunteer_hours,
COALESCE(ref.referral_count, 0) as referral_count
FROM families f
JOIN family_engagement_metrics fem ON f.family_id = fem.family_id
LEFT JOIN (
SELECT family_id, SUM(CAST(JSON_VALUE(metadata, '$.hours') AS DECIMAL)) as volunteer_hours
FROM interaction_log
WHERE interaction_type = 'volunteer_hours_logged'
GROUP BY family_id
) vol ON f.family_id = vol.family_id
LEFT JOIN (
SELECT family_id, COUNT(*) as referral_count
FROM interaction_log
WHERE interaction_type = 'referral_made'
GROUP BY family_id
) ref ON f.family_id = ref.family_id
WHERE fem.engagement_score > 85
AND (vol.volunteer_hours > 5 OR ref.referral_count > 1)
AND f.enrolled_current_semester = 1
`);
for (const family of mentorCandidates) {
// Find families they could mentor (new + struggling)
const menteeCount = await this.db.query(`
SELECT COUNT(*) as count
FROM families f2
JOIN family_engagement_metrics fem2 ON f2.family_id = fem2.family_id
WHERE fem2.engagement_score < 50
AND DATEDIFF(NOW(), f2.enrollment_date) < 180
`);
const potentialMentees = menteeCount[0].count;
if (potentialMentees > 0) {
opportunities.push({
family_id: family.family_id,
opportunity_type: 'champion',
opportunity_category: 'mentor',
opportunity_description: `${family.family_name} is high performer (${family.engagement_score} engagement, ${family.volunteer_hours} volunteer hours). Could mentor ${potentialMentees} struggling new families.`,
potential_value: potentialMentees * 200, // Value of retention improvement
success_probability: 0.70, // High performers usually say yes
receptivity_score: 0.80,
effort_required: 45, // Recruit + train + match
trigger_signals: {
engagement_score: family.engagement_score,
volunteer_hours: family.volunteer_hours,
referral_count: family.referral_count,
potential_mentees: potentialMentees
}
});
}
}
// Board member opportunities
const boardCandidates = await this.db.query(`
SELECT
f.family_id,
f.family_name,
fem.engagement_score,
DATEDIFF(NOW(), f.enrollment_date) / 365 as years_enrolled,
COALESCE(vol.volunteer_hours, 0) as volunteer_hours
FROM families f
JOIN family_engagement_metrics fem ON f.family_id = fem.family_id
LEFT JOIN (
SELECT family_id, SUM(CAST(JSON_VALUE(metadata, '$.hours') AS DECIMAL)) as volunteer_hours
FROM interaction_log
WHERE interaction_type = 'volunteer_hours_logged'
GROUP BY family_id
) vol ON f.family_id = vol.family_id
WHERE fem.engagement_score > 90
AND DATEDIFF(NOW(), f.enrollment_date) > 365
AND vol.volunteer_hours > 10
`);
for (const family of boardCandidates) {
opportunities.push({
family_id: family.family_id,
opportunity_type: 'champion',
opportunity_category: 'board_member',
opportunity_description: `${family.family_name} exceptional engagement (${family.engagement_score}), ${family.years_enrolled.toFixed(1)} years tenure, ${family.volunteer_hours} volunteer hours. Board candidate.`,
potential_value: 1000, // Leadership value
success_probability: 0.50,
receptivity_score: 0.75,
effort_required: 60,
trigger_signals: {
engagement_score: family.engagement_score,
years_enrolled: family.years_enrolled,
volunteer_hours: family.volunteer_hours
}
});
}
return opportunities;
}
async mineEngagementOpportunities() {
const opportunities = [];
// Receptivity window (recent positive spike)
const receptivityWindows = await this.db.query(`
SELECT
f.family_id,
f.family_name,
femh1.engagement_score as current_score,
femh2.engagement_score as previous_score,
(femh1.engagement_score - femh2.engagement_score) as score_change,
femh1.calculation_date as recent_date
FROM families f
JOIN family_engagement_metrics_history femh1 ON f.family_id = femh1.family_id
JOIN family_engagement_metrics_history femh2 ON f.family_id = femh2.family_id
AND femh2.calculation_date = DATE_SUB(femh1.calculation_date, INTERVAL 1 MONTH)
WHERE femh1.calculation_date = (
SELECT MAX(calculation_date) FROM family_engagement_metrics_history WHERE family_id = f.family_id
)
AND (femh1.engagement_score - femh2.engagement_score) > 15
AND femh1.engagement_score > 65
`);
for (const family of receptivityWindows) {
opportunities.push({
family_id: family.family_id,
opportunity_type: 'engagement',
opportunity_category: 'receptivity_window',
opportunity_description: `${family.family_name} engagement spiked ${family.score_change.toFixed(1)} points recently. High receptivity for deeper involvement.`,
potential_value: 300, // Increased retention probability
success_probability: 0.75,
receptivity_score: Math.min(1.0, family.score_change / 30),
effort_required: 20,
trigger_signals: {
score_change: family.score_change,
current_score: family.current_score,
previous_score: family.previous_score
},
optimal_contact_window_start: new Date(),
optimal_contact_window_end: this.addDays(new Date(), 14)
});
}
return opportunities;
}
async minePartnershipOpportunities() {
const opportunities = [];
// Families with professional services or assets
const assetFamilies = await this.db.query(`
SELECT
f.family_id,
f.family_name,
f.parent_profession,
f.special_resources,
fem.engagement_score
FROM families f
JOIN family_engagement_metrics fem ON f.family_id = fem.family_id
WHERE (
f.parent_profession IN ('photographer', 'graphic_designer', 'web_developer', 'teacher', 'lawyer', 'accountant')
OR f.special_resources LIKE '%community_center%'
OR f.special_resources LIKE '%meeting_space%'
)
AND fem.engagement_score > 60
`);
for (const family of assetFamilies) {
let category, description, value;
if (family.parent_profession === 'photographer') {
category = 'professional_service';
description = `${family.family_name} is professional photographer. Could provide event photography (valued at $500-1000 per event).`;
value = 750;
} else if (family.parent_profession === 'graphic_designer') {
category = 'professional_service';
description = `${family.family_name} is graphic designer. Could create marketing materials ($300-500 value).`;
value = 400;
} else if (family.special_resources && family.special_resources.includes('community_center')) {
category = 'physical_asset';
description = `${family.family_name} has access to community center. Could host events (valued at $200 per event).`;
value = 600; // 3 events per semester
}
if (category) {
opportunities.push({
family_id: family.family_id,
opportunity_type: 'partnership',
opportunity_category: category,
opportunity_description: description,
potential_value: value,
success_probability: 0.60,
receptivity_score: family.engagement_score / 100,
effort_required: 30,
trigger_signals: {
profession: family.parent_profession,
resources: family.special_resources,
engagement_score: family.engagement_score
}
});
}
}
return opportunities;
}
calculateOpportunityScore(opportunity) {
// Composite score formula
const score =
opportunity.potential_value *
opportunity.success_probability *
opportunity.receptivity_score *
(60 / opportunity.effort_required); // Efficiency factor (60 minutes baseline)
return Math.round(score * 100) / 100;
}
estimateExpansionProbability(family) {
// Factors that predict expansion success
let probability = 0.50; // Base rate
if (family.engagement_score > 85) probability += 0.20;
else if (family.engagement_score > 75) probability += 0.10;
if (family.financial_health_score > 85) probability += 0.15;
else if (family.financial_health_score > 75) probability += 0.08;
return Math.min(0.95, probability);
}
addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
async saveOpportunity(opportunity) {
// Check if similar opportunity already exists
const existing = await this.db.query(`
SELECT opportunity_id
FROM opportunities
WHERE family_id = ?
AND opportunity_category = ?
AND status IN ('new', 'in_progress')
`, [opportunity.family_id, opportunity.opportunity_category]);
if (existing.length === 0) {
await this.db.query(`
INSERT INTO opportunities (
family_id, opportunity_type, opportunity_category, opportunity_description,
potential_value, success_probability, receptivity_score, effort_required,
opportunity_score, trigger_signals,
optimal_contact_window_start, optimal_contact_window_end
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [
opportunity.family_id,
opportunity.opportunity_type,
opportunity.opportunity_category,
opportunity.opportunity_description,
opportunity.potential_value,
opportunity.success_probability,
opportunity.receptivity_score,
opportunity.effort_required,
opportunity.opportunity_score,
JSON.stringify(opportunity.trigger_signals),
opportunity.optimal_contact_window_start,
opportunity.optimal_contact_window_end
]);
}
}
async getOpportunityDashboard(topN = 20) {
return await this.db.query(`
SELECT
o.opportunity_id,
o.family_id,
f.family_name,
o.opportunity_type,
o.opportunity_category,
o.opportunity_description,
o.potential_value,
o.success_probability,
o.receptivity_score,
o.effort_required,
o.opportunity_score,
o.optimal_contact_window_end,
CASE
WHEN o.optimal_contact_window_end < CURRENT_DATE THEN 'expired'
WHEN o.optimal_contact_window_end <= DATE_ADD(CURRENT_DATE, INTERVAL 3 DAY) THEN 'urgent'
ELSE 'active'
END as urgency
FROM opportunities o
JOIN families f ON o.family_id = f.family_id
WHERE o.status = 'new'
ORDER BY o.opportunity_score DESC
LIMIT ?
`, [topN]);
}
}
module.exports = OpportunityMiningEngine;
Usage Example
const miner = new OpportunityMiningEngine(db);
// Mine all opportunities
const opportunities = await miner.mineOpportunities();
console.log(`\n=== DISCOVERED ${opportunities.length} OPPORTUNITIES ===\n`);
// Get top opportunities
const dashboard = await miner.getOpportunityDashboard(10);
dashboard.forEach((opp, i) => {
console.log(`${i+1}. ${opp.family_name} - ${opp.opportunity_category.toUpperCase()}`);
console.log(` Type: ${opp.opportunity_type}`);
console.log(` Description: ${opp.opportunity_description}`);
console.log(` Potential Value: $${opp.potential_value.toFixed(0)}`);
console.log(` Success Probability: ${(opp.success_probability * 100).toFixed(0)}%`);
console.log(` Opportunity Score: ${opp.opportunity_score.toFixed(2)}`);
console.log(` Urgency: ${opp.urgency}`);
console.log(``);
});
// Example output:
// === DISCOVERED 47 OPPORTUNITIES ===
//
// 1. Chen Family - MENTOR
// Type: champion
// Description: Chen Family is high performer (92 engagement, 12 volunteer hours). Could mentor 8 struggling new families.
// Potential Value: $1600
// Success Probability: 70%
// Opportunity Score: 672.00
// Urgency: active
//
// 2. Martinez Family - ADDITIONAL_ENROLLMENT
// Type: expansion
// Description: Martinez Family has 2 more eligible child(ren). High engagement and financial health.
// Potential Value: $900
// Success Probability: 75%
// Opportunity Score: 540.00
// Urgency: active
//
// 3. Johnson Family - PEER_REFERRAL
// Type: referral
// Description: Johnson Family is highly engaged with 7 connections. Recent positive experience 3 days ago.
// Potential Value: $450
// Success Probability: 40%
// Opportunity Score: 432.00
// Urgency: urgent
Variations
By Opportunity Type
Revenue-Focused: - Expansion (additional services/members) - Upsell (premium tiers) - Referral (new customers)
Relationship-Focused: - Champion development - Mentor recruitment - Community building
Resource-Focused: - Volunteer recruitment - Asset utilization - Partnership development
By Discovery Method
Rule-Based: - Explicit criteria (engagement > 80 + connections > 3) - Fast, interpretable - Requires domain knowledge
ML-Based: - Propensity models (likelihood to accept offer) - Lookalike modeling (similar to converters) - More accurate, less interpretable
Hybrid: - ML scores + rule filters - Best of both worlds
By Timing
Immediate: - Act within days - Receptivity windows - Time-sensitive
Near-Term (Weeks): - Standard opportunities - Planned outreach - Most opportunities
Long-Term (Months): - Strategic partnerships - Major commitments - Patient cultivation
Consequences
Benefits
1. Proactive growth Don't wait for opportunities to find you - discover them systematically.
2. Quantified priorities $1600 mentor opportunity > $450 referral opportunity (when success rates similar).
3. Optimal timing Strike during receptivity windows (within 7 days of positive experience).
4. Underutilized resources Discover photographer family, event space, professional services sitting idle.
5. Champion leverage High performers mentor 3-5 struggling families (multiplier effect).
6. ROI tracking Measure effort invested vs value realized.
Costs
1. Opportunity cost Time mining opportunities = less time solving problems.
2. Relationship risk Aggressive pursuit can feel transactional.
3. False positives Not every "opportunity" converts.
4. Complexity Many opportunity types to track and manage.
5. Timing pressure Receptivity windows create urgency.
6. Expectations Discovered opportunities create pressure to pursue.
Sample Code
Track opportunity conversion:
async function recordOpportunityOutcome(opportunityId, outcome, actualValue, notes) {
await db.query(`
UPDATE opportunities
SET
status = 'pursued',
outcome = ?,
actual_value = ?,
outcome_date = NOW(),
outcome_notes = ?
WHERE opportunity_id = ?
`, [outcome, actualValue, notes, opportunityId]);
// Update pattern statistics
const opp = await db.query(`
SELECT opportunity_category
FROM opportunities
WHERE opportunity_id = ?
`, [opportunityId]);
await db.query(`
UPDATE opportunity_patterns
SET
times_pursued = times_pursued + 1,
times_converted = times_converted + (CASE WHEN ? = 'converted' THEN 1 ELSE 0 END),
conversion_rate = (times_converted * 100.0) / times_pursued
WHERE opportunity_type = ?
`, [outcome, opp[0].opportunity_category]);
}
Known Uses
Homeschool Co-op Intelligence Platform - Mines 40-50 opportunities monthly - Pursues top 15-20 (resource constrained) - 65% conversion rate on pursued opportunities - Generated $12,000 additional revenue in one semester - Recruited 8 mentors (retained 18 struggling families)
E-Commerce: - Product recommendation engines - Upsell/cross-sell opportunities - Cart abandonment recovery
SaaS: - Expansion opportunities (add seats, features) - Upgrade propensity models - Champion/advocate programs
Financial Services: - Cross-sell opportunities (existing customers) - Wealth management upsell - Referral mining
Related Patterns
Requires: - Pattern 1: Universal Event Log - behavioral signals - Pattern 16: Cohort Discovery - high-value cohorts - Pattern 17: Anomaly Detection - positive anomalies = opportunities
Enables: - Pattern 15: Intervention Recommendation - opportunity-specific recommendations - Pattern 22: Progressive Escalation - opportunity cultivation sequences - Pattern 24: Template-Based Communication - outreach templates
Enhanced by: - Pattern 2: Behavioral Graph - connection-based opportunities - Pattern 12: Risk Stratification Models - propensity modeling
References
Academic Foundations
- Davenport, Thomas H., and Jeanne G. Harris (2007). Competing on Analytics. Harvard Business Press. ISBN: 978-1422103326
- Kumar, V., and Werner Reinartz (2018). Customer Relationship Management: Concept, Strategy, and Tools (3rd ed.). Springer. ISBN: 978-3662553817
- Gupta, Sunil, and Donald R. Lehmann (2005). Managing Customers as Investments. Wharton School Publishing. ISBN: 978-0132281638 - Customer lifetime value
- Fader, Peter S. (2012). Customer Centricity. Wharton Digital Press. ISBN: 978-1613630211
Cohort Analysis Methods
- Retention Cohorts: https://www.reforge.com/blog/retention-cohorts - Comprehensive guide by Reforge
- Amplitude Cohort Analysis: https://amplitude.com/blog/cohort-analysis - Best practices and examples
- Mixpanel Retention Report: https://mixpanel.com/topics/what-is-cohort-analysis/ - Cohort methodology
Propensity Modeling
- Logistic Regression for Propensity: Hosmer, D.W., Lemeshow, S., & Sturdivant, R.X. (2013). Applied Logistic Regression (3rd ed.). Wiley. ISBN: 978-0470582473
- Uplift Modeling: Rzepakowski, P., & Jaroszewicz, S. (2012). "Decision trees for uplift modeling with single and multiple treatments." Knowledge and Information Systems 32(2): 303-327.
- Propensity Score Matching: Rosenbaum, P.R., & Rubin, D.B. (1983). "The central role of the propensity score in observational studies for causal effects." Biometrika 70(1): 41-55.
Practical Implementation
- Python Lifetimes Library: https://github.com/CamDavidsonPilon/lifetimes - Customer lifetime value and retention
- Cohorts Package: https://github.com/grizzly-ridge/cohorts - Python cohort analysis
- pandas Groupby: https://pandas.pydata.org/docs/user_guide/groupby.html - Cohort calculations
- SQL Window Functions: https://www.postgresql.org/docs/current/tutorial-window.html - Cohort queries
Related Trilogy Patterns
- Pattern 8: Tier-Based Segmentation - Cohorts inform segmentation
- Pattern 11: Historical Pattern Matching - Match current cohort to historical cohorts
- Pattern 15: Intervention Recommendation Engine - Cohort-specific recommendations
- Volume 3, Pattern 6: Domain-Aware Validation - Domain-specific cohort definitions
Tools & Services
- Amplitude Cohorts: https://amplitude.com/cohorts - Behavioral cohort analysis
- Mixpanel Cohorts: https://mixpanel.com/cohorts - User cohort tracking
- Google Analytics Cohorts: https://support.google.com/analytics/answer/6074676 - GA cohort reports
- Looker: https://looker.com/ - SQL-based cohort analysis
- Mode Analytics: https://mode.com/ - Cohort SQL templates