KOUKKU Research · White Paper

Weather Favourability Index

A pure-Dart, literature-anchored fishing-favourability engine. 12 components. 8 species profiles. Zero AI cost. Sub-millisecond evaluation.

Version v2-26 · May 2026 Production · Shipped CodeHan Oy
§1

Executive Summary

WFI composes twelve independent, literature-anchored component scores into a single 0–100 fishing-favourability number. It runs entirely on-device, in pure Dart, at zero marginal cost — the only scoring layer in Koukku that scales linearly with users without touching the AI budget.

12
Components
Pressure (×3), wind, gust, time-of-day, lunar, solunar, cloud, air temp, precipitation, water temp
8
Species Profiles
Hauki, kuha, ahven, taimen, lohi, siika, made, silakka + species-neutral General baseline
<1 ms
Per scored hour
240 forecast hours re-scored in under 5 ms. "WFI Now" badge updates in under 100 µs.
~9%
Cells zeroed
A small fraction of species-by-component cells are pinned to zero — each with a documented missing citation. "When in doubt, zero it out."
The WFI answers a single narrow question: given everything the forecast can see, when should you be on the water? For Koukku it is the only scoring layer that runs at zero marginal cost — a property the rest of the stack works hard to achieve and that WFI gets for free by being pure-Dart math over a forecast already in memory.
§2

Why Fishing Forecasts Are Usually Wrong

Most fishing apps fail at one or more of three structural steps: reading the right signals, weighting them by what the fish care about, and surfacing the result without overclaiming.

§2.1 — Folklore

Single-metric forecasts dressed up as biology

Taking one variable — usually barometric pressure — slapping a "Best Fishing Times" label on it, and calling the job done. A 4 hPa pressure drop over 2 hours is the signature of an arriving frontal boundary; the same drop over 12 hours is a deepening low 200 km to the north. The biological response is very different. WFI splits pressure into three independent components capturing different timescales, each scored and weighted separately.

§2.2 — Black-box

Opaque ML pretending to be science

An opaque ML model wrapped around a heterogeneous training set is indefensible the moment a competent angler asks: what is the model trained on? Whose catch reports? What does the score mean for a species the training set never saw? Every WFI number traces to either a peer-reviewed paper, a documented zero, or an explicit Koukku design constant. There is no opaque layer.

§2.3 — Single species

"Good fishing" without a target

A burbot angler at midnight in February has nearly opposite optimal conditions to an ahven angler at noon in July. Reporting a single "fishing favourability" number is therefore wrong before the maths even begins. The WFI's answer is the species profile: each registered species ships its own 12-element weight vector, its own trapezoidal response curves, and its own circadian peaks.

§2.4 — Honesty

Overclaiming what a forecast can do

Conditions are favourable does not mean fish will bite. WFI is an opportunity index: it tells you when conditions reward effort, not when fish are guaranteed to be caught. A WFI of 80 means "the conditions are pulling in your favour"; it does not mean "go reel one in." Section 8 spells this out explicitly.

§3

Architecture — Three Commitments

The WFI's value proposition rests on three architectural decisions. They are mutually reinforcing — a change to any one of them tends to undo the others.

Pillar 1

Components first, species second

The engine scores one component at a time, then weights by species. There is no if (species == 'hauki') switch anywhere in the code. Adding a species cannot regress an existing one — a new profile is a new constant; the engine path is unchanged.

Pillar 2

Per-species weights as the relief valve

Evidence quality is wildly uneven across species. Pike is the best-studied; Baltic herring is inferred from commercial trap-net data. The response: make unevenness legible through weights. Strong literature → nonzero weight with citation. No source → weight pinned to zero with a one-line note. When in doubt, zero it out and document.

Pillar 3

Pure Dart · Offline-first · FinOps-zero

Every calculation runs on-device, in pure Dart, with no dependencies beyond dart:math. Two anglers fishing the same lake share a single HTTP request per cache window. The only network dependency is the Open-Meteo fetch that powers every other weather feature. Zero AI budget consumed.

Formula: WFI = round(100 × Σᵢ wᵢ × scoreᵢ) with component weights summing to 1.0, enforced as an assertion across every registered profile. When a component returns null (missing data), the engine skips it and renormalises over the present-data weights only.
§4

The 12 Components

Each component scores a single forecast-hour attribute into [0.0, 1.0]. A null return means the data is missing; the engine renormalises rather than penalising. Priority reflects the General-profile baseline — exact allocations vary by species profile.

# Component Physical signal Units General priority Key citation
1 pressureTrend 2 h centred Δ hPa High Stoner (2004); Casselman & Lewis (1996)
2 pressureChange6h 6 h backward Δ (pre-frontal) hPa Medium Stoner (2004)
3 pressureStability24h 24 h max-min range hPa Low inferred from frontal biology
4 wind sustained 10 m wind speed m/s High Lehtonen & Niemelä (1986)
5 gustFactor gusts ÷ max(sustained, 0.5) Low–Medium (species-specific) Casselman & Lewis (1996) for hauki
6 timeOfDay true sun-phase circadian (lat/lng-aware) High Baktoft et al. (2012); Ovidio et al. (1998)
7 lunar illumination % × altitude gate Low–High (species-specific) Müller (1973); Horppila et al. (2018)
8 solunarMajorMinor proximity to nearest moon transit minutes Zeroed in General Knight (1936) — weak evidence basis
9 cloud cloud cover % High Pierce et al. (2003); Helfman (1981)
10 airTemp 2 m air temperature °C Medium (proxy for water temp) Magnuson et al. (1979)
11 precipitation liquid precipitation rate mm/h Medium Steingrimsson & Gislason (2002) for taimen
12 waterTemp resolved water temperature °C Highest Casselman & Lewis (1996); Hofmann & Fischer (2002)
Pressure is split into three independent channels because a 2 h delta and a 6 h delta measure the same physical forcing at different timescales — and the disagreement between them is informative. A 2 h drop paired with a large 6 h drop signals a well-developed approaching front. The same 2 h drop with a small 6 h drop signals a fast-arriving squall with no broader pattern behind it.
The marquee burbot bug (2026-05-13 audit): the pre-audit made profile allocated the same pressure-trend priority as hauki — a surface predator. Hofmann & Fischer (2002) explicitly document that barometric pressure attenuates with depth; a benthic-deep species that shows no 6 h pressure response cannot plausibly show a 2 h response either. The fix reallocated pressure-trend weight to water temperature and lunar components, where the peer-reviewed biology actually lives for this species.
§5

Eight Finnish Fish, Eight Strategies

Each species ships its own 12-element weight vector, its own trapezoidal response curves, and its own circadian peaks. The engine does not branch on species — the same pure-Dart routine consumes whichever profile is supplied.

Species Pressure trend Wind Time of day Lunar Cloud Water temp Defining trait
General High High High Low High Highest Cross-species prior
Hauki Highest High Crepuscular Zeroed High Medium Strongest pre-frontal signal in registry
Kuha High High Nocturnal/dusk Highest freshwater High Medium Low-light apex predator
Ahven Highest registry High Afternoon peak Zeroed Medium High Daylight sight-feeder
Taimen Medium Medium Crepuscular Low Medium High (cold-skewed) Light rain bite trigger
Lohi Medium Medium Dawn-heavy Low Medium High (cooler plateau) Weakest pressure support in registry
Siika Medium High (calm-water) Midday Zeroed Medium Highest after made No peer-reviewed barometric study
Made Lowest Low (depth) Deep night Highest registry High Highest registry Depth attenuates all surface signals
Silakka Medium High (calm-water) Dusk Low High High (cold-coastal) Commercial literature only
Hauki Northern Pike
Esox lucius

Finland's dominant ambush predator. Most heavily studied freshwater piscivore in the temperate northern hemisphere. Thermal plateau 10–22 °C. Carries the highest pressure-trend priority of any species in the registry — the falling-barometer signal is real, well-replicated in Esocid literature, and remains the most robustly documented pre-frontal feeding response we have. Lunar weight is zeroed: Casselman & Lewis (1996) explicitly find no Esocid lunar correlation.

Peak scenario: Overcast crepuscular hour during a well-developed frontal arrival, light-to-moderate chop, water 16–22 °C, pressure falling steadily over the past 6 h → WFI 85–95.
Kuha Pikeperch
Sander lucioperca

