Skip to content
This repository was archived by the owner on Jul 17, 2021. It is now read-only.

Commit a5c7807

Browse files
committed
Add Precision and API support for gridbot
1 parent ee92b35 commit a5c7807

6 files changed

Lines changed: 74 additions & 45 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"reflect-metadata": "^0.1.13",
4747
"rimraf": "^3.0.2",
4848
"rxjs": "^6.5.4",
49-
"sand-ex": "^0.1.1",
49+
"sand-ex": "^0.1.3",
5050
"synaptic": "^1.1.4",
5151
"talib": "^1.1.3",
5252
"tulind": "^0.8.18",

src/__test__/gridbot/gridbot.spec.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,14 @@ describe('#Gridbot', () => {
8282
});
8383

8484
it('should Constructor set the properties', async () => {
85-
expect(gridBot.quotePerGrid).toBe(gridBotConfig.balanceQuote / gridBotConfig.gridQuantity);
85+
86+
let sumGridQuote = 0;
87+
88+
gridBot.grids.forEach(e => {
89+
sumGridQuote += e.maxQuantity * e.priceLow;
90+
});
91+
92+
expect(sumGridQuote).toBeLessThanOrEqual(gridBotConfig.balanceQuote);
8693
expect(gridBot.grids).toHaveLength(gridBotConfig.gridQuantity);
8794
});
8895

@@ -128,10 +135,10 @@ describe('#Perfect Gridbot', () => {
128135
expect(grid.activeOrderId).toBeGreaterThan(1);
129136
});
130137

131-
expect(ExchangeBalance).toMatchObject({ balanceQuote: 1049.874949438876 });
138+
expect(ExchangeBalance).toMatchObject({ balanceQuote: 1047.28074283 });
132139
expect(gridBot).toMatchObject({
133140
balanceAsset: 0,
134-
balanceQuote: 1049.874949438876,
141+
balanceQuote: 1047.2807428449998,
135142
});
136143
});
137144

@@ -158,9 +165,9 @@ describe('#Perfect Gridbot', () => {
158165
});
159166

160167

161-
expect(ExchangeBalance).toMatchObject({ balanceAsset: 0.1028258926975 });
168+
expect(ExchangeBalance).toMatchObject({ balanceAsset: 0.09748776 });
162169
expect(gridBot).toMatchObject({
163-
balanceAsset: 0.1028258926975,
170+
balanceAsset: 0.09748776,
164171
});
165172
});
166173

@@ -187,7 +194,7 @@ describe('#Perfect Gridbot', () => {
187194
});
188195

189196

190-
expect(ExchangeBalance).toMatchObject({ balanceAsset: 0, balanceQuote: 1099.749898877752 });
197+
expect(ExchangeBalance).toMatchObject({ balanceAsset: 0, balanceQuote: 1094.56148566 });
191198

192199
});
193200

src/grid_bot/index.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type GridBotConfig = {
2020
gridQuantity: number;
2121
balanceQuote: number;
2222
fee: number;
23+
precision?: number;
2324
};
2425

