Update market form on input change
This commit is contained in:
parent
bd7b2715bd
commit
df7726e313
|
@ -78,6 +78,15 @@
|
||||||
padding: 0 0.25em;
|
padding: 0 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
color: var(--muted);
|
||||||
|
border-color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request {
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 24px
|
font-size: 24px
|
||||||
}
|
}
|
||||||
|
@ -87,6 +96,10 @@
|
||||||
aspect-ratio: 560/315;
|
aspect-ratio: 560/315;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
color: var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
.text-muted {
|
.text-muted {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +124,7 @@
|
||||||
color: var(--fg-success);
|
color: var(--fg-success);
|
||||||
}
|
}
|
||||||
|
|
||||||
.neon.success:hover {
|
.neon.success:hover, .neon.success.active {
|
||||||
background-color: var(--fg-success);
|
background-color: var(--fg-success);
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
}
|
}
|
||||||
|
@ -121,7 +134,7 @@
|
||||||
color: var(--fg-error);
|
color: var(--fg-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
.neon.error:hover {
|
.neon.error:hover, .neon.error.active {
|
||||||
background-color: var(--fg-error);
|
background-color: var(--fg-error);
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -57,6 +57,7 @@ func HandleInvoice(sc context.Context) echo.HandlerFunc {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
marketRegexp = regexp.MustCompile("^create market (?P<id>[0-9]+)$")
|
marketRegexp = regexp.MustCompile("^create market (?P<id>[0-9]+)$")
|
||||||
|
orderRegexp = regexp.MustCompile("^create order [0-9]+ for market (?P<id>[0-9]+)$")
|
||||||
)
|
)
|
||||||
|
|
||||||
func toRedirectUrl(description string) templ.SafeURL {
|
func toRedirectUrl(description string) templ.SafeURL {
|
||||||
|
@ -65,5 +66,9 @@ func toRedirectUrl(description string) templ.SafeURL {
|
||||||
marketId := m[marketRegexp.SubexpIndex("id")]
|
marketId := m[marketRegexp.SubexpIndex("id")]
|
||||||
return templ.SafeURL(fmt.Sprintf("/market/%s", marketId))
|
return templ.SafeURL(fmt.Sprintf("/market/%s", marketId))
|
||||||
}
|
}
|
||||||
|
if m = orderRegexp.FindStringSubmatch(description); m != nil {
|
||||||
|
marketId := m[marketRegexp.SubexpIndex("id")]
|
||||||
|
return templ.SafeURL(fmt.Sprintf("/market/%s", marketId))
|
||||||
|
}
|
||||||
return "/"
|
return "/"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.ekzyis.com/ekzyis/delphi.market/lib/lmsr"
|
"git.ekzyis.com/ekzyis/delphi.market/lib/lmsr"
|
||||||
|
@ -98,15 +99,26 @@ func HandleCreate(sc context.Context) echo.HandlerFunc {
|
||||||
func HandleMarket(sc context.Context) echo.HandlerFunc {
|
func HandleMarket(sc context.Context) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
var (
|
var (
|
||||||
db = sc.Db
|
db = sc.Db
|
||||||
ctx = c.Request().Context()
|
ctx = c.Request().Context()
|
||||||
id = c.Param("id")
|
id = c.Param("id")
|
||||||
m = types.Market{}
|
quantity = c.QueryParam("q")
|
||||||
u = types.User{}
|
q int64
|
||||||
l = types.LSMR{}
|
m = types.Market{}
|
||||||
err error
|
u = types.User{}
|
||||||
|
l = types.LMSR{}
|
||||||
|
total float64
|
||||||
|
quote0 = types.MarketQuote{}
|
||||||
|
quote1 = types.MarketQuote{}
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if quantity == "" {
|
||||||
|
q = 1
|
||||||
|
} else if q, err = strconv.ParseInt(quantity, 10, 64); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "q must be integer")
|
||||||
|
}
|
||||||
|
|
||||||
if err = db.QueryRowContext(ctx, ""+
|
if err = db.QueryRowContext(ctx, ""+
|
||||||
"SELECT m.id, m.question, m.description, m.created_at, m.end_date, m.lmsr_b, "+
|
"SELECT m.id, m.question, m.description, m.created_at, m.end_date, m.lmsr_b, "+
|
||||||
"u.id, u.name, u.created_at, u.ln_pubkey, u.nostr_pubkey, u.msats "+
|
"u.id, u.name, u.created_at, u.ln_pubkey, u.nostr_pubkey, u.msats "+
|
||||||
|
@ -149,41 +161,151 @@ func HandleMarket(sc context.Context) echo.HandlerFunc {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pages.Market(m, types.MarketP{
|
total = lmsr.Quote(l.B, l.Q1, l.Q2, int(q))
|
||||||
Pyes: lmsr.Price(l.B, l.Q2, l.Q1),
|
quote0 = types.MarketQuote{
|
||||||
Pno: lmsr.Price(l.B, l.Q1, l.Q2), // prices
|
Outcome: 0,
|
||||||
|
AvgPrice: total / float64(q),
|
||||||
|
TotalPrice: total,
|
||||||
|
Reward: float64(q) - total,
|
||||||
|
}
|
||||||
|
|
||||||
}).Render(context.RenderContext(sc, c), c.Response().Writer)
|
total = lmsr.Quote(l.B, l.Q2, l.Q1, int(q))
|
||||||
|
quote1 = types.MarketQuote{
|
||||||
|
Outcome: 1,
|
||||||
|
AvgPrice: total / float64(q),
|
||||||
|
TotalPrice: total,
|
||||||
|
Reward: float64(q) - total,
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages.Market(m, quote0, quote1).Render(context.RenderContext(sc, c), c.Response().Writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPrice(sc context.Context) echo.HandlerFunc {
|
func HandleOrder(sc context.Context) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
db = sc.Db
|
db = sc.Db
|
||||||
ctx = c.Request().Context()
|
lnd = sc.Lnd
|
||||||
id = c.Param("id")
|
tx *sql.Tx
|
||||||
m = types.Market{}
|
ctx = c.Request().Context()
|
||||||
u = types.User{}
|
u = c.Get("session").(types.User)
|
||||||
err error
|
id = c.Param("id")
|
||||||
|
quantity = c.FormValue("q")
|
||||||
|
outcome = c.FormValue("o")
|
||||||
|
q int64
|
||||||
|
o int64
|
||||||
|
m = types.Market{}
|
||||||
|
mU = types.User{}
|
||||||
|
l = types.LMSR{}
|
||||||
|
totalF float64
|
||||||
|
total int
|
||||||
|
hash lntypes.Hash
|
||||||
|
paymentRequest string
|
||||||
|
expiry = int64(60)
|
||||||
|
expiresAt = time.Now().Add(time.Second * time.Duration(expiry))
|
||||||
|
invoiceId int
|
||||||
|
invDescription string
|
||||||
|
orderId int
|
||||||
|
qr templ.Component
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if quantity == "" {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "q must be given")
|
||||||
|
} else if q, err = strconv.ParseInt(quantity, 10, 64); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "q must be integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
if outcome == "" {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "o must be given")
|
||||||
|
} else if o, err = strconv.ParseInt(outcome, 10, 64); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "o must be integer")
|
||||||
|
}
|
||||||
|
if o < 0 && o > 1 {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "o must be 0 or 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: refactor since this uses same queries as function above
|
||||||
if err = db.QueryRowContext(ctx, ""+
|
if err = db.QueryRowContext(ctx, ""+
|
||||||
"SELECT m.id, m.question, m.description, m.created_at, m.end_date, "+
|
"SELECT m.id, m.question, m.description, m.created_at, m.end_date, m.lmsr_b, "+
|
||||||
"u.id, u.name, u.created_at, u.ln_pubkey, u.nostr_pubkey, msats "+
|
"u.id, u.name, u.created_at, u.ln_pubkey, u.nostr_pubkey, u.msats "+
|
||||||
"FROM markets m JOIN users u ON m.user_id = u.id "+
|
"FROM markets m "+
|
||||||
"WHERE m.id = $1", id).Scan(
|
"JOIN users u ON m.user_id = u.id "+
|
||||||
&m.Id, &m.Question, &m.Description, &m.CreatedAt, &m.EndDate,
|
"JOIN invoices i ON m.invoice_id = i.id "+
|
||||||
&u.Id, &u.Name, &u.CreatedAt, &u.LnPubkey, &u.NostrPubkey, &u.Msats); err != nil {
|
"WHERE m.id = $1 AND i.confirmed_at IS NOT NULL", id).Scan(
|
||||||
|
&m.Id, &m.Question, &m.Description, &m.CreatedAt, &m.EndDate, &l.B,
|
||||||
|
&mU.Id, &mU.Name, &mU.CreatedAt, &mU.LnPubkey, &mU.NostrPubkey, &mU.Msats); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return echo.NewHTTPError(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.User = mU
|
||||||
|
|
||||||
|
if err = db.QueryRowContext(ctx, ""+
|
||||||
|
"SELECT "+
|
||||||
|
"COUNT(o.quantity) FILTER(WHERE o.outcome = 0) AS q1, "+
|
||||||
|
"COUNT(o.quantity) FILTER(WHERE o.outcome = 1) AS q2 "+
|
||||||
|
"FROM orders o "+
|
||||||
|
"JOIN markets m ON o.market_id = m.id "+
|
||||||
|
"JOIN invoices i ON o.invoice_id = i.id "+
|
||||||
|
"WHERE o.market_id = $1 AND i.confirmed_at IS NOT NULL", id).Scan(
|
||||||
|
&l.Q1, &l.Q2); err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return echo.NewHTTPError(http.StatusNotFound)
|
return echo.NewHTTPError(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.User = u
|
if o == 0 {
|
||||||
|
totalF = lmsr.Quote(l.B, l.Q1, l.Q2, int(q))
|
||||||
|
} else if o == 1 {
|
||||||
|
totalF = lmsr.Quote(l.B, l.Q2, l.Q1, int(q))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
total = int(math.Round(totalF * 1000))
|
||||||
|
|
||||||
|
if tx, err = db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash, paymentRequest, err = lnd.Client.AddInvoice(ctx,
|
||||||
|
&invoicesrpc.AddInvoiceData{
|
||||||
|
Value: lnwire.MilliSatoshi(total),
|
||||||
|
Expiry: expiry,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.QueryRowContext(ctx, ""+
|
||||||
|
"INSERT INTO invoices (user_id, msats, hash, bolt11, expires_at) "+
|
||||||
|
"VALUES ($1, $2, $3, $4, $5) "+
|
||||||
|
"RETURNING id",
|
||||||
|
u.Id, total, hash.String(), paymentRequest, expiresAt).Scan(&invoiceId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.QueryRowContext(ctx, ""+
|
||||||
|
"INSERT INTO orders (market_id, user_id, quantity, outcome, invoice_id) "+
|
||||||
|
"VALUES ($1, $2, $3, $4, $5) "+
|
||||||
|
"RETURNING id",
|
||||||
|
id, u.Id, q, o, invoiceId).Scan(&orderId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
invDescription = fmt.Sprintf("create order %d for market %s", orderId, id)
|
||||||
|
if _, err = tx.ExecContext(ctx, ""+
|
||||||
|
"UPDATE invoices SET description = $1 WHERE id = $2",
|
||||||
|
invDescription, invoiceId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
qr = components.Invoice(hash.String(), paymentRequest, total, int(expiry), false, toRedirectUrl(invDescription))
|
||||||
|
|
||||||
|
return components.Modal(qr).Render(context.RenderContext(sc, c), c.Response().Writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ templ Head() {
|
||||||
}'
|
}'
|
||||||
/>
|
/>
|
||||||
<script src="/js/htmx.js" integrity="sha384-Xh+GLLi0SMFPwtHQjT72aPG19QvKB8grnyRbYBNIdHWc2NkCrz65jlU7YrzO6qRp" crossorigin="anonymous"></script>
|
<script src="/js/htmx.js" integrity="sha384-Xh+GLLi0SMFPwtHQjT72aPG19QvKB8grnyRbYBNIdHWc2NkCrz65jlU7YrzO6qRp" crossorigin="anonymous"></script>
|
||||||
|
<script defer src="/js/alpine.js" crossorigin="anonymous"></script>
|
||||||
if ctx.Value(c.EnvContextKey) == "development" {
|
if ctx.Value(c.EnvContextKey) == "development" {
|
||||||
<script defer src="/js/hotreload.js"></script>
|
<script defer src="/js/hotreload.js"></script>
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package components
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ekzyis.com/ekzyis/delphi.market/types"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ MarketForm(m types.Market, outcome int, q types.MarketQuote) {
|
||||||
|
<form
|
||||||
|
id={ formId(outcome) }
|
||||||
|
autocomplete="off"
|
||||||
|
class="grid grid-cols-2 gap-3"
|
||||||
|
hx-post={ fmt.Sprintf("/market/%d/order", m.Id) }
|
||||||
|
hx-target="#modal"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-select="#modal"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="o" value={ fmt.Sprint(outcome) } />
|
||||||
|
<div class="none col-span-2 htmx-request" />
|
||||||
|
<label for="p">avg price per share:</label>
|
||||||
|
<div id="p">{formatPrice(q.AvgPrice)}</div>
|
||||||
|
<label for="q">how many?</label>
|
||||||
|
<input
|
||||||
|
id={ inputId(outcome) }
|
||||||
|
name="q"
|
||||||
|
class="text-black px-1"
|
||||||
|
type="number"
|
||||||
|
autofocus
|
||||||
|
hx-get={ fmt.Sprintf("/market/%d", m.Id) }
|
||||||
|
hx-replace-url="true"
|
||||||
|
hx-target={ fmt.Sprintf("#%s", formId(outcome)) }
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-select={ fmt.Sprintf("#%s", formId(outcome)) }
|
||||||
|
hx-trigger="input changed delay:1s"
|
||||||
|
hx-preserve
|
||||||
|
hx-disabled-elt="next button"
|
||||||
|
hx-indicator={ hxIndicator(outcome) }
|
||||||
|
/>
|
||||||
|
<label for="total">you pay:</label>
|
||||||
|
<div id="total">{formatPrice(q.TotalPrice)}</div>
|
||||||
|
<label for="reward">{ "if you win:" }</label>
|
||||||
|
<div id="reward">+{formatPrice(q.Reward)}</div>
|
||||||
|
<button type="submit" class="col-span-2">submit</button>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
|
||||||
|
func formId (outcome int) string {
|
||||||
|
return fmt.Sprintf("outcome-%d-form", outcome)
|
||||||
|
}
|
||||||
|
|
||||||
|
func inputId (outcome int) string {
|
||||||
|
return fmt.Sprintf("outcome-%d-q", outcome)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hxIndicator (outcome int) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"#%s>#p, #%s>#total, #%s>#reward",
|
||||||
|
formId(outcome), formId(outcome), formId(outcome))
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPrice(p float64) string {
|
||||||
|
return fmt.Sprintf("%v sats", strconv.FormatFloat(p, 'f', 3, 64))
|
||||||
|
}
|
|
@ -4,16 +4,17 @@ import (
|
||||||
"git.ekzyis.com/ekzyis/delphi.market/server/router/pages/components"
|
"git.ekzyis.com/ekzyis/delphi.market/server/router/pages/components"
|
||||||
"git.ekzyis.com/ekzyis/delphi.market/types"
|
"git.ekzyis.com/ekzyis/delphi.market/types"
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"strconv"
|
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Add countdown? Use or at least show somewhere precise timestamps?
|
// TODO: Add countdown? Use or at least show somewhere precise timestamps?
|
||||||
|
|
||||||
templ Market(m types.Market, p types.MarketP) {
|
templ Market(m types.Market, q0 types.MarketQuote, q1 types.MarketQuote) {
|
||||||
<html>
|
<html>
|
||||||
@components.Head()
|
@components.Head()
|
||||||
<body class="container">
|
<body
|
||||||
|
x-data="{ outcome: undefined }"
|
||||||
|
class="container"
|
||||||
|
hx-preserve>
|
||||||
@components.Nav()
|
@components.Nav()
|
||||||
<div id="content" class="flex flex-col">
|
<div id="content" class="flex flex-col">
|
||||||
<small>
|
<small>
|
||||||
|
@ -23,13 +24,38 @@ templ Market(m types.Market, p types.MarketP) {
|
||||||
<div class="text-center text-muted my-1">{humanize.Time(m.EndDate)}</div>
|
<div class="text-center text-muted my-1">{humanize.Time(m.EndDate)}</div>
|
||||||
<div class="text-center text-muted my-1"></div>
|
<div class="text-center text-muted my-1"></div>
|
||||||
<blockquote cite="ekzyis" class="p-4 mb-4 border-s-4 border-muted">
|
<blockquote cite="ekzyis" class="p-4 mb-4 border-s-4 border-muted">
|
||||||
{ m.Description }
|
if m.Description != "" {
|
||||||
|
m.Description
|
||||||
|
} else {
|
||||||
|
<empty>
|
||||||
|
}
|
||||||
<div class="text-muted text-right pt-4">― {m.User.Name}, { humanize.Time(m.CreatedAt) }</div>
|
<div class="text-muted text-right pt-4">― {m.User.Name}, { humanize.Time(m.CreatedAt) }</div>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<div class="flex justify-center my-1">
|
<div class="flex flex-col justify-center my-1">
|
||||||
<button class="neon success mx-1">BET YES</button>
|
<div class="flex flex-row justify-center">
|
||||||
<button class="neon error mx-1">BET NO</button>
|
<button
|
||||||
|
class="neon success mx-1"
|
||||||
|
x-on:click="outcome = outcome === 1 ? undefined : 1"
|
||||||
|
:class="{ 'active' : outcome === 1 }"
|
||||||
|
>
|
||||||
|
BET YES
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="neon error mx-1"
|
||||||
|
x-on:click="outcome = outcome === 0 ? undefined : 0"
|
||||||
|
:class="{ 'active' : outcome === 0 }"
|
||||||
|
>
|
||||||
|
BET NO
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="mx-auto my-5" x-show="outcome === 1">
|
||||||
|
@components.MarketForm(m, 1, q1)
|
||||||
|
</div>
|
||||||
|
<div class="mx-auto my-5" x-show="outcome === 0">
|
||||||
|
@components.MarketForm(m, 0, q0)
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@components.Modal(nil)
|
@components.Modal(nil)
|
||||||
@components.Footer()
|
@components.Footer()
|
||||||
|
@ -37,11 +63,3 @@ templ Market(m types.Market, p types.MarketP) {
|
||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatPrice(p float64) string {
|
|
||||||
return fmt.Sprintf("%v msats", strconv.FormatInt(pToPrice(p), 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
func pToPrice(p float64) int64 {
|
|
||||||
// 0.513 means 513 msats
|
|
||||||
return int64(p * 1e3)
|
|
||||||
}
|
|
|
@ -17,6 +17,7 @@ func Init(e *echo.Echo, sc Context) {
|
||||||
e.GET("/create", handler.HandleIndex(sc))
|
e.GET("/create", handler.HandleIndex(sc))
|
||||||
e.POST("/create", handler.HandleCreate(sc), middleware.SessionGuard(sc))
|
e.POST("/create", handler.HandleCreate(sc), middleware.SessionGuard(sc))
|
||||||
e.GET("/market/:id", handler.HandleMarket(sc))
|
e.GET("/market/:id", handler.HandleMarket(sc))
|
||||||
|
e.POST("/market/:id/order", handler.HandleOrder(sc), middleware.SessionGuard(sc))
|
||||||
e.GET("/about", handler.HandleAbout(sc))
|
e.GET("/about", handler.HandleAbout(sc))
|
||||||
|
|
||||||
e.GET("/login", handler.HandleAuth(sc, "login"))
|
e.GET("/login", handler.HandleAuth(sc, "login"))
|
||||||
|
|
|
@ -38,14 +38,15 @@ type Market struct {
|
||||||
EndDate time.Time
|
EndDate time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type LSMR struct {
|
type LMSR struct {
|
||||||
B float64
|
B float64
|
||||||
Q1 int
|
Q1 int
|
||||||
Q2 int
|
Q2 int
|
||||||
}
|
}
|
||||||
|
|
||||||
type MarketP struct {
|
type MarketQuote struct {
|
||||||
// probability of outcomes
|
Outcome int
|
||||||
Pyes float64
|
AvgPrice float64
|
||||||
Pno float64
|
TotalPrice float64
|
||||||
|
Reward float64
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue