///|
pub(all) struct Stage1PromptInput {
request : @routine.RoutineRequest
features : @domain.MarketFeatureFrame
hints : @domain.Stage1FeatureHints
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct Stage2PromptInput {
request : @routine.RoutineRequest
diagnosis : @domain.Stage1Diagnosis
strategy_files : Array[@domain.StrategyFile]
experience_memories : Array[@memory.ExperienceMemoryEntry]
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn stage1_prompt_input(
request : @routine.RoutineRequest,
features : @domain.MarketFeatureFrame,
hints : @domain.Stage1FeatureHints,
) -> Stage1PromptInput {
{ request, features, hints }
}
///|
pub fn stage2_prompt_input(
request : @routine.RoutineRequest,
diagnosis : @domain.Stage1Diagnosis,
strategy_files : Array[@domain.StrategyFile],
experience_memories? : Array[@memory.ExperienceMemoryEntry] = [],
) -> Stage2PromptInput {
{ request, diagnosis, strategy_files, experience_memories }
}
///|
fn snapshot_section(snapshot : @domain.KlineSnapshot) -> PromptSection {
prompt_section(
"market_snapshot",
"source_id=\{snapshot.source_id}\nsymbol=\{snapshot.symbol}\ntimeframe=\{snapshot.timeframe}\nclosed_bars=\{snapshot.closed_bar_count()}",
)
}
///|
fn feature_section(features : @domain.MarketFeatureFrame) -> PromptSection {
prompt_section(
"deterministic_features",
"closed_bar_count=\{features.closed_bar_count}\natr=\{opt_double_label(features.atr)}\nema_fast=\{opt_double_label(features.ema_fast)}\nema_slow=\{opt_double_label(features.ema_slow)}\nwarmup_complete=\{features.warmup_complete}",
)
}
///|
fn hints_section(hints : @domain.Stage1FeatureHints) -> PromptSection {
prompt_section(
"stage1_feature_hints",
"cycle_bias=\{cycle_label(hints.cycle_bias)}\ndirection_bias=\{direction_label(hints.direction_bias)}\npattern_hints=\{pattern_list(hints.pattern_hints)}\nnotes=\{hints.notes.join("; ")}",
)
}
///|
fn skill_asset_section(
title : String,
assets : Array[@skills.SkillAsset],
) -> PromptSection {
prompt_section(
title,
assets
.map(fn(asset) { "- \{asset.id} | \{asset.book_path} | \{asset.summary}" })
.join("\n"),
)
}
///|
pub fn assemble_stage1_prompt(input : Stage1PromptInput) -> PromptPacket {
prompt_packet(
Stage1,
"stage1-diagnosis",
[
prompt_section(
"task", "Diagnose the market state from bounded evidence. Return one JSON object matching schema `stage1-diagnosis`.",
),
prompt_section("operator_request", input.request.operator_request),
snapshot_section(input.request.snapshot),
feature_section(input.features),
hints_section(input.hints),
skill_asset_section("stage1_skill_assets", @skills.stage1_skill_assets()),
prompt_section(
"constraints", "Do not place orders. Do not call tools. Use strategy files only as routing hints. Output bare JSON only.",
),
],
["compute_indicators", "classify_market_structure"],
)
}
///|
fn diagnosis_section(diagnosis : @domain.Stage1Diagnosis) -> PromptSection {
prompt_section(
"stage1_diagnosis",
"cycle_position=\{cycle_label(diagnosis.cycle_position)}\ndirection=\{direction_label(diagnosis.direction)}\nconfidence=\{diagnosis.confidence}\nmarket_phase=\{phase_label(diagnosis.market_phase)}\npatterns=\{pattern_list(diagnosis.patterns)}\nkey_signals=\{diagnosis.key_signals.join("; ")}",
)
}
///|
fn experience_case_label(kind : @memory.ExperienceCaseKind) -> String {
match kind {
SuccessCase => "success"
FailureCase => "failure"
}
}
///|
fn experience_memory_section(
entries : Array[@memory.ExperienceMemoryEntry],
) -> PromptSection {
prompt_section(
"experience_memory",
if entries.is_empty() {
"No matching MoonBook experience memory entries were selected."
} else {
entries
.map(fn(entry) {
"- \{entry.id} | \{entry.book_path} | \{experience_case_label(entry.case_kind)} | \{entry.summary} | lesson: \{entry.lesson}"
})
.join("\n")
},
)
}
///|
pub fn assemble_stage2_prompt(input : Stage2PromptInput) -> PromptPacket {
prompt_packet(
Stage2,
"stage2-decision",
[
prompt_section(
"task", "Decide whether a trade plan exists from Stage 1 evidence and routed strategy files. Return one JSON object matching schema `stage2-decision`.",
),
prompt_section("operator_request", input.request.operator_request),
snapshot_section(input.request.snapshot),
diagnosis_section(input.diagnosis),
skill_asset_section(
"base_trade_decision_skills",
@skills.stage2_base_skill_assets(),
),
prompt_section("strategy_files", input.strategy_files.join("\n")),
experience_memory_section(input.experience_memories),
prompt_section(
"constraints", "No broker execution path exists. If location or equation is weak, choose NoOrder or Wait with rationale.",
),
],
[
"route_strategy_files", "select_experience_memories", "validate_stage_json",
],
)
}
///|
pub fn decision_summary(decision : @domain.Stage2Decision) -> PromptSection {
prompt_section(
"stage2_decision_summary",
"action=\{action_label(decision.action)}\nconfidence=\{decision.confidence}\nrationale=\{decision.rationale}\nrisk_notes=\{decision.risk_notes.join("; ")}",
)
}