package market import ( "fmt" "sync" "time" "git.mleku.dev/mleku/dendrite/pkg/axiom" ) // Reputation tracks the accuracy of information signals over time. // Authors who signal dislocations that subsequently resolve profitably // accumulate reputation. Authors whose signals prove false lose it. // This is the organism's second infrastructure service: weighting // information by demonstrated accuracy rather than identity or volume. type Reputation struct { mu sync.RWMutex authors map[string]*AuthorRecord } // AuthorRecord tracks one author's signal accuracy. type AuthorRecord struct { Pubkey string Signals int // total signals recorded Correct int // signals where predicted direction matched outcome Incorrect int // signals where prediction was wrong Accuracy float64 // correct / signals Score float64 // weighted reputation score [0, 1] LastSignal time.Time } // Prediction is a recorded signal with its outcome. type Prediction struct { Pubkey string Asset string Direction int // +1 bullish, -1 bearish Time time.Time } // Outcome records whether a prediction was correct. type Outcome struct { Prediction Prediction Correct bool PriceMove float64 // actual price change percentage ResolvedAt time.Time } // NewReputation creates an empty reputation tracker. func NewReputation() *Reputation { return &Reputation{ authors: make(map[string]*AuthorRecord), } } // RecordSignal registers a new prediction from an author. func (r *Reputation) RecordSignal(pred Prediction) { r.mu.Lock() defer r.mu.Unlock() rec, ok := r.authors[pred.Pubkey] if !ok { rec = &AuthorRecord{Pubkey: pred.Pubkey} r.authors[pred.Pubkey] = rec } rec.Signals++ rec.LastSignal = pred.Time } // RecordOutcome updates an author's record with the result of a prediction. func (r *Reputation) RecordOutcome(outcome Outcome) { r.mu.Lock() defer r.mu.Unlock() rec, ok := r.authors[outcome.Prediction.Pubkey] if !ok { return // no record for this author } if outcome.Correct { rec.Correct++ } else { rec.Incorrect++ } if rec.Signals > 0 { rec.Accuracy = float64(rec.Correct) / float64(rec.Signals) } // Score is accuracy weighted by signal count, with decay for few signals. // An author with 1 correct signal out of 1 is not as credible as // an author with 50 correct out of 60. minSamples := 10.0 confidence := float64(rec.Signals) / (float64(rec.Signals) + minSamples) rec.Score = rec.Accuracy * confidence } // Author returns the reputation record for a pubkey, or nil. func (r *Reputation) Author(pubkey string) *AuthorRecord { r.mu.RLock() defer r.mu.RUnlock() return r.authors[pubkey] } // TopAuthors returns the N authors with highest reputation scores. func (r *Reputation) TopAuthors(n int) []*AuthorRecord { r.mu.RLock() defer r.mu.RUnlock() all := make([]*AuthorRecord, 0, len(r.authors)) for _, rec := range r.authors { all = append(all, rec) } // Simple selection sort for small N. for i := 0; i < len(all) && i < n; i++ { best := i for j := i + 1; j < len(all); j++ { if all[j].Score > all[best].Score { best = j } } all[i], all[best] = all[best], all[i] } if n > len(all) { n = len(all) } return all[:n] } // AuthorCount returns the number of tracked authors. func (r *Reputation) AuthorCount() int { r.mu.RLock() defer r.mu.RUnlock() return len(r.authors) } // AuthorRecordToElements decomposes an author's reputation into lattice elements. func AuthorRecordToElements(rec *AuthorRecord) []axiom.Element { return []axiom.Element{ element{"pubkey", rec.Pubkey}, element{"signal-count", rec.Signals}, element{"signal-correct", rec.Correct}, element{"signal-incorrect", rec.Incorrect}, element{"accuracy", rec.Accuracy}, element{"reputation-score", rec.Score}, element{"timestamp", fmt.Sprintf("%d", rec.LastSignal.Unix())}, } }