Skip to content

Commit c7b6f92

Browse files
author
raedah
committed
Add proposals 8 and 9, removing oscillations.
Proposal 8 removes oscilliations and optimizes for ticket pool target size accuracy. Proposal 9 removes oscillations and optimizes to remove volatility from the ticket price.
1 parent 937439e commit c7b6f92

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

main.go

+6
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,12 @@ func main() {
448448
case "7":
449449
sim.nextTicketPriceFunc = sim.calcNextStakeDiffProposal7
450450
pfResultsName = "Proposal 7"
451+
case "8":
452+
sim.nextTicketPriceFunc = sim.calcNextStakeDiffProposal8
453+
pfResultsName = "Proposal 8"
454+
case "9":
455+
sim.nextTicketPriceFunc = sim.calcNextStakeDiffProposal9
456+
pfResultsName = "Proposal 9"
451457
default:
452458
fmt.Printf("%q is not a valid ticket price func name\n",
453459
*pfName)

proposals.go

+162
Original file line numberDiff line numberDiff line change
@@ -538,3 +538,165 @@ func (s *simulator) calcNextStakeDiffProposal7() int64 {
538538
}
539539
return nextDiff
540540
}
541+
542+
// Algo 8 by raedah
543+
// The goal of this algorithm is to remove oscillations from ticket price.
544+
func (s *simulator) calcNextStakeDiffProposal8() int64 {
545+
// Stake difficulty before any tickets could possibly be purchased is
546+
// the minimum value.
547+
nextHeight := int32(0)
548+
if s.tip != nil {
549+
nextHeight = s.tip.height + 1
550+
}
551+
stakeDiffStartHeight := int32(s.params.CoinbaseMaturity) + 1
552+
if nextHeight < stakeDiffStartHeight {
553+
return s.params.MinimumStakeDiff
554+
}
555+
556+
// Return the previous block's difficulty requirements if the next block
557+
// is not at a difficulty retarget interval.
558+
intervalSize := s.params.StakeDiffWindowSize
559+
curDiff := s.tip.ticketPrice
560+
if int64(nextHeight)%intervalSize != 0 {
561+
return curDiff
562+
}
563+
564+
// Attempt to get the pool size from the previous retarget interval.
565+
var prevPoolSize int64
566+
prevRetargetHeight := nextHeight - int32(intervalSize)
567+
node := s.ancestorNode(s.tip, prevRetargetHeight, nil)
568+
if node != nil {
569+
prevPoolSize = int64(node.poolSize)
570+
}
571+
572+
// Get the immature ticket count from the previous interval.
573+
var prevImmatureTickets int64
574+
ticketMaturity := int64(s.params.TicketMaturity)
575+
s.ancestorNode(node, node.height-int32(ticketMaturity), func(n *blockNode) {
576+
prevImmatureTickets += int64(len(n.ticketsAdded))
577+
})
578+
579+
// Return the existing ticket price for the first interval.
580+
if prevPoolSize+prevImmatureTickets == 0 {
581+
return curDiff
582+
}
583+
584+
immatureTickets := int64(len(s.immatureTickets))
585+
curPoolSize := int64(s.tip.poolSize)
586+
curPoolSizeAll := curPoolSize + immatureTickets
587+
prevPoolSizeAll := prevPoolSize + prevImmatureTickets
588+
poolSizeChangeRatio := float64(curPoolSizeAll) / float64(prevPoolSizeAll)
589+
ticketsPerBlock := int64(s.params.TicketsPerBlock)
590+
ticketPoolSize := int64(s.params.TicketPoolSize)
591+
targetPoolSizeAll := ticketsPerBlock * (ticketPoolSize + ticketMaturity) // responsive, less expires
592+
//targetPoolSizeAll := (ticketsPerBlock * ticketPoolSize) + immatureTickets // smooth
593+
targetRatio := float64(curPoolSizeAll) / float64(targetPoolSizeAll)
594+
595+
targetBalancer := 1.0
596+
relativeBalancer := 1.0
597+
if curPoolSizeAll < prevPoolSizeAll { // trending down
598+
if targetRatio > 1.0 { // above target, right direction, continue
599+
relativeBalancer = poolSizeChangeRatio // lower price
600+
}
601+
if targetRatio < 1.0 { // below target, wrong direction, reverse
602+
targetBalancer = targetRatio // lower price
603+
}
604+
}
605+
if curPoolSizeAll > prevPoolSizeAll { // trending up
606+
if targetRatio < 1.0 { // below target, right direction, continue
607+
relativeBalancer = poolSizeChangeRatio // raise price
608+
}
609+
if targetRatio > 1.0 { // above target, wrong direction, reverse
610+
targetBalancer = targetRatio // raise price
611+
}
612+
}
613+
614+
// Voila!
615+
nextDiff := float64(curDiff) * relativeBalancer * targetBalancer
616+
617+
// Upper bound on price. Insures the pool gets fully populated during price rises.
618+
maximumStakeDiff := (float64(s.tip.totalSupply) / float64(targetPoolSizeAll)) * targetRatio
619+
if nextDiff > maximumStakeDiff {
620+
nextDiff = maximumStakeDiff
621+
}
622+
623+
// Lower bound on price. Prevents price bounce.
624+
weightedIdealDiff := (float64(s.tip.stakedCoins) / float64(targetPoolSizeAll)) * targetRatio
625+
if nextDiff < weightedIdealDiff {
626+
nextDiff = weightedIdealDiff
627+
}
628+
629+
// Hard coded minimum value.
630+
if int64(nextDiff) < s.params.MinimumStakeDiff {
631+
return s.params.MinimumStakeDiff
632+
}
633+
634+
return int64(nextDiff)
635+
}
636+
637+
// Algo 9 by raedah
638+
// This algorithm has the interesting property that it does not base its
639+
// calculations on the previous diff. Rather, it calculates anew each run.
640+
// This creates the smoothest ticket price adjustments,
641+
// with a trade off of a less stable ticket pool size. Expiries increase 1%.
642+
// Perhaps the accuracy can still be solved.
643+
func (s *simulator) calcNextStakeDiffProposal9() int64 {
644+
// Stake difficulty before any tickets could possibly be purchased is
645+
// the minimum value.
646+
nextHeight := int32(0)
647+
if s.tip != nil {
648+
nextHeight = s.tip.height + 1
649+
}
650+
stakeDiffStartHeight := int32(s.params.CoinbaseMaturity) + 1
651+
if nextHeight < stakeDiffStartHeight {
652+
return s.params.MinimumStakeDiff
653+
}
654+
655+
// Return the previous block's difficulty requirements if the next block
656+
// is not at a difficulty retarget interval.
657+
intervalSize := s.params.StakeDiffWindowSize
658+
curDiff := s.tip.ticketPrice
659+
if int64(nextHeight)%intervalSize != 0 {
660+
return curDiff
661+
}
662+
663+
// Attempt to get the pool size from the previous retarget interval.
664+
var prevPoolSize int64
665+
prevRetargetHeight := nextHeight - int32(intervalSize)
666+
node := s.ancestorNode(s.tip, prevRetargetHeight, nil)
667+
if node != nil {
668+
prevPoolSize = int64(node.poolSize)
669+
}
670+
671+
// Get the immature ticket count from the previous interval.
672+
var prevImmatureTickets int64
673+
ticketMaturity := int64(s.params.TicketMaturity)
674+
s.ancestorNode(node, node.height-int32(ticketMaturity), func(n *blockNode) {
675+
prevImmatureTickets += int64(len(n.ticketsAdded))
676+
})
677+
678+
// Return the existing ticket price for the first interval.
679+
if prevPoolSize+prevImmatureTickets == 0 {
680+
return curDiff
681+
}
682+
683+
immatureTickets := int64(len(s.immatureTickets))
684+
//curPoolSize := int64(s.tip.poolSize)
685+
//curPoolSizeAll := curPoolSize + immatureTickets
686+
//targetRatio := float64(curPoolSizeAll) / float64(targetPoolSizeAll)
687+
ticketsPerBlock := int64(s.params.TicketsPerBlock)
688+
ticketPoolSize := int64(s.params.TicketPoolSize)
689+
targetPoolSizeAll := (ticketsPerBlock * ticketPoolSize) + immatureTickets
690+
691+
// Voila!
692+
idealPrice := float64(s.tip.stakedCoins) / float64(targetPoolSizeAll)
693+
nextDiff := idealPrice
694+
//nextDiff := idealPrice * targetRatio //more responsive, less smooth
695+
696+
// Hard coded minimum value.
697+
if int64(nextDiff) < s.params.MinimumStakeDiff {
698+
return s.params.MinimumStakeDiff
699+
}
700+
701+
return int64(nextDiff)
702+
}

0 commit comments

Comments
 (0)