revenue.go raw

   1  package market
   2  
   3  import (
   4  	"fmt"
   5  	"sync"
   6  	"time"
   7  
   8  	"git.mleku.dev/mleku/dendrite/pkg/axiom"
   9  )
  10  
  11  // RevenueSource distinguishes where revenue came from.
  12  type RevenueSource int
  13  
  14  const (
  15  	ArbitrageRevenue       RevenueSource = iota // from exploiting dislocations
  16  	InfrastructureRevenue                        // from services (feeds, reputation, relay fees)
  17  )
  18  
  19  func (rs RevenueSource) String() string {
  20  	if rs == ArbitrageRevenue {
  21  		return "arbitrage"
  22  	}
  23  	return "infrastructure"
  24  }
  25  
  26  // RevenueEntry records a single revenue event.
  27  type RevenueEntry struct {
  28  	Source  RevenueSource
  29  	Amount  float64
  30  	Asset   string
  31  	Note    string
  32  	Time    time.Time
  33  }
  34  
  35  // RevenueAccounting tracks the split between extractive and corrective revenue.
  36  // The organism's health is measured not just by total revenue but by the ratio:
  37  // infrastructure revenue should grow while arbitrage revenue shrinks, indicating
  38  // the organism is building structure that eliminates the dislocations it fed on.
  39  type RevenueAccounting struct {
  40  	mu      sync.Mutex
  41  	entries []RevenueEntry
  42  }
  43  
  44  // NewRevenueAccounting creates an empty revenue tracker.
  45  func NewRevenueAccounting() *RevenueAccounting {
  46  	return &RevenueAccounting{}
  47  }
  48  
  49  // Record adds a revenue entry.
  50  func (ra *RevenueAccounting) Record(entry RevenueEntry) {
  51  	ra.mu.Lock()
  52  	defer ra.mu.Unlock()
  53  	ra.entries = append(ra.entries, entry)
  54  }
  55  
  56  // Summary returns aggregate revenue by source.
  57  type RevenueSummary struct {
  58  	Arbitrage      float64
  59  	Infrastructure float64
  60  	Total          float64
  61  	InfraRatio     float64 // infrastructure / total; higher = healthier
  62  	EntryCount     int
  63  }
  64  
  65  // Summary computes the revenue split.
  66  func (ra *RevenueAccounting) Summary() RevenueSummary {
  67  	ra.mu.Lock()
  68  	defer ra.mu.Unlock()
  69  
  70  	var s RevenueSummary
  71  	for _, e := range ra.entries {
  72  		switch e.Source {
  73  		case ArbitrageRevenue:
  74  			s.Arbitrage += e.Amount
  75  		case InfrastructureRevenue:
  76  			s.Infrastructure += e.Amount
  77  		}
  78  	}
  79  	s.Total = s.Arbitrage + s.Infrastructure
  80  	s.EntryCount = len(ra.entries)
  81  	if s.Total > 0 {
  82  		s.InfraRatio = s.Infrastructure / s.Total
  83  	}
  84  	return s
  85  }
  86  
  87  // IsTransitioning returns true when infrastructure revenue exceeds
  88  // arbitrage revenue — the organism has transitioned from parasite to symbiont.
  89  func (ra *RevenueAccounting) IsTransitioning() bool {
  90  	s := ra.Summary()
  91  	return s.Infrastructure > s.Arbitrage
  92  }
  93  
  94  // RevenueSummaryToElements decomposes the revenue split into lattice elements.
  95  func RevenueSummaryToElements(s *RevenueSummary) []axiom.Element {
  96  	return []axiom.Element{
  97  		element{"revenue-arbitrage", s.Arbitrage},
  98  		element{"revenue-infrastructure", s.Infrastructure},
  99  		element{"revenue-total", s.Total},
 100  		element{"revenue-infra-ratio", s.InfraRatio},
 101  		element{"revenue-entry-count", s.EntryCount},
 102  		element{"timestamp", fmt.Sprintf("%d", time.Now().Unix())},
 103  	}
 104  }
 105