mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-03 23:16:53 +00:00
depth: Add methods to derive liquidity allowances on orderbooks by volume and slippage (#962)
* depth: methods to derive liquidity impact details * depth: fix comments on link list methods * depth: fix whoopsie * Update exchanges/orderbook/linked_list_test.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * glorious: nits * orderbook: standardise methods to GCT math package * averagePrice: implementation (WIP) * ll: hmmmmmm * continued * orderbook: reworked functions * WIP * orderbook: add tests link up with RPC * orderbook: refined calculations, add tests (WIP) * orderbook: add tests finalise/verify remove state until next PR if needed * rpcserver/orderbook: remove redundant type and change wording * linter: fix * Update exchanges/orderbook/linked_list.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * gctcli: noobed it up * orderbook: work work work (yesterday) * depth: WIP and testing * orderbook: improve calculations for bids traversal * orderbook: adjust tests * orderbook: linters/nits * orderbook: fix error returns and add asset to whalebomb * orderbook: drop error when full book is potentially consumed * Update cmd/gctcli/orderbook.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * orderbook: Add tests and nits * glorious: nits * backtester: handle new errors * grpc: linter * orderbook: remove pesky t.Log()s * glorious: nits (yesterday) * depth/gctcli: Add in purchase requirements into orderbook movement, will also standardize in next commit after tests are fixed. * orderbook: standardize and overhaul * orderbook: update comments, update tests * rpcserver: add average ordercost and amounts * depth: add spread and imbalance methods * linter: fix * glorious: nits * orderbook: don't purge price, rn test. * glorious: codes nits * linter: fix * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * thrasher: nits Co-authored-by: Scott <gloriousCode@users.noreply.github.com> Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
@@ -1297,7 +1297,11 @@ func (s *RPCServer) SimulateOrder(ctx context.Context, r *gctrpc.SimulateOrderRe
|
||||
buy = false
|
||||
}
|
||||
|
||||
result := o.SimulateOrder(r.Amount, buy)
|
||||
result, err := o.SimulateOrder(r.Amount, buy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp gctrpc.SimulateOrderResponse
|
||||
for x := range result.Orders {
|
||||
resp.Orders = append(resp.Orders, &gctrpc.OrderbookItem{
|
||||
@@ -1332,12 +1336,17 @@ func (s *RPCServer) WhaleBomb(ctx context.Context, r *gctrpc.WhaleBombRequest) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = checkParams(r.Exchange, exch, asset.Spot, p)
|
||||
a, err := asset.New(r.AssetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o, err := exch.FetchOrderbook(ctx, p, asset.Spot)
|
||||
err = checkParams(r.Exchange, exch, a, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o, err := exch.FetchOrderbook(ctx, p, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -5333,3 +5342,226 @@ func (s *RPCServer) GetMarginRatesHistory(ctx context.Context, r *gctrpc.GetMarg
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetOrderbookMovement using the requested amount simulates a buy or sell and
|
||||
// returns the nominal/impact percentages and costings.
|
||||
func (s *RPCServer) GetOrderbookMovement(ctx context.Context, r *gctrpc.GetOrderbookMovementRequest) (*gctrpc.GetOrderbookMovementResponse, error) {
|
||||
exch, err := s.GetExchangeByName(r.Exchange)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
as, err := asset.New(r.Asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pair, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pair.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
|
||||
err = checkParams(r.Exchange, exch, as, pair)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
depth, err := orderbook.GetDepth(exch.GetName(), pair, as)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isRest, err := depth.IsRESTSnapshot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateProtocol := "WEBSOCKET"
|
||||
if isRest {
|
||||
updateProtocol = "REST"
|
||||
}
|
||||
|
||||
var move *orderbook.Movement
|
||||
var bought, sold, side string
|
||||
if r.Sell {
|
||||
move, err = depth.HitTheBidsFromBest(r.Amount, r.Purchase)
|
||||
bought = pair.Quote.Upper().String()
|
||||
sold = pair.Base.Upper().String()
|
||||
side = order.Bid.String()
|
||||
} else {
|
||||
move, err = depth.LiftTheAsksFromBest(r.Amount, r.Purchase)
|
||||
bought = pair.Base.Upper().String()
|
||||
sold = pair.Quote.Upper().String()
|
||||
side = order.Ask.String()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gctrpc.GetOrderbookMovementResponse{
|
||||
NominalPercentage: move.NominalPercentage,
|
||||
ImpactPercentage: move.ImpactPercentage,
|
||||
SlippageCost: move.SlippageCost,
|
||||
CurrencyBought: bought,
|
||||
Bought: move.Purchased,
|
||||
CurrencySold: sold,
|
||||
Sold: move.Sold,
|
||||
SideAffected: side,
|
||||
UpdateProtocol: updateProtocol,
|
||||
FullOrderbookSideConsumed: move.FullBookSideConsumed,
|
||||
NoSlippageOccurred: move.ImpactPercentage == 0,
|
||||
StartPrice: move.StartPrice,
|
||||
EndPrice: move.EndPrice,
|
||||
AverageOrderCost: move.AverageOrderCost,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetOrderbookAmountByNominal using the requested nominal percentage requirement
|
||||
// returns the amount on orderbook that can fit without exceeding that value.
|
||||
func (s *RPCServer) GetOrderbookAmountByNominal(ctx context.Context, r *gctrpc.GetOrderbookAmountByNominalRequest) (*gctrpc.GetOrderbookAmountByNominalResponse, error) {
|
||||
exch, err := s.GetExchangeByName(r.Exchange)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
as, err := asset.New(r.Asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pair, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pair.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
|
||||
err = checkParams(r.Exchange, exch, as, pair)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
depth, err := orderbook.GetDepth(exch.GetName(), pair, as)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isRest, err := depth.IsRESTSnapshot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateProtocol := "WEBSOCKET"
|
||||
if isRest {
|
||||
updateProtocol = "REST"
|
||||
}
|
||||
|
||||
var nominal *orderbook.Movement
|
||||
var selling, buying, side string
|
||||
if r.Sell {
|
||||
nominal, err = depth.HitTheBidsByNominalSlippageFromBest(r.NominalPercentage)
|
||||
selling = pair.Upper().Base.String()
|
||||
buying = pair.Upper().Quote.String()
|
||||
side = order.Bid.String()
|
||||
} else {
|
||||
nominal, err = depth.LiftTheAsksByNominalSlippageFromBest(r.NominalPercentage)
|
||||
buying = pair.Upper().Base.String()
|
||||
selling = pair.Upper().Quote.String()
|
||||
side = order.Ask.String()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &gctrpc.GetOrderbookAmountByNominalResponse{
|
||||
AmountRequired: nominal.Sold,
|
||||
CurrencySelling: selling,
|
||||
AmountReceived: nominal.Purchased,
|
||||
CurrencyBuying: buying,
|
||||
SideAffected: side,
|
||||
ApproximateNominalSlippagePercentage: nominal.NominalPercentage,
|
||||
UpdateProtocol: updateProtocol,
|
||||
FullOrderbookSideConsumed: nominal.FullBookSideConsumed,
|
||||
StartPrice: nominal.StartPrice,
|
||||
EndPrice: nominal.EndPrice,
|
||||
AverageOrderCost: nominal.AverageOrderCost,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetOrderbookAmountByImpact using the requested impact percentage requirement
|
||||
// determines the amount on orderbook that can fit that will slip the orderbook.
|
||||
func (s *RPCServer) GetOrderbookAmountByImpact(ctx context.Context, r *gctrpc.GetOrderbookAmountByImpactRequest) (*gctrpc.GetOrderbookAmountByImpactResponse, error) {
|
||||
exch, err := s.GetExchangeByName(r.Exchange)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
as, err := asset.New(r.Asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pair, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pair.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
|
||||
err = checkParams(r.Exchange, exch, as, pair)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
depth, err := orderbook.GetDepth(exch.GetName(), pair, as)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isRest, err := depth.IsRESTSnapshot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateProtocol := "WEBSOCKET"
|
||||
if isRest {
|
||||
updateProtocol = "REST"
|
||||
}
|
||||
|
||||
var impact *orderbook.Movement
|
||||
var selling, buying, side string
|
||||
if r.Sell {
|
||||
impact, err = depth.HitTheBidsByImpactSlippageFromBest(r.ImpactPercentage)
|
||||
selling = pair.Upper().Base.String()
|
||||
buying = pair.Upper().Quote.String()
|
||||
side = order.Bid.String()
|
||||
} else {
|
||||
impact, err = depth.LiftTheAsksByImpactSlippageFromBest(r.ImpactPercentage)
|
||||
buying = pair.Upper().Base.String()
|
||||
selling = pair.Upper().Quote.String()
|
||||
side = order.Ask.String()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &gctrpc.GetOrderbookAmountByImpactResponse{
|
||||
AmountRequired: impact.Sold,
|
||||
CurrencySelling: selling,
|
||||
AmountReceived: impact.Purchased,
|
||||
CurrencyBuying: buying,
|
||||
SideAffected: side,
|
||||
ApproximateImpactSlippagePercentage: impact.ImpactPercentage,
|
||||
UpdateProtocol: updateProtocol,
|
||||
FullOrderbookSideConsumed: impact.FullBookSideConsumed,
|
||||
StartPrice: impact.StartPrice,
|
||||
EndPrice: impact.EndPrice,
|
||||
AverageOrderCost: impact.AverageOrderCost,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user