2526
type Grid = {
@@ -33,37 +34,49 @@ type Grid = {
3334
export class GridBot {
3435
balanceQuote: number;
3536
balanceAsset: number;
36-
quotePerGrid: number;
37+
assetPerGrid: number;
3738
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3839
orderHistory: any[];
3940
grids: Grid[];
4041
exchange: SandExchange;
4142
syncedOrders: Set<number>;
4243
private readonly fee: number;
44+
private readonly precision: number;
4345

4446
constructor(config: GridBotConfig) {
4547
this.orderHistory = [];
4648
this.syncedOrders = new Set();
4749
this.balanceQuote = config.balanceQuote;
4850
this.balanceAsset = 0;
4951
this.fee = config.fee;
52+
this.precision = config.precision || DEFAULT_PRECISION;
5053

5154
this.exchange = new SandExchange({
5255
balanceAsset: this.balanceAsset,
5356
balanceQuote: this.balanceQuote,
5457
fee: this.fee,
58+
precision: this.precision,
5559
});
5660

57-
// Get last money partition ignore round-robin parts
58-
this.quotePerGrid = this.balanceQuote / config.gridQuantity;
61+
const pricePerStep = (config.priceHigh - config.priceLow) / config.gridQuantity;
62+
const totalPriceStep = (config.gridQuantity ** 2 + config.gridQuantity) / 2; // n * n + n / 2
63+
64+
this.assetPerGrid = this.balanceQuote / (config.gridQuantity * config.priceLow + totalPriceStep * pricePerStep);
65+
this.assetPerGrid = floor(this.assetPerGrid, DEFAULT_PRECISION);
5966

6067
this.grids = [...Array(config.gridQuantity)].map((_, i) => {
6168
const priceLow = config.priceLow + ((config.priceHigh - config.priceLow) / config.gridQuantity) * i;
6269
const priceHigh = config.priceLow + ((config.priceHigh - config.priceLow) / config.gridQuantity) * (i + 1);
63-
const maxQuantity = floor(this.quotePerGrid / priceLow, DEFAULT_PRECISION);
70+
6471
const ownedQuantity = 0;
6572

66-
return { priceLow, priceHigh, maxQuantity, ownedQuantity, activeOrderId: null };
73+
return {
74+
priceLow,
75+
priceHigh,
76+
maxQuantity: this.assetPerGrid,
77+
ownedQuantity,
78+
activeOrderId: null,
79+
};
6780
});
6881
}
6982

@@ -82,7 +95,7 @@ export class GridBot {
8295
const grindIndex = this.grids.findIndex(grid => grid.activeOrderId === order.orderId);
8396

8497
if (order.side === OrderSide.BUY) {
85-
const bookedQuantity = order.executedQty * (1 - this.fee);
98+
const bookedQuantity = floor(order.executedQty * (1 - this.fee), this.precision);
8699
this.balanceAsset += bookedQuantity;
87100
this.balanceQuote -= order.executedQty * order.price;
88101

@@ -102,6 +115,7 @@ export class GridBot {
102115
};
103116
}
104117

118+
this.orderHistory.push(order);
105119
this.syncedOrders.add(order.orderId);
106120
}
107121
});

src/httpserver/controllers/backtest.controller.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { OptimizeConfig, BacktestService } from '../services/backtest.service';
55

66
@Controller('backtest')
77
export class BacktestController {
8-
constructor(private backtestService: BacktestService) { }
8+
constructor(private backtestService: BacktestService) {}
99

1010
@Post('optimize')
1111
async optimize(@Body() optimizeConfig: OptimizeConfig): Promise<any> {
@@ -17,16 +17,4 @@ export class BacktestController {
1717
throw new InternalServerErrorException();
1818
}
1919
}
20-
21-
@Post('gridbot')
22-
async gridbot(@Body() optimizeConfig: OptimizeConfig): Promise<any> {
23-
try {
24-
return await this.backtestService.optimize(optimizeConfig);
25-
} catch (err) {
26-
logger.error(`NestJS API error, ${err}`);
27-
28-
throw new InternalServerErrorException();
29-
}
30-
}
31-
3220
}

src/httpserver/controllers/gridbot.controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { Controller, Post, InternalServerErrorException, Body } from '@nestjs/common';
22

33
import { logger } from '../../logger';
4-
import { GridbotService } from '../services/gridbot.service';
4+
import { GridbotService, GridbotConfig } from '../services/gridbot.service';
55

66
@Controller('gridbot')
77
export class GridbotController {
8-
constructor(private gridbotService: GridbotService) { }
8+
constructor(private gridbotService: GridbotService) {}
99

1010
@Post('backtest')
11-
async backtest(@Body() gridbotConfig: any): Promise<any> {
11+
async backtest(@Body() gridbotConfig: GridbotConfig): Promise<any> {
1212
try {
1313
return await this.gridbotService.backtest(gridbotConfig);
1414
} catch (err) {
Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
import { Injectable } from '@nestjs/common';
22
import { OHLCV } from 'sand-ex/build/types';
3-
import _ from 'lodash';
3+
// import _ from 'lodash';
44

55
import tradePairs from '../../tradepairs/tradepairs';
66
import { GridBotConfig, GridBot } from '../../grid_bot';
77

8+
export interface GridbotConfig {
9+
exchange: string;
10+
symbol: string;
11+
priceLow: number;
12+
priceHigh: number;
13+
gridQuantity: number;
14+
fee: number;
15+
rangeInDays: number;
16+
}
17+
818
@Injectable()
919
export class GridbotService {
10-
async backtest(config: any): Promise<any> {
11-
const exchange = 'binance';
12-
const symbol = 'BTC/USDT';
13-
const candleLimit = 1440 * 30;
20+
async backtest(gridbotConfig: GridbotConfig): Promise<any> {
21+
const exchange = gridbotConfig.exchange ?? 'binance';
22+
const symbol = gridbotConfig.symbol ?? 'BTC/USDT';
23+
const { rangeInDays, priceLow, priceHigh, gridQuantity } = gridbotConfig;
24+
const fee = gridbotConfig.fee ?? 0.00075; // 0.0075% / 100,
25+
26+
const candleLimit = 1440 * rangeInDays;
27+
28+
const testQuote = 100000;
1429

1530
const candleData = await tradePairs.getCandlestickFromDB(exchange, symbol, 60, candleLimit);
1631

@@ -19,27 +34,32 @@ export class GridbotService {
1934

2035
// Strategy optimizer, helper function
2136
const gridBotConfig: GridBotConfig = {
22-
priceLow: 7500,
23-
priceHigh: 10000,
24-
gridQuantity: 8,
25-
balanceQuote: 10000,
26-
fee: 0.00075, // 0.0075% / 100,
37+
priceLow,
38+
priceHigh,
39+
gridQuantity,
40+
balanceQuote: testQuote,
41+
fee,
2742
};
2843

2944
const gridBot = new GridBot(gridBotConfig);
3045

31-
console.log(candleSticks.length);
46+
const profits = [];
3247

3348
for (let i = 0; i < candleSticks.length; i++) {
3449
gridBot.update((candleSticks[i] as unknown) as OHLCV);
35-
}
3650

37-
console.log(gridBot.exchange.getOrders().length);
51+
profits.push(gridBot.balanceQuote + gridBot.balanceAsset * candleSticks[candleSticks.length - 1][4]);
52+
}
3853

39-
console.log(gridBot.balanceAsset);
40-
console.log(gridBot.balanceQuote + gridBot.balanceAsset * candleSticks[candleSticks.length - 1][4]);
54+
const totalEndBalance = gridBot.balanceQuote + gridBot.balanceAsset * candleSticks[candleSticks.length - 1][4];
4155

42-
return '';
56+
return {
57+
balanceAsset: gridBot.balanceAsset,
58+
balanceQuote: gridBot.balanceQuote,
59+
totalEndBalance,
60+
profitPct: (totalEndBalance / 100000 - 1) * 100,
61+
orderHistory: gridBot.orderHistory,
62+
};
4363
}
4464
}
4565
}

0 commit comments

Comments
 (0)