2020-11-03 09:55:20 +00:00
package service
import (
2021-01-29 05:15:44 +00:00
"strconv"
"strings"
2020-11-03 09:55:20 +00:00
"github.com/jmoiron/sqlx"
2020-11-05 03:00:51 +00:00
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
2020-11-03 09:55:20 +00:00
"github.com/c9s/bbgo/pkg/types"
)
type OrderService struct {
DB * sqlx . DB
}
2020-11-05 03:00:51 +00:00
// QueryLast queries the last order from the database
2021-01-19 18:09:12 +00:00
func ( s * OrderService ) QueryLast ( ex types . ExchangeName , symbol string , isMargin bool , isIsolated bool ) ( * types . Order , error ) {
log . Infof ( "querying last order exchange = %s AND symbol = %s AND is_margin = %v AND is_isolated = %v" , ex , symbol , isMargin , isIsolated )
rows , err := s . DB . NamedQuery ( ` SELECT * FROM orders WHERE exchange = :exchange AND symbol = :symbol AND is_margin = :is_margin AND is_isolated = :is_isolated ORDER BY gid DESC LIMIT 1 ` , map [ string ] interface { } {
"exchange" : ex ,
"symbol" : symbol ,
"is_margin" : isMargin ,
"is_isolated" : isIsolated ,
2020-11-05 03:00:51 +00:00
} )
if err != nil {
return nil , errors . Wrap ( err , "query last order error" )
}
if rows . Err ( ) != nil {
return nil , rows . Err ( )
}
defer rows . Close ( )
if rows . Next ( ) {
var order types . Order
err = rows . StructScan ( & order )
return & order , err
}
return nil , rows . Err ( )
}
2021-01-29 09:52:13 +00:00
type AggOrder struct {
types . Order
AveragePrice * float64 ` json:"averagePrice" db:"average_price" `
}
2021-01-29 10:48:00 +00:00
type QueryOrdersOptions struct {
2021-01-29 05:15:44 +00:00
Exchange types . ExchangeName
Symbol string
LastGID int64
2021-01-29 10:48:00 +00:00
Ordering string
2021-01-29 05:15:44 +00:00
}
2021-01-29 10:48:00 +00:00
func ( s * OrderService ) Query ( options QueryOrdersOptions ) ( [ ] AggOrder , error ) {
2021-02-03 09:12:06 +00:00
sql := genOrderSQL ( options )
rows , err := s . DB . NamedQuery ( sql , map [ string ] interface { } {
"exchange" : options . Exchange ,
"symbol" : options . Symbol ,
"gid" : options . LastGID ,
} )
if err != nil {
return nil , err
}
defer rows . Close ( )
return s . scanAggRows ( rows )
}
func genOrderSQL ( options QueryOrdersOptions ) string {
2021-01-29 05:15:44 +00:00
// ascending
ordering := "ASC"
2021-01-29 10:48:00 +00:00
switch v := strings . ToUpper ( options . Ordering ) ; v {
case "DESC" , "ASC" :
ordering = options . Ordering
2021-01-29 05:15:44 +00:00
}
var where [ ] string
if options . LastGID > 0 {
switch ordering {
case "ASC" :
where = append ( where , "gid > :gid" )
case "DESC" :
where = append ( where , "gid < :gid" )
}
}
if len ( options . Exchange ) > 0 {
where = append ( where , "exchange = :exchange" )
}
if len ( options . Symbol ) > 0 {
where = append ( where , "symbol = :symbol" )
}
2021-01-29 09:52:13 +00:00
sql := ` SELECT orders.*, IFNULL(SUM(t.price * t.quantity)/SUM(t.quantity), orders.price) AS average_price FROM orders ` +
` LEFT JOIN trades AS t ON (t.order_id = orders.order_id) `
2021-01-29 05:15:44 +00:00
if len ( where ) > 0 {
sql += ` WHERE ` + strings . Join ( where , " AND " )
}
2021-01-29 09:52:13 +00:00
sql += ` GROUP BY orders.gid `
sql += ` ORDER BY orders.gid ` + ordering
2021-01-29 05:15:44 +00:00
sql += ` LIMIT ` + strconv . Itoa ( 500 )
2021-01-29 10:34:03 +00:00
log . Info ( sql )
2021-02-03 09:12:06 +00:00
return sql
2021-01-29 09:52:13 +00:00
}
func ( s * OrderService ) scanAggRows ( rows * sqlx . Rows ) ( orders [ ] AggOrder , err error ) {
for rows . Next ( ) {
var order AggOrder
if err := rows . StructScan ( & order ) ; err != nil {
return nil , err
}
orders = append ( orders , order )
}
return orders , rows . Err ( )
2020-11-03 09:55:20 +00:00
}
func ( s * OrderService ) scanRows ( rows * sqlx . Rows ) ( orders [ ] types . Order , err error ) {
for rows . Next ( ) {
var order types . Order
if err := rows . StructScan ( & order ) ; err != nil {
return nil , err
}
orders = append ( orders , order )
}
return orders , rows . Err ( )
}
2021-02-06 08:05:21 +00:00
func ( s * OrderService ) Insert ( order types . Order ) ( err error ) {
if s . DB . DriverName ( ) == "mysql" {
_ , err = s . DB . NamedExec ( `
2021-01-19 15:33:06 +00:00
INSERT INTO orders ( exchange , order_id , client_order_id , order_type , status , symbol , price , stop_price , quantity , executed_quantity , side , is_working , time_in_force , created_at , updated_at , is_margin , is_isolated )
VALUES ( : exchange , : order_id , : client_order_id , : order_type , : status , : symbol , : price , : stop_price , : quantity , : executed_quantity , : side , : is_working , : time_in_force , : created_at , : updated_at , : is_margin , : is_isolated )
2020-11-05 05:35:04 +00:00
ON DUPLICATE KEY UPDATE status = : status , executed_quantity = : executed_quantity , is_working = : is_working , updated_at = : updated_at ` , order )
2021-02-06 08:05:21 +00:00
return err
}
_ , err = s . DB . NamedExec ( `
INSERT INTO orders ( exchange , order_id , client_order_id , order_type , status , symbol , price , stop_price , quantity , executed_quantity , side , is_working , time_in_force , created_at , updated_at , is_margin , is_isolated )
VALUES ( : exchange , : order_id , : client_order_id , : order_type , : status , : symbol , : price , : stop_price , : quantity , : executed_quantity , : side , : is_working , : time_in_force , : created_at , : updated_at , : is_margin , : is_isolated )
` , order )
2020-11-03 09:55:20 +00:00
return err
}