forked from stellar/go
/
order_book.go
115 lines (95 loc) · 2.32 KB
/
order_book.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package simplepath
import (
"errors"
"math/big"
sq "github.com/Masterminds/squirrel"
"github.com/stellar/go/services/horizon/internal/assets"
"github.com/stellar/go/services/horizon/internal/db2/core"
"github.com/stellar/go/xdr"
)
// ErrNotEnough represents an error that occurs when pricing a trade on an
// orderbook. This error occurs when the orderbook cannot fulfill the
// requested amount.
var ErrNotEnough = errors.New("not enough depth")
type orderBook struct {
Selling xdr.Asset
Buying xdr.Asset
Q *core.Q
}
func (ob *orderBook) Cost(source xdr.Asset, sourceAmount xdr.Int64) (result xdr.Int64, err error) {
// load offers from the two assets
var (
// selling/buying types
st, bt xdr.AssetType
// selling/buying codes
sc, bc string
// selling/buying issuers
si, bi string
)
err = ob.Selling.Extract(&st, &sc, &si)
if err != nil {
return
}
err = ob.Buying.Extract(&bt, &bc, &bi)
if err != nil {
return
}
sql := sq.
Select("amount", "pricen", "priced", "offerid").
From("offers").
Where(sq.Eq{
"sellingassettype": st,
"COALESCE(sellingassetcode, '')": sc,
"COALESCE(sellingissuer, '')": si}).
Where(sq.Eq{
"buyingassettype": bt,
"COALESCE(buyingassetcode, '')": bc,
"COALESCE(buyingissuer, '')": bi})
inverted := assets.Equals(source, ob.Buying)
if !inverted {
sql = sql.OrderBy("price ASC")
} else {
sql = sql.OrderBy("price DESC")
}
rows, err := ob.Q.Query(sql)
if err != nil {
return
}
defer rows.Close()
var (
needed = int64(sourceAmount)
cost int64
)
for rows.Next() {
// load data from the row
var available, pricen, priced, offerid int64
if inverted {
err = rows.Scan(&available, &priced, &pricen, &offerid)
available = mul(available, pricen, priced)
} else {
err = rows.Scan(&available, &pricen, &priced, &offerid)
}
if err != nil {
return
}
if available >= needed {
cost += mul(needed, pricen, priced)
result = xdr.Int64(cost)
return
}
cost += mul(available, pricen, priced)
needed -= available
}
err = ErrNotEnough
return
}
// mul multiplies the input amount by the input price
func mul(amount int64, pricen int64, priced int64) int64 {
var r, n, d big.Int
r.SetInt64(amount)
n.SetInt64(pricen)
d.SetInt64(priced)
r.Mul(&r, &n)
r.Quo(&r, &d)
return r.Int64()
}