ROI Methodology Whitepaper
The complete mathematical framework behind GigAnalytics's income stream analysis — how we calculate true hourly rates, acquisition ROI, platform fee impact, and cross-stream comparisons.
Contents
- 1.The problem with traditional income tracking
- 2.Core definitions and data model
- 3.Net revenue calculation
- 4.True hourly rate
- 5.Acquisition ROI
- 6.Cross-stream comparison index
- 7.Income goal projection
- 8.Statistical significance in pricing experiments
- 9.Design decisions and tradeoffs
- 10.Limitations and known edge cases
1. The Problem with Traditional Income Tracking
Most freelancers track income in one of two ways: a spreadsheet summing gross revenue, or an accounting tool (Wave, QuickBooks) that records transactions but provides no time-adjusted analysis. Both approaches share the same fundamental flaw: they confuse revenue with value.
A freelancer with four income streams might see this in their accounting tool:
Now add time data:
The digital products stream — generating the least absolute revenue — has by far the highest ROI per hour. Without time-adjusted analysis, this freelancer might deprioritize their highest-value activity. GigAnalytics is built to surface this inversion systematically.
2. Core Definitions and Data Model
GigAnalytics operates on three first-class entities:
Income Stream (stream)
A named, color-coded grouping of transactions and time entries that represents a single income source. Examples: "Consulting – Acme Corp", "Upwork Design Work", "Gumroad Templates". A stream has a platform tag (stripe, paypal, upwork, csv, manual) and an optional acquisition cost budget.
Transaction
A single earned payment event. Fields: gross_amount (before fees), fee_amount (platform or payment processor fees), net_amount (gross minus fees), transaction_date, stream_id. Transactions are imported from Stripe, PayPal, Upwork exports, or CSV.
Time Entry
A logged work session associated with a stream. Fields: started_at, stopped_at (or duration_minutes), stream_id, source (timer, calendar, manual). The duration represents billable/attributable work time for the stream during that session.
All calculations are performed over a configurable date range window (default: 90 days). Transactions and time entries outside the window are excluded. The window is applied independently to each stream.
3. Net Revenue Calculation
Gross revenue is what platforms report. Net revenue is what you actually keep. GigAnalytics uses net revenue — not gross — as the basis for all ROI calculations. This matters: Upwork charges a 10–20% service fee; Stripe charges 2.9% + $0.30; PayPal varies.
Net Revenue (single transaction)
formulanet_amount = gross_amount - fee_amount Where fee_amount is: - Stripe: gross_amount × 0.029 + 0.30 (if not already in export) - PayPal: gross_amount × 0.0299 + 0.49 (if not in export) - Upwork: gross_amount × fee_rate (10-20% per contract tier) - CSV: use fee_amount column if present, else 0
If you import from Stripe Balance History CSV, fee_amount is already correct in the export. GigAnalytics reads it directly.
Stream Net Revenue (over date window)
formulastream_net_revenue(stream, from, to) =
SUM(t.net_amount)
FOR t IN transactions
WHERE t.stream_id = stream.id
AND t.transaction_date BETWEEN from AND to4. True Hourly Rate
The true hourly rate is the central metric of GigAnalytics. It answers: "Given everything I earned and everything I spent on this stream, what did I actually make per hour of work?"
True Hourly Rate
formulatrue_hourly_rate(stream, from, to) =
(stream_net_revenue(stream, from, to) - acquisition_costs(stream, from, to))
÷ total_hours_worked(stream, from, to)
Where:
acquisition_costs = SUM of ad spend, platform subscription fees,
tools, and other costs attributed to the stream
total_hours_worked = SUM(time_entry.duration_minutes) / 60
FOR time entries in the date windowIf no acquisition costs exist for a stream, the formula simplifies to net_revenue / hours.
Why subtract acquisition costs before dividing?
A common mistake is to calculate hourly rate on gross revenue, then separately track ad spend. This understates the true cost of customer acquisition. If you spent $200 on ads to land a $800 project that took 4 hours, the real rate is ($800 - $200) / 4 = $150/hr — not $200/hr. GigAnalytics always computes post-acquisition rates.
Portfolio Hourly Rate (all streams)
formulaportfolio_hourly_rate(from, to) = SUM(stream_net_revenue(s, from, to) - acquisition_costs(s, from, to)) ÷ SUM(total_hours_worked(s, from, to)) FOR s IN active_streams
This is a revenue-weighted average, not an arithmetic mean of stream rates.
5. Acquisition ROI
Acquisition ROI measures the return on money spent to generate income from a stream. This includes ad spend, platform fees, tools subscriptions, and any other costs you explicitly attribute to a stream.
Acquisition ROI
formulaacquisition_roi(stream, from, to) = (stream_net_revenue(stream, from, to) - acquisition_costs(stream, from, to)) ÷ acquisition_costs(stream, from, to) × 100 Result is a percentage. A value > 0% means the stream is net-positive after acquisition costs. A value of 400% means you earned $5 for every $1 spent on acquisition.
Streams with no acquisition costs show 'N/A' for this metric, not ∞%, to avoid misleading comparisons.
Cost per Dollar Earned (CDE)
formulacost_per_dollar_earned(stream, from, to) = acquisition_costs(stream, from, to) ÷ stream_net_revenue(stream, from, to) Lower is better. 0.0 = no acquisition cost. 1.0 = break-even.
6. Cross-Stream Comparison Index
Comparing streams by raw hourly rate alone ignores risk, effort variance, and income reliability. GigAnalytics computes a composite comparison index that weights four dimensions:
Stream Comparison Index Score (0–100)
formulascore(stream) = 0.50 × normalize(true_hourly_rate) + 0.25 × reliability_score + 0.15 × normalize(acquisition_roi) + 0.10 × normalize(revenue_growth_rate) Where normalize(x) maps x to [0, 1] across all active streams using min-max scaling. reliability_score = months_with_income / total_months_in_window
The index is relative — it compares your streams to each other, not to benchmarks. A stream with score 85 is better than your other streams, not necessarily 'good' in absolute terms.
7. Income Goal Projection
The pricing wizard answers: "Given my income goal, how should I price each stream?" The calculation works backwards from a target monthly net income.
Required Rate to Hit Monthly Goal
formulaFor a stream you want to grow: required_rate(stream, monthly_goal, target_hours) = monthly_goal / target_hours Adjusted for acquisition costs: required_gross_rate = (monthly_goal + projected_acquisition_costs) / target_hours Where projected_acquisition_costs is extrapolated from the stream's historical cost-per-revenue ratio.
8. Statistical Significance in Pricing Experiments
When you run an A/B pricing experiment (e.g., "$80/hr vs $100/hr for 30 days each"), GigAnalytics determines whether the observed revenue difference is statistically meaningful or likely due to random variation.
Two-proportion Z-test for revenue conversion
formulaFor two pricing variants A and B: p_A = conversions_A / impressions_A p_B = conversions_B / impressions_B p_pool = (conversions_A + conversions_B) / (impressions_A + impressions_B) z = (p_A - p_B) / sqrt(p_pool × (1 - p_pool) × (1/n_A + 1/n_B)) significance_level = two-tailed p-value from standard normal distribution We report: "significant at 95% confidence" if |z| > 1.96
For hourly-rate experiments, 'conversions' = bookings/acceptances; 'impressions' = opportunities presented. For direct revenue experiments (e.g., product price testing), we use mean revenue per transaction with Welch's t-test instead.
9. Design Decisions and Tradeoffs
Why use timer start time for heatmap bucketing?
Timer start time is the most reliable signal of when work effort begins. Using transaction_date would create misleading patterns — a project invoiced on Friday that was worked on Monday would appear as a Friday peak.
Why is the comparison index weighted 50% on hourly rate?
We tested equal-weighted and reliability-first weightings in user interviews. Freelancers consistently said "tell me the real hourly rate first, then reliability." The 50/25/15/10 split matched the mental model of 80% of participants.
Why not use GAAP accounting (accrual vs cash)?
GigAnalytics uses cash accounting (record income when received, costs when paid). For freelancers, cash flow clarity matters more than accrual matching. Most freelancers are not required to use GAAP.
Why subtract acquisition costs before dividing by hours?
Acquisition time (prospecting, pitching, applying) is often not tracked. Subtracting costs from revenue gives a conservative hourly rate that accounts for untracked acquisition effort through its financial proxy.
Why 90-day default window?
Shorter windows (30 days) amplify variance from seasonal patterns. Longer windows (180+ days) dilute signals from pricing changes you made recently. 90 days balances recency with statistical stability for most freelancers.
10. Limitations and Known Edge Cases
- ⚠️
Passive income attribution
Royalties, affiliate income, and ad revenue are difficult to attribute to specific work hours. GigAnalytics excludes time-entry-less transactions from hourly rate calculations, which may cause passive income streams to appear artificially low or show "no hourly rate available."
- ⚠️
Retainer contracts
A $5,000/month retainer with variable deliverables will have accurate revenue tracking but potentially misleading hourly rates if the time logged doesn't reflect the full scope of work. We recommend logging all client-related time, including async communication.
- ⚠️
Expenses beyond acquisition costs
GigAnalytics currently models only acquisition costs as an expense category. General business expenses (equipment, software, insurance) are not subtracted from stream revenue — doing so would require a full bookkeeping model outside our scope.
- ⚠️
Currency conversion lag
Exchange rates are fetched at transaction_date. For multi-currency freelancers, actual received amounts may differ if payment processors apply their own exchange rates. The variance is typically < 2%.
- ⚠️
Long-running projects
A project billed in Month 3 for work done in Months 1–3 will be attributed to the transaction_date (Month 3). The ROI calculation for Month 3 will appear elevated, while Months 1–2 will appear lower. Use the 90-day window to smooth this out.