Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 32 additions & 18 deletions analysts/__tests__/froggy.enrichment_adapter.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
import { describe, it, expect } from "vitest";
import {
scoreFroggyTrendPullbackFromEnriched,
scoreFroggyTrendPullback,
type FroggyTrendPullbackScore
} from "../froggy.trend_pullback_v1";
import { defaultUwrConfig } from "../../validators/UniversalWeightingRule";
import {
BROKE_EMA_WITH_BODY_UNIMPLEMENTED_STUB,
buildFroggyTrendPullbackInputFromEnriched,
type FroggyEnrichedView
} from "../froggy.enrichment_adapter";
import {
buildFroggyResidualInput,
composeFroggyTrendPullbackInput
} from "../froggy.residual_builder";
import { interpretEnrichmentMapping } from "../../validators/EnrichmentMappingInterpreter";
import { NEWEST_REGISTERED_FROGGY_MAPPING } from "./support/froggyMappings";

/** The live scoring path (scorer node shape): registered mapping fragment +
* residual → composer → rubric. The adapter-only convenience wrapper left
* with DEM-PRODUCER-PLAN (the adapter no longer emits a full input). */
function scoreViaMapping(enriched: FroggyEnrichedView): FroggyTrendPullbackScore {
const { fragment } = interpretEnrichmentMapping(NEWEST_REGISTERED_FROGGY_MAPPING(), enriched);
const input = composeFroggyTrendPullbackInput(fragment, buildFroggyResidualInput(enriched));
return scoreFroggyTrendPullback(input, defaultUwrConfig, enriched);
}

describe("froggy.enrichment_adapter", () => {
it("produces a high-ish score for a strong enriched setup", () => {
Expand All @@ -19,7 +34,14 @@ describe("froggy.enrichment_adapter", () => {
technical: {
emaDistancePct: 0.5,
isInValueSweetSpot: true,
brokeEmaWithBody: false
// DEM-PRODUCER-CANDLE: the lane's computed candle-structure facts.
brokeEmaWithBody: false,
haFlatBack: "bullish",
haFlatBackConfirmed: true,
// DEM-PRODUCER-PLAN: a strong setup carries a verified plan; the
// risk axis reads the provider's R:R through the mapping (a plan-less
// submission scores the declared floor).
plan: { entryPrice: 100, stopPrice: 98, firstTargetPrice: 104, rrToFirstTarget: 2, targetCount: 1 }
},
pattern: {
patternName: "liquidity sweep + reversal",
Expand All @@ -31,7 +53,7 @@ describe("froggy.enrichment_adapter", () => {
};

const result: FroggyTrendPullbackScore =
scoreFroggyTrendPullbackFromEnriched(enriched);
scoreViaMapping(enriched);

// All scoring data is now in analystScore (canonical)
expect(result.analystScore.uwrAxes.structure).toBeGreaterThan(0.3);
Expand All @@ -41,23 +63,15 @@ describe("froggy.enrichment_adapter", () => {
expect(result.analystScore.uwrScore).toBeGreaterThan(0.3);
});

it("falls back to safe defaults when optional sections are missing", () => {
it("REFUSES when the technical lane is absent — the candle facts are required binds (D-DEM-5(2)); no safe default", () => {
const enriched: FroggyEnrichedView = {
signalId: "enriched-2",
symbol: "ETH",
market: "crypto",
timeframe: "4h"
// no technical/pattern/sentiment/news/aiMl
};

const result = scoreFroggyTrendPullbackFromEnriched(enriched);

// All scoring data is now in analystScore (canonical)
expect(result.analystScore.uwrAxes.structure).toBeDefined();
expect(result.analystScore.uwrAxes.execution).toBeDefined();
expect(result.analystScore.uwrAxes.risk).toBeDefined();
expect(result.analystScore.uwrAxes.insight).toBeDefined();
expect(result.analystScore.uwrScore).toBeDefined();
expect(() => scoreViaMapping(enriched)).toThrow(/required-source-absent|brokeEmaWithBody/);
});

// EQ-GOV D-EQ-2: the confidence -> triggerPatternQuality threshold map is
Expand Down Expand Up @@ -162,15 +176,15 @@ describe("froggy.enrichment_adapter", () => {
});
});

it("uses the declared brokeEmaWithBody stub when technical omits the field (D5 zero-movement)", () => {
expect(BROKE_EMA_WITH_BODY_UNIMPLEMENTED_STUB).toBe(false);
it("emits neither brokeEmaWithBody nor haFlatBackConfirmed (DEM-PRODUCER-CANDLE: computed lane facts, mapping-bound)", () => {
const input = buildFroggyTrendPullbackInputFromEnriched({
signalId: "stub-broke",
signalId: "no-stub",
symbol: "BTC",
market: "crypto",
timeframe: "1h",
technical: { emaDistancePct: 0.5, isInValueSweetSpot: true }
});
expect(input.brokeEmaWithBody).toBe(BROKE_EMA_WITH_BODY_UNIMPLEMENTED_STUB);
expect(Object.keys(input)).not.toContain("brokeEmaWithBody");
expect(Object.keys(input)).not.toContain("haFlatBackConfirmed");
});
});
Loading
Loading