Commit 94cca7a5 authored by Cranky Kernel's avatar Cranky Kernel

history: paginate the results

parent 3c0c9e42
Pipeline #53988411 passed with stage
in 12 minutes and 44 seconds
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
- Provide visiable health status in the - Provide visiable health status in the
UI. https://gitlab.com/crankykernel/maker/issues/42 UI. https://gitlab.com/crankykernel/maker/issues/42
- Add simple Binance balance view. - Add simple Binance balance view.
- Paginate trade history view. Too many trades can harm performance,
server side and in the browser.
[Full Changelog](https://gitlab.com/crankykernel/maker/compare/0.3.2...master) [Full Changelog](https://gitlab.com/crankykernel/maker/compare/0.3.2...master)
......
...@@ -264,6 +264,8 @@ func DbGetTradeByID(tradeId string) (*types.TradeState, error) { ...@@ -264,6 +264,8 @@ func DbGetTradeByID(tradeId string) (*types.TradeState, error) {
type TradeQueryOptions struct { type TradeQueryOptions struct {
IsClosed bool IsClosed bool
Offset int64
Limit int64
} }
func DbQueryTrades(options TradeQueryOptions) ([]types.TradeState, error) { func DbQueryTrades(options TradeQueryOptions) ([]types.TradeState, error) {
...@@ -279,6 +281,11 @@ func DbQueryTrades(options TradeQueryOptions) ([]types.TradeState, error) { ...@@ -279,6 +281,11 @@ func DbQueryTrades(options TradeQueryOptions) ([]types.TradeState, error) {
sql = fmt.Sprintf("%s WHERE %s", sql, strings.Join(where, "AND ")) sql = fmt.Sprintf("%s WHERE %s", sql, strings.Join(where, "AND "))
} }
sql = fmt.Sprintf("%s ORDER BY json_extract(binance_trade.data, '$.CloseTime') DESC ", sql)
sql = fmt.Sprintf("%s LIMIT %d OFFSET %d ", sql, options.Limit, options.Offset)
log.Printf("%s", sql)
rows, err := db.Query(sql) rows, err := db.Query(sql)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -530,16 +530,36 @@ func staticAssetHandler() http.HandlerFunc { ...@@ -530,16 +530,36 @@ func staticAssetHandler() http.HandlerFunc {
} }
} }
func queryTradesHandler(w http.ResponseWriter, r *http.Request) { // AKA trade history.
func tradeQueryHandler(w http.ResponseWriter, r *http.Request) {
queryOptions := db.TradeQueryOptions{} queryOptions := db.TradeQueryOptions{}
queryOptions.IsClosed = true queryOptions.IsClosed = true
queryOptions.Offset = 0
queryOptions.Limit = 50
if r.FormValue("offset") != "" {
offset, err := strconv.ParseInt(r.FormValue("offset"), 10, 64)
if err != nil {
log.WithError(err).WithFields(log.Fields{
"handler": "tradeQuery",
"offset": r.FormValue("offset"),
}).Errorf("Failed to parse offset as int64")
} else {
queryOptions.Offset = offset
}
}
log.Println(r.FormValue("offset"))
log.Println(r.FormValue("limit"))
s := time.Now()
trades, err := db.DbQueryTrades(queryOptions) trades, err := db.DbQueryTrades(queryOptions)
if err != nil { if err != nil {
log.WithError(err).Error("Failed to load trades from database.") log.WithError(err).Error("Failed to load trades from database.")
return return
} }
e := time.Now().Sub(s)
log.Printf("Found %d trades in %v", len(trades), e)
WriteJsonResponse(w, http.StatusOK, trades) WriteJsonResponse(w, http.StatusOK, trades)
} }
......
...@@ -272,7 +272,7 @@ func ServerMain() { ...@@ -272,7 +272,7 @@ func ServerMain() {
binanceProxyHandlers := NewBinanceProxyHandlers() binanceProxyHandlers := NewBinanceProxyHandlers()
binanceProxyHandlers.RegisterHandlers(router) binanceProxyHandlers.RegisterHandlers(router)
router.HandleFunc("/api/trade/query", queryTradesHandler). router.HandleFunc("/api/trade/query", tradeQueryHandler).
Methods("GET") Methods("GET")
router.HandleFunc("/api/trade/{tradeId}", router.HandleFunc("/api/trade/{tradeId}",
getTradeHandler).Methods("GET") getTradeHandler).Methods("GET")
......
...@@ -239,13 +239,13 @@ Loop: ...@@ -239,13 +239,13 @@ Loop:
} }
type MakerMessage struct { type MakerMessage struct {
Type MakerMessageType `json:"messageType"` Type MakerMessageType `json:"messageType"`
Trade *types.TradeState `json:"trade,omitempty"` Trade *types.TradeState `json:"trade,omitempty"`
TradeID string `json:"tradeId,omitempty"` TradeID string `json:"tradeId,omitempty"`
BinanceAggTrade *binanceapi.StreamAggTrade `json:"binanceAggTrade,omitempty"` BinanceAggTrade *binanceapi.StreamAggTrade `json:"binanceAggTrade,omitempty"`
BinanceOutboundAccountInfo *binanceapi.StreamOutboundAccountInfo `json:"binanceOutboundAccountInfo,omitempty"` BinanceOutboundAccountInfo *binanceapi.StreamOutboundAccountInfo `json:"binanceOutboundAccountInfo,omitempty"`
Notice *clientnotificationservice.Notice `json:"notice,omitempty"` Notice *clientnotificationservice.Notice `json:"notice,omitempty"`
Health *healthservice.State `json:"health,omitempty"` Health *healthservice.State `json:"health,omitempty"`
} }
type MakerMessageType string type MakerMessageType string
......
<div class="container-fluid"> <div class="container-fluid">
<br/> <nav class="navbar navbar-expand navbar-light bg-light">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
{{ (page * pageSize) + 1 }}
to
{{ (page * pageSize) + trades.length }}
of
{{ trades.length < pageSize ? page * pageSize + trades.length : ((page * pageSize) + trades.length) + '+' }}
</li>
</ul>
<div class="btn-group" role="group">
<button type="button"
[attr.disabled]="page === 0 ? '' : null"
class="btn btn-outline-primary"
(click)="gotoPreviousPage()"
>Previous
</button>
<button type="button"
class="btn btn-outline-primary"
[attr.disabled]="trades.length < pageSize ? '' : null"
(click)="gotoNextPage()"
>Next
</button>
</div>
<button class="btn btn-outline-secondary ml-2" (click)="refresh()">Refresh</button>
</nav>
<table class="table table-sm"> <table class="table table-sm">
<tr> <tr>
......
...@@ -13,37 +13,72 @@ ...@@ -13,37 +13,72 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import {AfterViewInit, Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {TradeState} from '../maker.service'; import {TradeState} from '../maker.service';
import {AppTradeState, toAppTradeState} from '../trade-table/trade-table.component'; import {toAppTradeState} from '../trade-table/trade-table.component';
import {MakerApiService} from "../maker-api.service"; import {MakerApiService} from "../maker-api.service";
import {ActivatedRoute, Router} from "@angular/router";
@Component({ @Component({
selector: 'app-history', selector: 'app-history',
templateUrl: './history.component.html', templateUrl: './history.component.html',
styleUrls: ['./history.component.scss'] styleUrls: ['./history.component.scss']
}) })
export class HistoryComponent implements OnInit, AfterViewInit { export class HistoryComponent implements OnInit {
trades: TradeState[] = []; trades: TradeState[] = [];
constructor(private makerApi: MakerApiService) { page: number = 0;
pageSize = 50;
constructor(private makerApi: MakerApiService,
private route: ActivatedRoute,
private router: Router) {
} }
ngOnInit() { ngOnInit() {
this.route.params.subscribe((params) => {
console.log(params);
if (params.page !== undefined) {
this.page = +params.page;
}
this.refresh()
});
} }
ngAfterViewInit() { refresh() {
this.makerApi.get("/api/trade/query") let offset = this.page * this.pageSize;
.subscribe((trades: TradeState[]) => { console.log(offset);
this.trades = trades.map((trade) => { this.makerApi.get("/api/trade/query", {
params: {
offset: offset,
limit: this.pageSize,
}
}).subscribe((trades: TradeState[]) => {
this.trades = trades
.map((trade) => {
return toAppTradeState(trade); return toAppTradeState(trade);
}).sort(this.sort); });
}); });
}
gotoPreviousPage() {
let page = this.page < 1 ? 0 : this.page - 1;
let params = {
page: page,
};
this.router.navigate([".", params], {
queryParamsHandling: "merge",
});
} }
private sort(a: AppTradeState, b: AppTradeState) { gotoNextPage() {
return new Date(b.OpenTime).getTime() - let params = {
new Date(a.OpenTime).getTime(); page: this.page + 1,
};
this.router.navigate([".", params], {
queryParamsHandling: "merge",
});
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment