-
Notifications
You must be signed in to change notification settings - Fork 0
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
lh-api #4
lh-api #4
Changes from all commits
a96429a
17d9473
3ba7195
2930ba0
33e8aa0
dfcc353
1ce67df
2b8f15f
3fc7e1a
2463a52
b20759a
8e20f96
d63b549
a1845b2
745ae70
2f31ee3
5a41dee
fb284e4
4c16648
ada26fd
96eb820
97f48b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.vscode/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package redisrepo | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/google/uuid" | ||
"github.com/orbs-network/order-book/models" | ||
"github.com/orbs-network/order-book/utils/logger" | ||
"github.com/orbs-network/order-book/utils/logger/logctx" | ||
) | ||
|
||
// //////////////////////////////////////////////// | ||
type OrderIter struct { | ||
index int | ||
ids []string | ||
redis *redisRepository | ||
} | ||
|
||
func (i *OrderIter) Next(ctx context.Context) *models.Order { | ||
if i.index >= len(i.ids) { | ||
logctx.Error(ctx, "Error iterator reached last element") | ||
return nil | ||
} | ||
|
||
// increment index | ||
i.index = i.index + 1 | ||
// get order | ||
orderId, err := uuid.Parse(i.ids[i.index]) | ||
if err != nil { | ||
logctx.Error(ctx, "Error parsing bid order id", logger.Error(err)) | ||
return nil | ||
} | ||
order, err := i.redis.FindOrderById(ctx, orderId) | ||
if err != nil { | ||
logctx.Error(ctx, "Error fetching order", logger.Error(err)) | ||
return nil | ||
} | ||
|
||
return order | ||
} | ||
|
||
func (i *OrderIter) HasNext() bool { | ||
return i.index < (len(i.ids) - 1) | ||
} | ||
|
||
// //////////////////////////////////////////////// | ||
func (r *redisRepository) GetMinAsk(ctx context.Context, symbol models.Symbol) models.OrderIter { | ||
key := CreateSellSidePricesKey(symbol) | ||
|
||
// Min ask price for selling | ||
orderIDs, err := r.client.ZRange(ctx, key, 0, -1).Result() | ||
if err != nil { | ||
logctx.Error(ctx, "Error fetching asks", logger.Error(err)) | ||
} | ||
// create order iter | ||
return &OrderIter{ | ||
index: -1, | ||
ids: orderIDs, | ||
redis: r, | ||
} | ||
|
||
} | ||
|
||
// //////////////////////////////////////////////// | ||
func (r *redisRepository) GetMaxBid(ctx context.Context, symbol models.Symbol) models.OrderIter { | ||
key := CreateBuySidePricesKey(symbol) | ||
|
||
// Min ask price for selling | ||
orderIDs, err := r.client.ZRevRange(ctx, key, 0, -1).Result() | ||
|
||
if err != nil { | ||
logctx.Error(ctx, "Error fetching bids", logger.Error(err)) | ||
} | ||
// create order iter | ||
return &OrderIter{ | ||
index: -1, | ||
ids: orderIDs, | ||
redis: r, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think you are using this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will remove it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. taking it back, ofcourse it is used in t he redis impl |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package redisrepo | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/orbs-network/order-book/models" | ||
) | ||
|
||
func (r *redisRepository) StoreAuction(ctx context.Context, auctionID string, fillOrders []models.FilledOrder) error { | ||
// auctionId:<ID>: [{orderID: <ID>, filledAmount: <amount>}, ...}] | ||
panic("not implemented") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package mocks | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/orbs-network/order-book/models" | ||
) | ||
|
||
type OrderIter struct { | ||
Error error | ||
Order *models.Order | ||
ShouldHaveNext bool | ||
} | ||
|
||
func (o *OrderIter) HasNext() bool { | ||
return o.ShouldHaveNext | ||
} | ||
|
||
func (o *OrderIter) Next(ctx context.Context) *models.Order { | ||
return o.Order | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package models | ||
|
||
import ( | ||
"github.com/google/uuid" | ||
"github.com/shopspring/decimal" | ||
) | ||
|
||
type AmountOut struct { | ||
AmountOut decimal.Decimal | ||
FillOrders []FilledOrder | ||
} | ||
|
||
type FilledOrder struct { | ||
OrderId uuid.UUID | ||
Amount decimal.Decimal | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package models | ||
|
||
import "context" | ||
|
||
type OrderIter interface { | ||
HasNext() bool | ||
Next(ctx context.Context) *Order | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package service | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/orbs-network/order-book/models" | ||
"github.com/orbs-network/order-book/utils/logger" | ||
"github.com/orbs-network/order-book/utils/logger/logctx" | ||
"github.com/shopspring/decimal" | ||
) | ||
|
||
// orderID->amount bought or sold in A token always | ||
|
||
func (s *Service) GetAmountOut(ctx context.Context, auctionId string, symbol models.Symbol, side models.Side, amountIn decimal.Decimal) (models.AmountOut, error) { | ||
|
||
var it models.OrderIter | ||
var res models.AmountOut | ||
var err error | ||
if side == models.BUY { | ||
it = s.orderBookStore.GetMinAsk(ctx, symbol) | ||
res, err = getAmountOutInAToken(ctx, it, amountIn) | ||
|
||
} else { // SELL | ||
it = s.orderBookStore.GetMaxBid(ctx, symbol) | ||
res, err = getAmountOutInBToken(ctx, it, amountIn) | ||
} | ||
if err != nil { | ||
logctx.Error(ctx, "getAmountOutIn failed", logger.Error(err)) | ||
return models.AmountOut{}, err | ||
} | ||
err = s.orderBookStore.StoreAuction(ctx, auctionId, res.FillOrders) | ||
if err != nil { | ||
logctx.Error(ctx, "StoreAuction failed", logger.Error(err)) | ||
return models.AmountOut{}, err | ||
} | ||
return res, nil | ||
} | ||
|
||
// PAIR/SYMBOL A-B (ETH-USDC) | ||
// amount in B token (USD) | ||
// amount out A token (ETH) | ||
func getAmountOutInAToken(ctx context.Context, it models.OrderIter, amountInB decimal.Decimal) (models.AmountOut, error) { | ||
amountOutA := decimal.NewFromInt(0) | ||
var fillOrders []models.FilledOrder | ||
var order *models.Order | ||
for it.HasNext() && amountInB.IsPositive() { | ||
order = it.Next(ctx) | ||
// max Spend in B token for this order | ||
orderSizeB := order.Price.Mul(order.Size) | ||
// spend the min of orderSizeB/amountInB | ||
spendB := decimal.Min(orderSizeB, amountInB) | ||
|
||
// Gain | ||
gainA := spendB.Div(order.Price) | ||
|
||
// sub-add | ||
amountInB = amountInB.Sub(spendB) | ||
logctx.Info(ctx, "StoreAuction failed") | ||
amountOutA = amountOutA.Add(gainA) | ||
|
||
// res | ||
logctx.Info(ctx, fmt.Sprintf("append FilledOrder gainA: %s", gainA.String())) | ||
logctx.Info(ctx, fmt.Sprintf("append FilledOrder spendB: %s", spendB.String())) | ||
fillOrders = append(fillOrders, models.FilledOrder{OrderId: order.Id, Amount: gainA}) | ||
} | ||
// not all is Spent - error | ||
if amountInB.IsPositive() { | ||
logctx.Warn(ctx, models.ErrInsufficientLiquity.Error()) | ||
return models.AmountOut{}, models.ErrInsufficientLiquity | ||
} | ||
logctx.Info(ctx, fmt.Sprintf("append FilledOrder amountOutA: %s", amountOutA.String())) | ||
return models.AmountOut{AmountOut: amountOutA, FillOrders: fillOrders}, nil | ||
} | ||
|
||
// PAIR/SYMBOL A-B (ETH-USDC) | ||
// amount in A token (ETH) | ||
// amount out B token (USD) | ||
func getAmountOutInBToken(ctx context.Context, it models.OrderIter, amountInA decimal.Decimal) (models.AmountOut, error) { | ||
amountOutB := decimal.NewFromInt(0) | ||
var order *models.Order | ||
var fillOrders []models.FilledOrder | ||
for it.HasNext() && amountInA.IsPositive() { | ||
order = it.Next(ctx) | ||
|
||
// Spend | ||
spendA := decimal.Min(order.Size, amountInA) | ||
fmt.Println("sizeA ", spendA.String()) | ||
|
||
// Gain | ||
gainB := order.Price.Mul(spendA) | ||
fmt.Println("gainB ", gainB.String()) | ||
|
||
// sub-add | ||
amountInA = amountInA.Sub(spendA) | ||
amountOutB = amountOutB.Add(gainB) | ||
|
||
// res | ||
logctx.Info(ctx, fmt.Sprintf("append FilledOrder spendA: %s", spendA.String())) | ||
logctx.Info(ctx, fmt.Sprintf("append FilledOrder gainB: %s", gainB.String())) | ||
fillOrders = append(fillOrders, models.FilledOrder{OrderId: order.Id, Amount: spendA}) | ||
} | ||
if amountInA.IsPositive() { | ||
logctx.Warn(ctx, models.ErrInsufficientLiquity.Error()) | ||
return models.AmountOut{}, models.ErrInsufficientLiquity | ||
} | ||
logctx.Info(ctx, fmt.Sprintf("append FilledOrder amountOutB: %s", amountOutB.String())) | ||
return models.AmountOut{AmountOut: amountOutB, FillOrders: fillOrders}, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why initialise this with -1? Better to start with 0 so it aligns with how we usually think about array indexes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i like the use of -1 as in "not been used" rather than 0 which is more implicit in case of only one order in the array