Finland's premier low-light freshwater predator. Tapetum lucidum gives retinal sensitivity 2× pike's; strike rates peak below 1 lux. Undertakes diel vertical migrations to hunt smelt in the upper water column at dusk. Carries the highest lunar priority among freshwater piscivores — bright moonlit nights extend the kuha feeding window and are scored accordingly.

Peak scenario: Heavy overcast dusk on a falling-barometer day, water 18 °C, light wind → WFI 85–92.
Ahven European Perch
Perca fluviatilis

Daylight sight-feeding generalist. Strike rates peak at intermediate light — broken cloud, not full overcast or full glare. Diel pattern daytime-dominant (peak 11:00–17:00 local). Carries the highest pressure-trend priority in the entire registry — the Stoner (2004) percid review places perch among the strongest barometric responders. Both lunar and solunar components are zeroed: no peer-reviewed support found for either in this species.

Peak scenario: Mid-afternoon in summer, water 16–22 °C, light wind, broken cloud, falling barometer → WFI 88–95.
Taimen Brown/Sea Trout
Salmo trutta

Cold-stenothermal salmonid. Thermal preferendum 12.2 °C. Light rain is a documented bite trigger — drift availability spikes are cited by Steingrimsson & Gislason (2002). Pressure response essentially neutral: the angler-tradition "rising pressure triggers trout" rule has no peer-reviewed corroboration (Stoner 2004). Carries the highest precipitation priority of any profiled species.

Peak scenario: Overcast crepuscular dawn during a light spring rain, water 12–16 °C, light wind → WFI 82–90.
Lohi Atlantic Salmon
Salmo salar

Cooler than taimen; thermal preferendum ~14 °C. Adult migration arrest above 22 °C (Solomon & Sambrook 2004). Feeding peaks at low surface light. Dawn-heavy diel pattern. The pressure-trend allocation is the most uncertain in the registry — no salmonid barometric signal has been cleanly replicated in peer-reviewed work — and is flagged as a Koukku-calibration candidate.

Peak scenario: Overcast dawn at start of summer run, water 10–14 °C, light coastal wind → WFI 80–88.
Siika European Whitefish
Coregonus lavaretus

Cold-water planktivore. Sustained feeding plateau 7–14 °C — the coldest in the registry. Calm-water specialist: surface chop above 4 m/s disrupts thermocline stability and shoal depth predictability. Pressure response is entirely unstudied — no peer-reviewed coregonid barometric study has been found. Carries the highest water-temperature priority of any species after made. Barometric allocation flagged as a calibration candidate.

Peak scenario: Calm midday in early summer, water 8–14 °C, broken cloud, stable barometer → WFI 80–88.
Made Burbot
Lota lota · only freshwater Gadidae

Cold-water benthic specialist living at depths of 10–80 m. Carries the highest water-temperature priority in the entire registry — water temp is the dominant driver of burbot activity. Also carries the highest lunar priority in the registry — Müller (1973) and Harrison et al. (2013) both anchor burbot's entrainment to the dark half of the day. Surface pressure signals attenuate with depth; three components are zeroed entirely.

Peak scenario: Deep night, full moon, water 6–12 °C, settled 24 h barometer → WFI 88–95. Bright noon at 18 °C: WFI 5–15.
Silakka Baltic Herring
Clupea harengus membras

#1 Finnish coastal angler catch by volume. Calm-water shoaling specialist — commercial trap-net data (Casini et al. 2006; LUKE 2018) shows catches drop sharply above 4 m/s. Dusk-dominant diel pattern. The weakest recreational-angler literature coverage in the registry; most sources are commercial-fishery (ICES WGBFAS, LUKE) or general clupeid biology. All allocations flagged as calibration candidates.

Peak scenario: Calm dusk, water 8–14 °C, heavy overcast, stable 24 h atmosphere → WFI 80–88.
§6

The General Profile

When the user hasn't told Koukku what they're fishing for, the WFI uses the General profile — a deliberate prior, not an average of species profiles.

What General is — and is not

The General profile is a prior probability distribution over the space of Finnish angling sessions. It weights components by how reliably predictive they are cross-species, not by what any single species most cares about. Water temperature carries the highest single allocation — it is the most-cited environmental variable in fish metabolic physiology. Solunar proximity carries no allocation at all — the species-neutral solunar literature is angler-tradition, not peer-reviewed ecology.

What General does not do: it is not the mean of all species profiles. Taking the mean would produce a ghost solunar weight with no clear biological interpretation. The General profile honours the relief-valve discipline — zero with documentation — as strictly as any species profile.

The 2026-05-13 rebalance

The v2-26 General profile was rebalanced alongside the expansion from 8 to 12 components. The full pressure family (trend + 6 h + 24 h) was trimmed as a group. The trim reflects the Stoner (2004) effect-size caveat: angler tradition over-weights barometric signals, and the General baseline is the appropriate place to push back gently against that bias. The freed allocation was redistributed to lunar, air temperature, and water temperature components.

§7

Validation Roadmap

Three stages of evidence quality, only Stage 1 currently active. The architecture was designed from day one to support Stages 2 and 3 without refactoring the engine.

Stage 1 · Now

Literature priors

Every non-zero weight cites peer-reviewed literature in the source comment. Every zero weight names the absent citation. The per-catch wfi_version column stamps every catch with the engine version so historical analytics remain queryable across future refactors. Current stage.

Stage 2 · ≥1 000 catches

Bayesian update against catch data

When per-species catch counts reach 1 000 with metadata (timestamp, lat/lng, weather snapshot), Koukku can compute a posterior probability distribution over WFI weights conditioned on observed CPUE. The architecture is ready: wfi_version is already stored; the literature priors are the Stage 2 starting point.

Stage 3 · Mature

Per-user General weights

With sufficient per-user catch data, the General profile can be personalised. A user whose hauki bites historically correlate with solunar transits could legitimately receive a higher solunar allocation than the General prior. Years away; requires the user base and data density to justify the added complexity.

§8

What the WFI Is Not

Explicitly stated so no Koukku user, biologist, or partner is left with an incorrect impression.

Not a catch predictor
A high WFI means "conditions reward effort." Fish behaviour is probabilistic. The engine ranks hours by opportunity, not guarantee.
Not a guide for what to fish for
The species profile is supplied by the user. WFI scores the current conditions against that profile. It cannot recommend a species switch.
Not a replacement for local knowledge
Hydrography, weed structure, prey migration, and seasonal anomalies are invisible to WFI. A 90-scoring hour in the wrong spot is a blank.
Not a black-box ML model
Every weight is named and citable. Every zero is documented. The engine cannot produce a number that can't be traced to a source comment or this white paper.
Not a solunar app
solunarMajorMinor exists and contributes for some species, but the General profile zeroes it out and most species keep it at very low priority. WFI is not built around the lunar-transit hypothesis.
Not a replacement for eyes
A rising fish, a surface temperature gradient visible in dawn light, or a cloud of mayflies tells the angler something the forecast never will. WFI informs; it doesn't observe.
§9 — §13 in the full paper

Worked Example: October Dawn for Hauki

A concrete scoring walkthrough for a specific forecast hour — the canonical hauki scenario. The bar lengths show each component's contribution relative to the highest-scoring component in this hour.

Scenario: October, Finnish inland lake, 05:30 local. Pressure falling rapidly over 2 h; 6 h delta strongly pre-frontal; 24 h range moderate. Wind 4 m/s with elevated gust factor. 70 % cloud. Air temp 6 °C. No rain. Water temp 14 °C.
pressureTrend
Peak
pressureChange6h
Very high
cloud
Peak
wind
High
gustFactor
High
waterTemp
High
pressureStability24h
Good
solunarMajorMinor
Moderate
timeOfDay
Low
airTemp
Moderate
precipitation
Neutral
lunar
Zeroed
WFI (hauki)
84
/ 100

Same hour scored against ahven profile → 77 / 100. The dominant positive is the pressure-trend component. The dominant negative is timeOfDay — pre-dawn is not yet the ahven's mid-afternoon optimum. This divergence illustrates exactly why a species-neutral "WFI" score would be meaningless: the same hour reads very differently to a pike angler vs a perch angler.

§10

Key References

Selected peer-reviewed citations that anchor the WFI scoring architecture. Every non-zero weight in every species profile traces to one of these (or a companion listed in the full white paper).

Companion document: WSI White Paper — the Weather Similarity Index, the atmospheric-distance sibling of the WFI.