Rule-based maritime voyage profit & risk estimation with FastAPI + MCP-style tool orchestration.
Maritime shipping companies often estimate voyage economics manually in spreadsheets—slow, error-prone, and opaque. This system automates voyage estimation using deterministic business rules, not probabilistic ML. It calculates distance, fuel cost, profit margins, and provides GO/NO-GO decisions based on configurable profit zones and risk thresholds.
Built to demonstrate: business-rule modeling, MCP-style tool orchestration (distance → fuel → decision), failure-safe flows with manual overrides, and clean full-stack integration.
MCP-Style Tool Orchestration
Three loosely-coupled tools (Distance, Fuel, Decision) executed in sequence. Each tool can fail independently; the system retries once, then prompts for manual input if needed. Clean separation allows individual tool testing and future replacement (e.g., hooking to real port APIs).
Profit & Risk Analysis
Implements profit zones (≥15% = STRONG GO, 5–15% = CAUTION, 0–5% = RISKY, <0 = DO NOT SAIL). Flags fuel-heavy voyages (>65% expense = risky, >75% = reject), high-speed warnings, port-heavy routes, and freight-below-fuel scenarios.
Business-Safe Failure Handling
Distance tool fails (route not in map) → retry → user enters manual distance. Fuel tool fails (invalid inputs) → retry → user enters manual fuel cost. Decision engine fails → system returns raw numbers + "MANUAL REVIEW REQUIRED". Never silent failures.
Production-Like UX
Frontend form validates empty fields, negatives, and fuel price = 0 (hard reject). Warnings for speed >20 or freight <$50k (soft). Color-coded banners (yellow = tool failure, red = loss voyage, orange = risky). Dashboard shows key metrics, profit zone tag, fuel/port expense ratios, and actionable suggestions.
- Backend: Python 3.10+, FastAPI, Uvicorn, Pydantic v2
- Frontend: HTML5, CSS3, Vanilla JavaScript (no React/Vue—intentional for simplicity)
- Architecture: MCP-style toolchain with retry/fallback logic, RESTful API, CORS-enabled
- Style: Deterministic calculations only. No ML, no randomness, no black-box inference. Pure rule-based "agentic" decisions.
- User Input: Frontend form collects ports, speed, fuel consumption, fuel price, port charges, freight income, currency. Validates on submit.
- API Call:
POST /estimatesends JSON payload to FastAPI backend. - Distance Tool: Looks up nautical miles from predefined route map. If route missing → raises
DistanceToolFailure→ retry once → still fails → returns 400 withneeds_manual_distance: true. - Fuel Tool: Calculates voyage days (distance / speed), total fuel used (consumption × days), total fuel cost. If invalid inputs → raises
FuelToolFailure→ retry → fails → returns 400 withneeds_manual_fuel_cost: true. - Decision Engine: Computes profit %, maps to profit zone, calculates fuel/port expense ratios, generates decision tag and business suggestions. If failure → fallback to raw numbers +
final_decision: "MANUAL REVIEW REQUIRED". - Response: Backend returns JSON with all metrics, warnings, risk flags, banners. Frontend renders dashboard with color-coded decision tag, key numbers, and suggestions list.
Request Body:
{
"start_port": "Singapore",
"end_port": "Rotterdam",
"speed": 14.5,
"fuel_consumption": 25.0,
"fuel_price": 580.0,
"port_charges": 120000.0,
"freight_income": 320000.0,
"currency": "USD",
"manual_distance": null,
"manual_fuel_cost": null
}Response Body (Success):
{
"distance_nm": 9800.0,
"voyage_days": 28.74,
"total_fuel_used": 718.5,
"total_fuel_cost": 416730.0,
"total_expense": 536730.0,
"net_profit": -216730.0,
"profit_percent": -40.37,
"profit_zone": "DO NOT SAIL",
"fuel_percent_of_expense": 77.65,
"port_percent_of_expense": 22.35,
"final_decision": "DO NOT SAIL",
"suggestions": [
"Renegotiate freight or adjust fuel plan",
"Revisit commercial terms or routing"
],
"risk_flags": [
"Voyage loss expected",
"Fuel cost dominates (>75% of expense)",
"Freight below fuel cost"
],
"warnings": [],
"banners": ["Loss-making voyage detected"],
"needs_manual_distance": false,
"needs_manual_fuel_cost": false,
"currency": "USD"
}Response Body (Tool Failure):
{
"distance_nm": null,
"voyage_days": null,
"...": "...",
"final_decision": "MANUAL INPUT REQUIRED",
"suggestions": ["Enter distance manually and resubmit"],
"banners": ["Distance tool failed. Provide manual distance to continue."],
"needs_manual_distance": true,
"needs_manual_fuel_cost": false
}| Zone | Profit % | Decision Tag |
|---|---|---|
| STRONG GO | ≥15% | Green light |
| GO WITH CAUTION | 5% to <15% | Proceed carefully |
| RISKY | 0% to <5% | Marginal economics |
| DO NOT SAIL | <0% | Loss-making |
- Loss voyage (profit < 0): Automatic
DO NOT SAIL. - Fuel >75% of expense:
DO NOT SAIL(unless freight renegotiated). - Fuel >65% of expense: Tag as
RISKY, suggest slow steaming or renegotiation. - Speed >18 knots: Warning about high fuel burn.
- Port charges >20% of expense: Flag port-heavy route.
- Freight < Fuel cost: Economically weak voyage; suggest freight renegotiation.
Generated dynamically based on flags:
- Fuel-heavy → "Monitor bunker market and consider slow steaming"
- Low profit → "Seek additional cargo or adjust port calls"
- Loss → "Revisit commercial terms or routing"
- Freight < Fuel → "Renegotiate freight rate or adjust speed"
Distance Tool Failure
Route not in predefined map → Retry once → Still fails → Return needs_manual_distance: true + yellow banner. User enters distance in frontend, resubmits with manual_distance field populated.
Fuel Tool Failure
Invalid speed/consumption/price (e.g., zero, negative) → Retry once → Still fails → Return needs_manual_fuel_cost: true + red banner. User enters fuel cost manually, resubmits.
Decision Engine Failure
Rare edge cases (e.g., zero expense) → Retry once → Falls back to raw calculations + final_decision: "MANUAL REVIEW REQUIRED" + gray banner. User reviews raw numbers offline.
This is intentional design for safety, not a bug. The system never silently fails or returns nonsense data.
- Python 3.10 or higher (Download here)
- Git (Download here)
- Modern web browser (Chrome, Firefox, Edge, Safari)
Step 1: Clone the repository
git clone https://github.com/soulrahulrk/AI-Voyage-Estimation-Decision-System.git
cd AI-Voyage-Estimation-Decision-SystemStep 2: Create and activate virtual environment
On Windows (PowerShell):
python -m venv .venv
.\.venv\Scripts\Activate.ps1On macOS/Linux (Bash/Zsh):
python3 -m venv .venv
source .venv/bin/activateStep 3: Install dependencies
pip install -r requirements.txtThis installs FastAPI, Uvicorn, and Pydantic. Takes ~30 seconds.
Step 4: Start the servers
Simply double-click start_all.bat in the project root folder, or run:
.\start_all.batThis will:
- ✅ Start the backend server on port 8000
- ✅ Start the frontend server on port 5500
- ✅ Automatically open the application in your default browser
- ✅ Keep both servers running in separate windows
To stop: Close the two command windows that opened.
Terminal 1 - Backend:
uvicorn backend.main:app --reload --host 0.0.0.0 --port 8000Terminal 2 - Frontend:
# Windows
cd frontend
python -m http.server 5500
# macOS/Linux
cd frontend
python3 -m http.server 5500✅ Backend: http://localhost:8000 (API docs: http://localhost:8000/docs)
✅ Frontend: http://localhost:5500
Step 5: Open in browser
Navigate to http://localhost:5500 and you'll see the voyage estimation form.
"python: command not found"
- On macOS/Linux, use
python3instead ofpython - Verify installation:
python --versionorpython3 --version
"cannot be loaded because running scripts is disabled" (Windows PowerShell)
Run this once as Administrator:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUserPort already in use
- Backend (8000): Change to
--port 8001in the uvicorn command - Frontend (5500): Change to
python -m http.server 5501
"ModuleNotFoundError: No module named 'fastapi'"
- Make sure virtual environment is activated (you should see
(.venv)in your terminal) - Re-run
pip install -r requirements.txt
Frontend not displaying / Connection refused
- Use
start_all.bat(Windows) for persistent server windows - Verify both servers are running:
netstat -ano | findstr "8000 5500" - Check firewall isn't blocking local ports
Input:
- Start Port:
Singapore - End Port:
Rotterdam - Speed:
14knots - Fuel Consumption:
25tons/day - Fuel Price:
$580per ton - Port Charges:
$120,000 - Freight Income:
$320,000 - Currency:
USD
Expected Result: DO NOT SAIL (loss-making, fuel >75% expense).
Problem: Shipping companies manually estimate voyage economics in Excel. Formulas hidden, errors frequent, risk analysis inconsistent across teams. No audit trail.
Solution: This system centralizes business rules, automates calculations, surfaces risk flags immediately, and provides audit-ready decision logs. Ship operators get instant GO/NO-GO decisions with commercial justification.
Future Extensions: Plug into real port distance APIs (e.g., Sea-Distances.org), integrate live bunker prices, store voyage history for trend analysis, add LLM-based "ask why" explanations for decisions.
Business-Rule Modeling
Translated domain knowledge (profit zones, fuel thresholds, port costs) into clean, testable logic. Not just CRUD—actual business intelligence.
Backend Architecture & Tool Orchestration
Designed MCP-style toolchain with clear separation of concerns. Each tool is independently testable, replaceable, and has defined failure modes. Retry logic, fallback paths, and manual override support built in from day one.
Failure-Safe Design
Every tool can fail; system handles it gracefully. No silent errors, no "contact admin" dead ends. User always has a path forward (manual input or raw data review).
Full-Stack Integration
Backend (FastAPI + Pydantic) talks to frontend (vanilla JS) via clean REST API. Frontend validates, shows contextual banners, renders dashboard. End-to-end flow from form to decision in <1 second.
Production Thinking
CORS configured, input validation strict, error messages actionable, API responses structured for frontend consumption. Not a toy—architected like something I'd deploy.
- Hard-coded distance map: 18 predefined routes. Missing routes fail (by design, but limits real use).
- No persistence: No database, no auth, no voyage history. Every request is stateless.
- Demo-scale only: Not load-tested, no rate limiting, no multi-tenancy.
- Real port distance data: Hook into Sea-Distances.org API or use Haversine formula + port coordinates.
- Voyage history storage: Add PostgreSQL/MongoDB to log decisions, enable trend analysis ("we rejected 12 voyages last month due to fuel costs").
- Live bunker prices: Integrate Bunkerworld or similar API for real-time fuel pricing.
- LLM-based assistant: Add "Explain this decision" button that uses GPT-4 to generate natural-language justification from raw data + rules.
- Multi-currency support: Expand beyond USD/EUR/INR with live FX rates.
voyage-ship-calculator/
├── backend/
│ ├── __init__.py
│ ├── main.py # FastAPI app, endpoints, orchestration logic
│ ├── distance_tool.py # Distance lookup with predefined route map
│ ├── fuel_tool.py # Fuel & cost calculations
│ ├── decision_engine.py # Profit zones, rules, decision tag generation
│ └── test_decision_engine.py # Unit tests for decision logic (9 tests)
├── frontend/
│ ├── index.html # Form + dashboard UI
│ ├── style.css # Responsive layout, color-coded banners
│ └── script.js # Form validation, API calls, dashboard rendering
├── start_all.bat # Windows: Launch both servers + open browser
├── start_backend.bat # Windows: Backend server only
├── start_frontend.bat # Windows: Frontend server only
├── requirements.txt # FastAPI, Uvicorn, Pydantic, pytest
├── .gitignore # Python, venv, IDE exclusions
└── README.md
MIT License. Free to use, modify, and learn from.
Built by Rahul as a portfolio demo project.
GitHub: [Your GitHub Profile]
LinkedIn: [Your LinkedIn Profile]
For questions or collaboration: [Your Email]