Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creation of Market Price Projection API #277

Merged
merged 12 commits into from
Aug 30, 2024
68 changes: 68 additions & 0 deletions backend/handlers/markets/marketprojectedprobability.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package marketshandlers

import (
"encoding/json"
"net/http"
"socialpredict/handlers/marketpublicresponse"
"socialpredict/handlers/math/probabilities/wpam"
"socialpredict/handlers/tradingdata"
"socialpredict/models"
"socialpredict/util"
"strconv"
"time"

"github.com/gorilla/mux"
)

// ProjectNewProbabilityHandler handles the projection of a new probability based on a new bet.
func ProjectNewProbabilityHandler(w http.ResponseWriter, r *http.Request) {
// Parse market ID, amount, and outcome from the URL
vars := mux.Vars(r)
marketId := vars["marketId"]
amountStr := vars["amount"]
outcome := vars["outcome"]

// Convert market ID to uint
marketIDUint64, err := strconv.ParseUint(marketId, 10, 64)
if err != nil {
http.Error(w, "Invalid market ID", http.StatusBadRequest)
return
}
marketIDUint := uint(marketIDUint64)

// Convert amount to int64
amount, err := strconv.ParseInt(amountStr, 10, 64)
if err != nil {
http.Error(w, "Invalid amount value", http.StatusBadRequest)
return
}

// Create a new Bet object without a username
newBet := models.Bet{
Amount: amount,
Outcome: outcome,
PlacedAt: time.Now(), // Assuming the bet is placed now
MarketID: marketIDUint,
}

// Open up database to utilize connection pooling
db := util.GetDB()

// Fetch all bets for the market
currentBets := tradingdata.GetBetsForMarket(db, marketIDUint)

// Fetch the market creation time using utility function
publicResponseMarket, err := marketpublicresponse.GetPublicResponseMarketByID(db, marketId)
if err != nil {
http.Error(w, "Invalid market ID", http.StatusBadRequest)
return
}
marketCreatedAt := publicResponseMarket.CreatedAt

// Project the new probability
projectedProbability := wpam.ProjectNewProbabilityWPAM(marketCreatedAt, currentBets, newBet)

// Set the content type to JSON and encode the response
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(projectedProbability)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ type ProbabilityChange struct {
Timestamp time.Time `json:"timestamp"`
}

type ProjectedProbability struct {
Probability float64 `json:"projectedprobability"`
}

// appConfig holds the loaded application configuration accessible within the package
var appConfig *setup.EconomicConfig

Expand All @@ -34,7 +38,6 @@ func CalculateMarketProbabilitiesWPAM(marketCreatedAtTime time.Time, bets []mode
totalYes := appConfig.Economics.MarketCreation.InitialMarketYes
totalNo := appConfig.Economics.MarketCreation.InitialMarketNo

// Add initial state
probabilityChanges = append(probabilityChanges, ProbabilityChange{Probability: P_initial, Timestamp: marketCreatedAtTime})

// Calculate probabilities after each bet
Expand All @@ -51,3 +54,14 @@ func CalculateMarketProbabilitiesWPAM(marketCreatedAtTime time.Time, bets []mode

return probabilityChanges
}

func ProjectNewProbabilityWPAM(marketCreatedAtTime time.Time, currentBets []models.Bet, newBet models.Bet) ProjectedProbability {

updatedBets := append(currentBets, newBet)

probabilityChanges := CalculateMarketProbabilitiesWPAM(marketCreatedAtTime, updatedBets)

finalProbability := probabilityChanges[len(probabilityChanges)-1].Probability

return ProjectedProbability{Probability: finalProbability}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package wpam_test

import (
"socialpredict/handlers/math/outcomes/dbpm"
"socialpredict/handlers/math/probabilities/wpam"
"socialpredict/models"
"testing"
"time"
)

type TestCase struct {
Name string
Bets []models.Bet
ProbabilityChanges []wpam.ProbabilityChange
S_YES int64
S_NO int64
CoursePayouts []dbpm.CourseBetPayout
F_YES float64
F_NO float64
ExpectedF_YES float64
ExpectedF_NO float64
ScaledPayouts []int64
AdjustedScaledPayouts []int64
AggregatedPositions []dbpm.MarketPosition
NetPositions []dbpm.MarketPosition
}

var now = time.Now() // Capture current time for consistent test data

var TestCases = []TestCase{
{
Name: "Prevent simultaneous shares held",
Bets: []models.Bet{
{
Amount: 3,
Outcome: "YES",
Username: "user1",
PlacedAt: time.Date(2024, 5, 18, 5, 7, 31, 428975000, time.UTC),
MarketID: 3,
},
{
Amount: 1,
Outcome: "NO",
Username: "user1",
PlacedAt: time.Date(2024, 5, 18, 5, 8, 13, 922665000, time.UTC),
MarketID: 3,
},
},
ProbabilityChanges: []wpam.ProbabilityChange{
{Probability: 0.5},
{Probability: 0.615385},
{Probability: 0.571429},
},
S_YES: 3,
S_NO: 1,
CoursePayouts: []dbpm.CourseBetPayout{
{Payout: 0.5999999999999999, Outcome: "YES"},
{Payout: 0.17500000000000004, Outcome: "NO"},
},
F_YES: 5.000000000000001,
F_NO: 5.714285714285713,
ExpectedF_YES: 5.000000,
ExpectedF_NO: 5.714286,
ScaledPayouts: []int64{3, 1},
AdjustedScaledPayouts: []int64{3, 1},
AggregatedPositions: []dbpm.MarketPosition{
{Username: "user1", YesSharesOwned: 3, NoSharesOwned: 1},
},
NetPositions: []dbpm.MarketPosition{
{Username: "user1", YesSharesOwned: 2, NoSharesOwned: 0},
},
},
{
Name: "infinity avoidance",
Bets: []models.Bet{
{
Amount: 1,
Outcome: "YES",
Username: "user2",
PlacedAt: now,
MarketID: 1,
},
{
Amount: -1,
Outcome: "YES",
Username: "user2",
PlacedAt: now.Add(time.Minute),
MarketID: 1,
},
{
Amount: 1,
Outcome: "NO",
Username: "user1",
PlacedAt: now.Add(2 * time.Minute),
MarketID: 1,
},
{
Amount: -1,
Outcome: "NO",
Username: "user1",
PlacedAt: now.Add(3 * time.Minute),
MarketID: 1,
},
{
Amount: 1,
Outcome: "NO",
Username: "user1",
PlacedAt: now.Add(4 * time.Minute),
MarketID: 1,
},
},
ProbabilityChanges: []wpam.ProbabilityChange{
{Probability: 0.50},
{Probability: 0.545455},
{Probability: 0.50},
{Probability: 0.454545},
{Probability: 0.50},
{Probability: 0.454545},
},
S_YES: 0,
S_NO: 1,
CoursePayouts: []dbpm.CourseBetPayout{
{Payout: 0.25, Outcome: "YES"},
{Payout: -0.5, Outcome: "YES"},
{Payout: 0.25, Outcome: "NO"},
{Payout: -0, Outcome: "NO"}, // golang math.Round() rounds to -0 and +0
{Payout: 0.25, Outcome: "NO"},
},
F_YES: 0,
F_NO: 2,
ExpectedF_YES: 0,
ExpectedF_NO: 2,
ScaledPayouts: []int64{0, 0, 1, 0, 1},
AdjustedScaledPayouts: []int64{0, 0, 1, 0, 0},
AggregatedPositions: []dbpm.MarketPosition{
{Username: "user1", YesSharesOwned: 0, NoSharesOwned: 1},
{Username: "user2", YesSharesOwned: 0, NoSharesOwned: 0},
},
NetPositions: []dbpm.MarketPosition{
{Username: "user1", YesSharesOwned: 0, NoSharesOwned: 1},
{Username: "user2", YesSharesOwned: 0, NoSharesOwned: 0},
},
},
}

func TestCalculateMarketProbabilitiesWPAM(t *testing.T) {
for _, tc := range TestCases {
t.Run(tc.Name, func(t *testing.T) {
// Call the function under test
probChanges := wpam.CalculateMarketProbabilitiesWPAM(tc.Bets[0].PlacedAt, tc.Bets)

if len(probChanges) != len(tc.ProbabilityChanges) {
t.Fatalf("expected %d probability changes, got %d", len(tc.ProbabilityChanges), len(probChanges))
}

for i, pc := range probChanges {
expected := tc.ProbabilityChanges[i]
if pc.Probability != expected.Probability {
t.Errorf("at index %d, expected probability %f, got %f", i, expected.Probability, pc.Probability)
}
}
})
}
}
2 changes: 2 additions & 0 deletions backend/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func Start() {
// markets display, market information
router.HandleFunc("/v0/markets", marketshandlers.ListMarketsHandler).Methods("GET")
router.HandleFunc("/v0/markets/{marketId}", marketshandlers.MarketDetailsHandler).Methods("GET")
router.HandleFunc("/v0/marketprojection/{marketId}/{amount}/{outcome}/", marketshandlers.ProjectNewProbabilityHandler).Methods("GET")

// handle market positions, get trades
router.HandleFunc("/v0/markets/bets/{marketId}", betshandlers.MarketBetsDisplayHandler).Methods("GET")
router.HandleFunc("/v0/markets/positions/{marketId}", positions.MarketDBPMPositionsHandler).Methods("GET")
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/test_scenarios.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package test
package tests

import (
"socialpredict/handlers/math/outcomes/dbpm"
Expand Down
27 changes: 0 additions & 27 deletions backend/tests/wpam/wpammarketprobabilities_test.go

This file was deleted.

Loading