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