Library for predicting menstrual cycles based on historical data
npm install cycliaWarning: This library is not a medical device and does not replace professional medical advice. All predictions are probabilistic and for informational purposes only.








Library for predicting menstrual cycles based on historical data. Provides accurate algorithms for forecasting upcoming periods, ovulation, and fertile windows.
- Multiple prediction strategies: Calendar and WMA (Weighted Moving Average)
- Data quality analysis: automatic assessment of cycle regularity
- Ovulation prediction: based on luteal phase
- Fertile window detection: for pregnancy planning
- Pregnancy tracking: current week, trimester, and milestones
- Confidence system: confidence score for each prediction
- Extensible architecture: ability to add custom algorithms
- TypeScript support: full typing out of the box
``bash`
npm install cyclia
`bash`
yarn add cyclia
`bash`
pnpm add cyclia
Detailed usage examples and API are described on the official website.
`typescript
import { PredictionEngine } from "cyclia";
const engine = new PredictionEngine({ strategy: "wma" });
const history = {
periodStarts: [
{ date: "2025-01-02" },
{ date: "2025-01-31" }, // 29 дней
{ date: "2025-03-01" }, // 29 дней
{ date: "2025-03-29" }, // 28 дней
{ date: "2025-04-27" }, // 29 дней
],
};
// Predict next period
const nextPeriod = engine.predictNextPeriod(history);
console.log("Next period:", nextPeriod.likely);
console.log("Confidence:", Math.round(nextPeriod.confidence * 100) + "%");
// Predict ovulation
const ovulation = engine.predictOvulation(history);
console.log("Ovulation:", ovulation.likely);
// Fertile window
const fertile = engine.predictFertileWindow(history);
console.log("Fertile window:", fertile.start, "-", fertile.end);
// Pregnancy prediction
const pregnancy = engine.predictPregnancy(new Date("2025-01-15"));
console.log("Pregnancy week:", pregnancy.currentWeek);
console.log("Due date:", pregnancy.dueDate);
`
`typescript`
// Get analytics summary
const summary = engine.analyze(history);
console.log("Average cycle length:", summary.averageCycle);
console.log("Regularity:", summary.irregular ? "Irregular" : "Regular");
console.log("Data quality:", summary.dataQuality);
Main class for working with predictions.
#### Constructor
`typescript`
new PredictionEngine(config?: PredictorConfig)
#### Configuration (PredictorConfig)
| Parameter | Type | Default | Description |
| --------------------------- | --------------------- | ------------ | --------------------------------------------- |
| strategy | 'wma' \| 'calendar' | 'wma' | Prediction strategy |lutealPhaseDays
| | number | 14 | Luteal phase duration |irregularityStdThreshold
| | number | 4 | Irregularity threshold (standard deviation) |minIntervalsForConfidence
| | number | 3 | Minimum intervals for confidence |timezone
| | string | Local TZ | Timezone for calculations |
#### Methods
##### analyze(history: HistoryInput): AnalyticsSummary
Analyzes historical data and returns a summary.
`typescript`
const summary = engine.analyze(history);
// {
// sampleSize: 4,
// cycleIntervals: [29, 29, 28, 29],
// averageCycle: 28.75,
// medianCycle: 29,
// stdCycle: 0.5,
// irregular: false,
// dataQuality: "medium"
// }
##### predictNextPeriod(history: HistoryInput): PredictionResult
Predicts the date of the next period.
`typescript`
const result = engine.predictNextPeriod(history);
// {
// likely: "2025-05-26",
// window: { start: "2025-05-24", end: "2025-05-28" },
// confidence: 0.8,
// notes: ["wma: weighted last intervals", "irregularity: ±2d"]
// }
##### predictOvulation(history: HistoryInput): PredictionResult
Predicts the date of ovulation.
##### predictFertileWindow(history: HistoryInput): FertileWindow
Determines the fertile window.
`typescript`
const fertile = engine.predictFertileWindow(history);
// {
// start: "2025-05-07",
// peak: "2025-05-12",
// end: "2025-05-13",
// confidence: 0.7,
// notes: ["ovulation = nextPeriod - lutealPhaseDays", "fertile = ovulation ± (−5..+1)"]
// }
##### predictPregnancy(lastPeriodDate: Date): PregnancyPrediction
Predicts pregnancy progress based on last period date.
`typescript`
const pregnancy = engine.predictPregnancy(new Date("2025-01-15"));
// {
// dueDate: Date,
// currentWeek: 12,
// currentTrimester: 1,
// daysRemaining: 196,
// milestones: ["Завершение первого триместра", "Снижение риска выкидыша"]
// }
##### predictPregnancyFromHistory(history: HistoryInput): PregnancyPrediction
Predicts pregnancy progress using the last period date from cycle history.
`typescript`
const pregnancy = engine.predictPregnancyFromHistory(history);
// Uses the most recent period date from history
#### HistoryInput
`typescript
interface HistoryInput {
periodStarts: PeriodStartRecord[];
}
interface PeriodStartRecord {
date: ISODate; // YYYY-MM-DD
}
`
#### PredictionResult
`typescript`
interface PredictionResult {
likely: ISODate | null; // Вероятная дата
window: DateRange | null; // Окно прогноза
confidence: number; // Уверенность (0-1)
notes: string[]; // Комментарии
}
#### FertileWindow
`typescript`
interface FertileWindow {
start: ISODate; // Начало фертильного окна
peak: ISODate; // Пик фертильности (овуляция)
end: ISODate; // Конец фертильного окна
confidence: number; // Уверенность
notes: string[]; // Комментарии
}
#### PregnancyPrediction
`typescript`
interface PregnancyPrediction {
dueDate: Date; // Предполагаемая дата родов
currentWeek: number; // Текущая неделя беременности
currentTrimester: number; // Текущий триместр (1-3)
daysRemaining: number; // Дней до родов
milestones: string[]; // Вехи развития для текущей недели
}
#### Prediction Hook
`typescript
import { useState, useEffect } from "react";
import { PredictionEngine, HistoryInput } from "cyclia";
const useCyclePredictions = (history: HistoryInput) => {
const [predictions, setPredictions] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (history.periodStarts.length >= 2) {
setLoading(true);
setError(null);
try {
const engine = new PredictionEngine({ strategy: "wma" });
const nextPeriod = engine.predictNextPeriod(history);
const ovulation = engine.predictOvulation(history);
const fertile = engine.predictFertileWindow(history);
const summary = engine.analyze(history);
setPredictions({ nextPeriod, ovulation, fertile, summary });
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
}, [history]);
return { predictions, loading, error };
};
`
#### Calendar Component
`typescript
import React from 'react';
import { useCyclePredictions } from './hooks/useCyclePredictions';
interface CycleCalendarProps {
periodHistory: HistoryInput;
}
const CycleCalendar: React.FC
const { predictions, loading, error } = useCyclePredictions(periodHistory);
if (loading) return
return (
Средний цикл: {predictions.summary.averageCycle} дней
Регулярность: {predictions.summary.irregular ? 'Нерегулярный' : 'Регулярный'}
Качество данных: {predictions.summary.dataQuality}
$3
#### Composition API
`typescript
Анализируем данные...
Ошибка: {{ error }}
Следующая менструация
{{ predictions.nextPeriod.likely }}
Уверенность: {{ Math.round(predictions.nextPeriod.confidence * 100) }}%
Овуляция
{{ predictions.ovulation.likely }}
Фертильное окно
{{ predictions.fertile.start }} - {{ predictions.fertile.end }}
`
$3
#### Service
`typescript
import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable } from "rxjs";
import {
PredictionEngine,
type HistoryInput,
type PredictionResult,
} from "cyclia";export interface CyclePredictions {
nextPeriod: PredictionResult;
ovulation: PredictionResult;
fertile: any;
summary: any;
}
@Injectable({
providedIn: "root",
})
export class CyclePredictionService {
private engine = new PredictionEngine({ strategy: "wma" });
private predictionsSubject = new BehaviorSubject(
null
);
private loadingSubject = new BehaviorSubject(false);
predictions$ = this.predictionsSubject.asObservable();
loading$ = this.loadingSubject.asObservable();
calculatePredictions(history: HistoryInput): void {
if (history.periodStarts.length < 2) {
this.predictionsSubject.next(null);
return;
}
this.loadingSubject.next(true);
try {
const predictions = {
nextPeriod: this.engine.predictNextPeriod(history),
ovulation: this.engine.predictOvulation(history),
fertile: this.engine.predictFertileWindow(history),
summary: this.engine.analyze(history),
};
this.predictionsSubject.next(predictions);
} catch (error) {
console.error("Ошибка расчета прогнозов:", error);
} finally {
this.loadingSubject.next(false);
}
}
}
`
#### Component
`typescript
import { Component, Input, OnInit } from "@angular/core";
import {
CyclePredictionService,
type CyclePredictions,
} from "./cycle-prediction.service";
import { type HistoryInput } from "cyclia";@Component({
selector: "app-cycle-predictor",
template:
{{ predictions.nextPeriod.likely }}
Уверенность:
{{ predictions.nextPeriod.confidence * 100 | number: "1.0-0" }}%
{{ predictions.ovulation.likely }}
{{ predictions.fertile.start }} - {{ predictions.fertile.end }}
Пик: {{ predictions.fertile.peak }}
,
styleUrls: ["./cycle-predictor.component.scss"],
})
export class CyclePredictorComponent implements OnInit {
@Input() periodHistory!: HistoryInput; predictions$ = this.predictionService.predictions$;
loading$ = this.predictionService.loading$;
constructor(private predictionService: CyclePredictionService) {}
ngOnInit() {
this.predictionService.calculatePredictions(this.periodHistory);
}
}
`
📱 Mobile Applications
$3
`typescript
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { PredictionEngine, type HistoryInput } from 'cyclia';interface CyclePredictorProps {
periodHistory: HistoryInput;
}
const CyclePredictor: React.FC = ({ periodHistory }) => {
const [predictions, setPredictions] = React.useState(null);
const [loading, setLoading] = React.useState(false);
React.useEffect(() => {
if (periodHistory.periodStarts.length >= 2) {
setLoading(true);
const engine = new PredictionEngine({ strategy: 'wma' });
const nextPeriod = engine.predictNextPeriod(periodHistory);
const ovulation = engine.predictOvulation(periodHistory);
const fertile = engine.predictFertileWindow(periodHistory);
setPredictions({ nextPeriod, ovulation, fertile });
setLoading(false);
}
}, [periodHistory]);
if (loading) {
return (
Анализируем данные...
);
}
if (!predictions) {
return (
Недостаточно данных для прогноза
);
}
return (
Следующая менструация
{predictions.nextPeriod.likely}
Уверенность: {Math.round(predictions.nextPeriod.confidence * 100)}%
Овуляция
{predictions.ovulation.likely}
Фертильное окно
{predictions.fertile.start} - {predictions.fertile.end}
);
};
const styles = StyleSheet.create({
container: {
padding: 16,
},
card: {
backgroundColor: '#fff',
padding: 16,
marginBottom: 12,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
date: {
fontSize: 16,
color: '#333',
},
confidence: {
fontSize: 14,
color: '#666',
marginTop: 4,
},
range: {
fontSize: 16,
color: '#333',
},
});
export default CyclePredictor;
`
🔧 Extending Functionality
$3
`typescript
import {
BaseRule,
type HistoryInput,
type PredictionResult,
type PredictorConfig,
type AnalyticsSummary,
} from "cyclia";class MLPredictionRule extends BaseRule {
readonly id = "ml-prediction";
predictNextPeriod(
history: HistoryInput,
cfg: Required,
summary: AnalyticsSummary
): PredictionResult {
// Ваша ML логика здесь
const mlPrediction = this.runMLModel(history, summary);
return {
likely: mlPrediction.date,
window: {
start: mlPrediction.startDate,
end: mlPrediction.endDate,
},
confidence: mlPrediction.confidence,
notes: ["ML-based prediction",
model: ${mlPrediction.modelName}],
};
} private runMLModel(history: HistoryInput, summary: AnalyticsSummary) {
// Интеграция с вашей ML моделью
// Например, TensorFlow.js, ONNX.js, или внешний API
return {
date: "2025-06-15",
startDate: "2025-06-13",
endDate: "2025-06-17",
confidence: 0.85,
modelName: "cycle-predictor-v1",
};
}
}
// Register in engine
const engine = new PredictionEngine();
engine.register(new MLPredictionRule());
// Usage
const result = engine.predictNextPeriod(history);
`
$3
`typescript
class EnhancedPredictionEngine extends PredictionEngine {
async predictWithExternalFactors(history: HistoryInput) {
const basePrediction = this.predictNextPeriod(history); // Получаем дополнительные факторы
const weatherData = await this.getWeatherData();
const stressLevel = await this.getStressLevel();
const sleepQuality = await this.getSleepQuality();
// Корректируем прогноз
return this.adjustPrediction(basePrediction, {
weather: weatherData,
stress: stressLevel,
sleep: sleepQuality,
});
}
private adjustPrediction(basePrediction: PredictionResult, factors: any) {
let adjustedConfidence = basePrediction.confidence;
const notes = [...basePrediction.notes];
// Корректировка на основе факторов
if (factors.stress > 7) {
adjustedConfidence *= 0.9;
notes.push("high stress detected");
}
if (factors.sleep < 6) {
adjustedConfidence *= 0.95;
notes.push("poor sleep quality");
}
return {
...basePrediction,
confidence: Math.max(0.1, adjustedConfidence),
notes,
};
}
}
`
🗄️ Working with Data
$3
`typescript
class CycleDataService {
private readonly STORAGE_KEY = "cycle_history";
private readonly PREDICTIONS_KEY = "cycle_predictions"; async savePeriodDate(date: string): Promise {
const history = await this.loadHistory();
history.periodStarts.push({ date });
await this.saveHistory(history);
// Пересчитываем прогнозы
const engine = new PredictionEngine();
const predictions = engine.predictNextPeriod(history);
await this.savePredictions(predictions);
}
async loadHistory(): Promise {
const stored = localStorage.getItem(this.STORAGE_KEY);
return stored ? JSON.parse(stored) : { periodStarts: [] };
}
async saveHistory(history: HistoryInput): Promise {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(history));
}
async getCurrentPredictions(): Promise {
const history = await this.loadHistory();
const engine = new PredictionEngine();
return {
nextPeriod: engine.predictNextPeriod(history),
ovulation: engine.predictOvulation(history),
fertile: engine.predictFertileWindow(history),
summary: engine.analyze(history),
};
}
}
`
$3
`typescript
class DataExportService {
exportToCSV(history: HistoryInput): string {
const headers = ["Date", "Cycle Length"];
const rows = []; for (let i = 1; i < history.periodStarts.length; i++) {
const prev = new Date(history.periodStarts[i - 1].date);
const curr = new Date(history.periodStarts[i].date);
const cycleLength = Math.round(
(curr.getTime() - prev.getTime()) / (1000 60 60 * 24)
);
rows.push([history.periodStarts[i].date, cycleLength]);
}
return [headers, ...rows].map((row) => row.join(",")).join("\n");
}
exportToJSON(history: HistoryInput, predictions: any): string {
return JSON.stringify(
{
history,
predictions,
exportDate: new Date().toISOString(),
},
null,
2
);
}
}
`
🧪 Testing
$3
`typescript
import { PredictionEngine } from "cycle-predictor-core";describe("PredictionEngine", () => {
let engine: PredictionEngine;
beforeEach(() => {
engine = new PredictionEngine({ strategy: "wma" });
});
it("should predict next period correctly", () => {
const history = {
periodStarts: [
{ date: "2025-01-01" },
{ date: "2025-01-30" }, // 29 дней
{ date: "2025-02-28" }, // 29 дней
],
};
const result = engine.predictNextPeriod(history);
expect(result.likely).toBeTruthy();
expect(result.confidence).toBeGreaterThan(0);
expect(result.confidence).toBeLessThanOrEqual(1);
});
it("should handle irregular cycles", () => {
const history = {
periodStarts: [
{ date: "2025-01-01" },
{ date: "2025-01-25" }, // 24 дня
{ date: "2025-02-28" }, // 33 дня
{ date: "2025-03-25" }, // 25 дней
],
};
const summary = engine.analyze(history);
expect(summary.irregular).toBe(true);
});
});
`
$3
`typescript
describe("Cycle Prediction Integration", () => {
it("should provide consistent predictions across methods", () => {
const engine = new PredictionEngine();
const history = {
periodStarts: [
{ date: "2025-01-01" },
{ date: "2025-01-30" },
{ date: "2025-02-28" },
{ date: "2025-03-29" },
],
}; const nextPeriod = engine.predictNextPeriod(history);
const ovulation = engine.predictOvulation(history);
const fertile = engine.predictFertileWindow(history);
// Проверяем логическую связь между прогнозами
expect(nextPeriod.likely).toBeTruthy();
expect(ovulation.likely).toBeTruthy();
expect(fertile.start).toBeTruthy();
expect(fertile.end).toBeTruthy();
});
});
`
🚀 Performance
$3
`typescript
class OptimizedPredictionEngine extends PredictionEngine {
private cache = new Map(); predictNextPeriod(history: HistoryInput): PredictionResult {
const cacheKey = this.generateCacheKey(history);
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
const result = super.predictNextPeriod(history);
this.cache.set(cacheKey, result);
return result;
}
private generateCacheKey(history: HistoryInput): string {
return JSON.stringify(history.periodStarts.map((p) => p.date));
}
clearCache(): void {
this.cache.clear();
}
}
`
$3
`typescript
// worker.ts
import { PredictionEngine } from "cyclia";self.onmessage = (event) => {
const { history, config } = event.data;
try {
const engine = new PredictionEngine(config);
const predictions = {
nextPeriod: engine.predictNextPeriod(history),
ovulation: engine.predictOvulation(history),
fertile: engine.predictFertileWindow(history),
summary: engine.analyze(history),
};
self.postMessage({ success: true, predictions });
} catch (error) {
self.postMessage({ success: false, error: error.message });
}
};
// main.ts
const worker = new Worker("./worker.js");
worker.onmessage = (event) => {
if (event.data.success) {
setPredictions(event.data.predictions);
} else {
setError(event.data.error);
}
};
const calculatePredictions = (history: HistoryInput) => {
worker.postMessage({ history, config: { strategy: "wma" } });
};
`
🔒 Security and Privacy
$3
`typescript
class PrivacyAwarePredictionEngine extends PredictionEngine {
// Все вычисления происходят локально
// Данные не отправляются на сервер predictNextPeriod(history: HistoryInput): PredictionResult {
// Локальная обработка
return super.predictNextPeriod(history);
}
}
`
$3
`typescript
import CryptoJS from "crypto-js";class EncryptedDataService {
private readonly SECRET_KEY = "your-secret-key";
encryptData(data: any): string {
return CryptoJS.AES.encrypt(
JSON.stringify(data),
this.SECRET_KEY
).toString();
}
decryptData(encryptedData: string): any {
const bytes = CryptoJS.AES.decrypt(encryptedData, this.SECRET_KEY);
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
}
async saveEncryptedHistory(history: HistoryInput): Promise {
const encrypted = this.encryptData(history);
localStorage.setItem("encrypted_cycle_data", encrypted);
}
async loadEncryptedHistory(): Promise {
const encrypted = localStorage.getItem("encrypted_cycle_data");
if (!encrypted) return { periodStarts: [] };
return this.decryptData(encrypted);
}
}
`
📊 Monitoring and Analytics
$3
`typescript
class AnalyticsService {
logPredictionRequest(history: HistoryInput, predictions: any): void {
const event = {
timestamp: new Date().toISOString(),
dataPoints: history.periodStarts.length,
predictionType: "cycle",
confidence: predictions.nextPeriod.confidence,
irregular: predictions.summary.irregular,
}; // Отправка в аналитическую систему
this.sendAnalytics(event);
}
private sendAnalytics(event: any): void {
// Интеграция с Google Analytics, Mixpanel, etc.
if (typeof gtag !== "undefined") {
gtag("event", "cycle_prediction", event);
}
}
}
`
🤝 Contributing
$3
`bash
git clone https://github.com/NextFutureHub/cyclia.git
cd cyclia
npm install
npm run test
`
$3
`
cyclia/
├── src/
│ ├── core/ # Core logic
│ ├── plugins/ # Prediction algorithms
│ ├── utils/ # Utilities
│ ├── types.ts # TypeScript types
│ └── index.ts # Entry point
├── tests/ # Tests
├── docs/ # Documentation
└── examples/ # Usage examples
`
$3
1. Create a new class that extends
BaseRule
2. Implement the predictNextPeriod and predictFertility methods
3. Add tests
4. Update documentation`typescript
// src/plugins/yourAlgorithm.ts
import { BaseRule } from "./baseRule";export class YourAlgorithm extends BaseRule {
readonly id = "your-algorithm";
predictNextPeriod(history, cfg, summary) {
// Your logic here
return {
likely: "2025-06-15",
window: { start: "2025-06-13", end: "2025-06-17" },
confidence: 0.8,
notes: ["your algorithm"],
};
}
}
``1. Fork the repository
2. Create a branch for your feature
3. Add tests
4. Update documentation
5. Submit a Pull Request
- [ ] Machine learning for improved accuracy
- [ ] Integration with wearable devices
- [ ] Multi-user support
- [ ] API for server-side processing
- [ ] Plugins for popular frameworks
- [ ] Integration with medical systems
- v1.0.0 - Basic functionality
- Email: support@cycle-predictor.com
MIT License - see LICENSE for details.
⭐ If you like this library, consider giving it a star on GitHub!
---
Important: This library is for informational purposes only and does not replace medical advice. Always consult your doctor for health matters.