@gbozee/ultimate 0.0.2-20 → 0.0.2-22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +112 -32
- package/dist/index.js +774 -71
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -25320,7 +25320,7 @@ var require_websocket = __commonJS((exports, module) => {
|
|
|
25320
25320
|
var http = __require("http");
|
|
25321
25321
|
var net = __require("net");
|
|
25322
25322
|
var tls = __require("tls");
|
|
25323
|
-
var { randomBytes, createHash } = __require("crypto");
|
|
25323
|
+
var { randomBytes: randomBytes2, createHash } = __require("crypto");
|
|
25324
25324
|
var { Duplex, Readable } = __require("stream");
|
|
25325
25325
|
var { URL: URL2 } = __require("url");
|
|
25326
25326
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -25729,7 +25729,7 @@ var require_websocket = __commonJS((exports, module) => {
|
|
|
25729
25729
|
}
|
|
25730
25730
|
}
|
|
25731
25731
|
const defaultPort = isSecure ? 443 : 80;
|
|
25732
|
-
const key =
|
|
25732
|
+
const key = randomBytes2(16).toString("base64");
|
|
25733
25733
|
const request = isSecure ? https.request : http.request;
|
|
25734
25734
|
const protocolSet = new Set;
|
|
25735
25735
|
let perMessageDeflate;
|
|
@@ -32104,6 +32104,47 @@ class Client {
|
|
|
32104
32104
|
// src/database.ts
|
|
32105
32105
|
var import_socks_proxy_agent = __toESM(require_dist2(), 1);
|
|
32106
32106
|
var import_https_proxy_agent = __toESM(require_dist3(), 1);
|
|
32107
|
+
import {
|
|
32108
|
+
createCipheriv,
|
|
32109
|
+
createDecipheriv,
|
|
32110
|
+
randomBytes,
|
|
32111
|
+
scryptSync
|
|
32112
|
+
} from "crypto";
|
|
32113
|
+
function encryptObject(obj, password) {
|
|
32114
|
+
const jsonString = JSON.stringify(obj);
|
|
32115
|
+
const salt = randomBytes(16);
|
|
32116
|
+
const key = scryptSync(password, salt, 32);
|
|
32117
|
+
const iv = randomBytes(12);
|
|
32118
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
32119
|
+
const encrypted = Buffer.concat([
|
|
32120
|
+
cipher.update(jsonString, "utf8"),
|
|
32121
|
+
cipher.final()
|
|
32122
|
+
]);
|
|
32123
|
+
const authTag = cipher.getAuthTag();
|
|
32124
|
+
const resultBuffer = Buffer.concat([salt, iv, authTag, encrypted]);
|
|
32125
|
+
return resultBuffer.toString("base64");
|
|
32126
|
+
}
|
|
32127
|
+
function decryptObject(encryptedString, password) {
|
|
32128
|
+
try {
|
|
32129
|
+
const dataBuffer = Buffer.from(encryptedString, "base64");
|
|
32130
|
+
const salt = dataBuffer.subarray(0, 16);
|
|
32131
|
+
const iv = dataBuffer.subarray(16, 28);
|
|
32132
|
+
const authTag = dataBuffer.subarray(28, 44);
|
|
32133
|
+
const encrypted = dataBuffer.subarray(44);
|
|
32134
|
+
const key = scryptSync(password, salt, 32);
|
|
32135
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
32136
|
+
decipher.setAuthTag(authTag);
|
|
32137
|
+
const decrypted = Buffer.concat([
|
|
32138
|
+
decipher.update(encrypted),
|
|
32139
|
+
decipher.final()
|
|
32140
|
+
]);
|
|
32141
|
+
const jsonString = decrypted.toString("utf8");
|
|
32142
|
+
return JSON.parse(jsonString);
|
|
32143
|
+
} catch (error) {
|
|
32144
|
+
console.error("Decryption failed:", error.message);
|
|
32145
|
+
return null;
|
|
32146
|
+
}
|
|
32147
|
+
}
|
|
32107
32148
|
async function initPocketBaseClient(proxy_credentials) {
|
|
32108
32149
|
const pb = new Client(proxy_credentials.host);
|
|
32109
32150
|
await pb.collection("_superusers").authWithPassword(proxy_credentials.email, proxy_credentials.password);
|
|
@@ -32115,6 +32156,37 @@ class AppDatabase {
|
|
|
32115
32156
|
constructor(pb) {
|
|
32116
32157
|
this.pb = pb;
|
|
32117
32158
|
}
|
|
32159
|
+
getCredentials(password) {
|
|
32160
|
+
const credentials = this.pb.authStore.record.credentials;
|
|
32161
|
+
if (credentials) {
|
|
32162
|
+
return decryptObject(credentials, password);
|
|
32163
|
+
}
|
|
32164
|
+
return null;
|
|
32165
|
+
}
|
|
32166
|
+
async saveCredentials(password, credentials) {
|
|
32167
|
+
const encrypted = encryptObject(credentials, password);
|
|
32168
|
+
await this.pb.collection("_superusers").update(this.pb.authStore.record.id, {
|
|
32169
|
+
credentials: encrypted
|
|
32170
|
+
});
|
|
32171
|
+
}
|
|
32172
|
+
async addNewCredential(password, payload) {
|
|
32173
|
+
let credentials = this.getCredentials(password);
|
|
32174
|
+
if (credentials) {
|
|
32175
|
+
let found = false;
|
|
32176
|
+
for (const credential of credentials) {
|
|
32177
|
+
if (credential.name === payload.name && credential.exchange === payload.exchange) {
|
|
32178
|
+
credential.api_key = payload.api_key;
|
|
32179
|
+
credential.api_secret = payload.api_secret;
|
|
32180
|
+
found = true;
|
|
32181
|
+
break;
|
|
32182
|
+
}
|
|
32183
|
+
}
|
|
32184
|
+
if (!found) {
|
|
32185
|
+
credentials.push(payload);
|
|
32186
|
+
}
|
|
32187
|
+
await this.saveCredentials(password, credentials);
|
|
32188
|
+
}
|
|
32189
|
+
}
|
|
32118
32190
|
async getAllSymbolsFromPositions(options) {
|
|
32119
32191
|
const { no_position = false, kind = "long", custom_filter } = options || {};
|
|
32120
32192
|
let filter = custom_filter || (no_position ? `quantity = 0` : undefined);
|
|
@@ -32410,6 +32482,15 @@ class AppDatabase {
|
|
|
32410
32482
|
return { success: false, error: error.message };
|
|
32411
32483
|
}
|
|
32412
32484
|
}
|
|
32485
|
+
async getMoverExchangeInstances() {
|
|
32486
|
+
const result = await this.pb.collection("exchange_accounts").getFullList({
|
|
32487
|
+
filter: `totalRisk > 0 && movePercent > 0 && profit_percent > 0 && risk_reward > 0 && max_non_essential > 0`
|
|
32488
|
+
});
|
|
32489
|
+
return result;
|
|
32490
|
+
}
|
|
32491
|
+
async updateScheduledTrade(id, payload) {
|
|
32492
|
+
return await this.pb.collection("scheduled_trades").update(id, payload);
|
|
32493
|
+
}
|
|
32413
32494
|
async createOrUpdatePositionConfig(db_position, payload) {
|
|
32414
32495
|
let config = null;
|
|
32415
32496
|
if (db_position.config) {
|
|
@@ -32422,6 +32503,12 @@ class AppDatabase {
|
|
|
32422
32503
|
if (payload.profit_percent !== undefined) {
|
|
32423
32504
|
obj.profit_percent = payload.profit_percent;
|
|
32424
32505
|
}
|
|
32506
|
+
if (payload.place_tp !== undefined) {
|
|
32507
|
+
obj.place_tp = payload.place_tp;
|
|
32508
|
+
}
|
|
32509
|
+
if (payload.profit !== undefined) {
|
|
32510
|
+
obj.profit = payload.profit;
|
|
32511
|
+
}
|
|
32425
32512
|
return await this.pb.collection("scheduled_trades").update(db_position.config, obj);
|
|
32426
32513
|
} else {
|
|
32427
32514
|
const kind = payload.entry > payload.stop ? "long" : "short";
|
|
@@ -32436,7 +32523,9 @@ class AppDatabase {
|
|
|
32436
32523
|
stop: payload.stop,
|
|
32437
32524
|
risk_reward: payload.risk_reward,
|
|
32438
32525
|
risk: payload.risk,
|
|
32439
|
-
profit_percent: payload.profit_percent
|
|
32526
|
+
profit_percent: payload.profit_percent,
|
|
32527
|
+
place_tp: payload.place_tp !== undefined ? payload.place_tp : true,
|
|
32528
|
+
profit: payload.profit !== undefined ? payload.profit : config.profit
|
|
32440
32529
|
});
|
|
32441
32530
|
for (const _config of configs) {
|
|
32442
32531
|
if (_config.id !== config.id) {
|
|
@@ -32447,14 +32536,14 @@ class AppDatabase {
|
|
|
32447
32536
|
config = await this.pb.collection("scheduled_trades").create({
|
|
32448
32537
|
symbol: db_position.symbol,
|
|
32449
32538
|
account: db_position.account,
|
|
32450
|
-
profit: payload.risk,
|
|
32539
|
+
profit: payload.profit || payload.risk,
|
|
32451
32540
|
kind,
|
|
32452
32541
|
entry: payload.entry,
|
|
32453
32542
|
stop: payload.stop,
|
|
32454
32543
|
risk_reward: payload.risk_reward,
|
|
32455
32544
|
risk: payload.risk,
|
|
32456
32545
|
profit_percent: payload.profit_percent,
|
|
32457
|
-
place_tp: true
|
|
32546
|
+
place_tp: payload.place_tp !== undefined ? payload.place_tp : true
|
|
32458
32547
|
});
|
|
32459
32548
|
}
|
|
32460
32549
|
await this.pb.collection("positions").update(db_position.id, {
|
|
@@ -32700,6 +32789,12 @@ class AppDatabase {
|
|
|
32700
32789
|
});
|
|
32701
32790
|
return result;
|
|
32702
32791
|
}
|
|
32792
|
+
async hasExistingOrders(symbol) {
|
|
32793
|
+
const result = await this.pb.collection("orders").getFullList({
|
|
32794
|
+
filter: `symbol:lower="${symbol.toLowerCase()}"`
|
|
32795
|
+
});
|
|
32796
|
+
return result;
|
|
32797
|
+
}
|
|
32703
32798
|
async removeSymbolFromUnwindingMarkets(symbol) {
|
|
32704
32799
|
const result = await this.pb.collection("winding_down_markets").getFullList({
|
|
32705
32800
|
filter: `symbol:lower="${symbol.toLowerCase()}"`
|
|
@@ -34168,10 +34263,10 @@ async function analyzeCharts(params) {
|
|
|
34168
34263
|
return finalPairs;
|
|
34169
34264
|
}
|
|
34170
34265
|
async function getWeeklyKlines(payload) {
|
|
34171
|
-
const { client, symbol, limit = 20 } = payload;
|
|
34266
|
+
const { client, symbol, limit = 20, interval = "1w" } = payload;
|
|
34172
34267
|
const requestParams = {
|
|
34173
34268
|
symbol: symbol.toUpperCase(),
|
|
34174
|
-
interval
|
|
34269
|
+
interval
|
|
34175
34270
|
};
|
|
34176
34271
|
if (limit) {
|
|
34177
34272
|
requestParams.limit = limit;
|
|
@@ -34228,11 +34323,18 @@ async function getActiveSymbols(payload) {
|
|
|
34228
34323
|
const response = await client.getExchangeInfo();
|
|
34229
34324
|
return response.symbols;
|
|
34230
34325
|
}
|
|
34326
|
+
async function getAllOpenOrders(payload) {
|
|
34327
|
+
const { client } = payload;
|
|
34328
|
+
const response = await client.getAllOpenOrders();
|
|
34329
|
+
return response;
|
|
34330
|
+
}
|
|
34231
34331
|
|
|
34232
34332
|
class BinanceExchange {
|
|
34233
34333
|
client;
|
|
34234
|
-
|
|
34334
|
+
main_client;
|
|
34335
|
+
constructor(client, main_client) {
|
|
34235
34336
|
this.client = client;
|
|
34337
|
+
this.main_client = main_client;
|
|
34236
34338
|
}
|
|
34237
34339
|
async placeStopOrders(payload) {
|
|
34238
34340
|
if (payload.place) {
|
|
@@ -34249,6 +34351,22 @@ class BinanceExchange {
|
|
|
34249
34351
|
});
|
|
34250
34352
|
}
|
|
34251
34353
|
}
|
|
34354
|
+
async createLimitPurchaseOrders(payload) {
|
|
34355
|
+
const {
|
|
34356
|
+
orders,
|
|
34357
|
+
kind,
|
|
34358
|
+
decimal_places = "%.3f",
|
|
34359
|
+
price_places = "%.1f",
|
|
34360
|
+
symbol
|
|
34361
|
+
} = payload;
|
|
34362
|
+
const _orders = orders.map((order) => ({
|
|
34363
|
+
...order,
|
|
34364
|
+
price: order.entry,
|
|
34365
|
+
kind,
|
|
34366
|
+
side: kind.toLowerCase() === "long" ? "buy" : "sell"
|
|
34367
|
+
}));
|
|
34368
|
+
return await createLimitPurchaseOrders(this.client, symbol, price_places, decimal_places, _orders);
|
|
34369
|
+
}
|
|
34252
34370
|
async bulkPlaceLimitOrders(payload) {
|
|
34253
34371
|
const {
|
|
34254
34372
|
orders,
|
|
@@ -34378,20 +34496,25 @@ class BinanceExchange {
|
|
|
34378
34496
|
}
|
|
34379
34497
|
maxLeverage = Math.max(...brackets.brackets.map((b) => b.initialLeverage));
|
|
34380
34498
|
}
|
|
34381
|
-
|
|
34382
|
-
|
|
34383
|
-
|
|
34384
|
-
|
|
34499
|
+
try {
|
|
34500
|
+
await this.client.setLeverage({
|
|
34501
|
+
symbol: payload.symbol,
|
|
34502
|
+
leverage: payload.leverage || maxLeverage
|
|
34503
|
+
});
|
|
34504
|
+
} catch (error) {
|
|
34505
|
+
console.log("error", error);
|
|
34506
|
+
}
|
|
34385
34507
|
return maxLeverage;
|
|
34386
34508
|
}
|
|
34387
34509
|
async generateConfig(payload) {
|
|
34388
34510
|
const symbols = await getActiveSymbols({ client: this.client });
|
|
34389
34511
|
console.log("symbols", symbols);
|
|
34390
|
-
const { symbol, limit = 5 } = payload;
|
|
34512
|
+
const { symbol, limit = 5, interval = "1w" } = payload;
|
|
34391
34513
|
const klines = await getWeeklyKlines({
|
|
34392
34514
|
client: this.client,
|
|
34393
34515
|
symbol,
|
|
34394
|
-
limit
|
|
34516
|
+
limit,
|
|
34517
|
+
interval
|
|
34395
34518
|
});
|
|
34396
34519
|
const { support, resistance } = calculateSupportResistance(klines);
|
|
34397
34520
|
const target = symbols.find((s2) => s2.symbol === symbol);
|
|
@@ -34402,14 +34525,19 @@ class BinanceExchange {
|
|
|
34402
34525
|
if (f.filterType === "MIN_NOTIONAL")
|
|
34403
34526
|
minNotional = parseFloat(f.notional);
|
|
34404
34527
|
});
|
|
34528
|
+
if (["BTCUSDT", "BTCUSDC"].includes(symbol)) {
|
|
34529
|
+
minNotional = 105;
|
|
34530
|
+
}
|
|
34531
|
+
const isBTC = ["BTCUSDT", "BTCUSDC"].includes(symbol);
|
|
34405
34532
|
const currentPrice = await getCurrentPrice(this.client, symbol);
|
|
34406
|
-
const price_places = `%.${getPricePlaces(currentPrice)}f`;
|
|
34533
|
+
const price_places = isBTC ? "%.1f" : `%.${getPricePlaces(currentPrice)}f`;
|
|
34407
34534
|
const decimal_places = `%.${target.quantityPrecision}f`;
|
|
34535
|
+
const min_size = to_f((minNotional || 0) / support, decimal_places);
|
|
34408
34536
|
const configObj = {
|
|
34409
34537
|
support,
|
|
34410
34538
|
resistance,
|
|
34411
34539
|
minNotional,
|
|
34412
|
-
min_size:
|
|
34540
|
+
min_size: isBTC ? 0.002 : min_size,
|
|
34413
34541
|
price_places,
|
|
34414
34542
|
decimal_places,
|
|
34415
34543
|
leverage: await this.setLeverage({ symbol }),
|
|
@@ -34427,18 +34555,23 @@ class BinanceExchange {
|
|
|
34427
34555
|
const delisted = movers.map((x) => x.symbol).filter((symbol) => !activeSymbols.includes(symbol));
|
|
34428
34556
|
const validMovers = movers.filter((m) => !delisted.includes(m.symbol)).filter((m) => activeSymbols.includes(m.symbol));
|
|
34429
34557
|
const activeMovers = [];
|
|
34430
|
-
|
|
34431
|
-
|
|
34558
|
+
const isActivePromises = validMovers.map(async (m) => {
|
|
34559
|
+
return await isSymbolActive({
|
|
34432
34560
|
client: this.client,
|
|
34433
34561
|
symbol: m.symbol
|
|
34434
34562
|
});
|
|
34435
|
-
|
|
34436
|
-
|
|
34563
|
+
});
|
|
34564
|
+
const isActiveResults = await Promise.all(isActivePromises);
|
|
34565
|
+
for (let i2 = 0;i2 < validMovers.length; i2++) {
|
|
34566
|
+
if (isActiveResults[i2]) {
|
|
34567
|
+
activeMovers.push(validMovers[i2]);
|
|
34437
34568
|
}
|
|
34438
34569
|
}
|
|
34439
34570
|
console.log(`Top movers:`, movers);
|
|
34440
34571
|
console.log(`Delisted movers:`, delisted);
|
|
34441
|
-
|
|
34572
|
+
const toBeDelisted = await this.getDelistedSpotSymbols();
|
|
34573
|
+
const _movers = activeMovers.filter((m) => !toBeDelisted.includes(m.symbol));
|
|
34574
|
+
return { movers: _movers, delisted };
|
|
34442
34575
|
}
|
|
34443
34576
|
async closePosition(payload) {
|
|
34444
34577
|
const { symbol, kind, price_places, decimal_places } = payload;
|
|
@@ -34451,6 +34584,21 @@ class BinanceExchange {
|
|
|
34451
34584
|
kind
|
|
34452
34585
|
});
|
|
34453
34586
|
}
|
|
34587
|
+
async getAllOpenSymbols() {
|
|
34588
|
+
const response = await getAllOpenOrders({ client: this.client });
|
|
34589
|
+
return Array.from(new Set(response.map((x) => x.symbol)));
|
|
34590
|
+
}
|
|
34591
|
+
async getDelistedSpotSymbols() {
|
|
34592
|
+
if (this.main_client) {
|
|
34593
|
+
const client = this.main_client;
|
|
34594
|
+
const response = await client.getDelistSchedule();
|
|
34595
|
+
if (response.length > 0) {
|
|
34596
|
+
return response[0].symbols;
|
|
34597
|
+
}
|
|
34598
|
+
return [];
|
|
34599
|
+
}
|
|
34600
|
+
return [];
|
|
34601
|
+
}
|
|
34454
34602
|
}
|
|
34455
34603
|
function getPricePlaces(target) {
|
|
34456
34604
|
const numStr = target.toString();
|
|
@@ -34949,8 +35097,10 @@ async function analyzeCharts2(params) {
|
|
|
34949
35097
|
|
|
34950
35098
|
class BybitExchange {
|
|
34951
35099
|
client;
|
|
34952
|
-
|
|
35100
|
+
main_client;
|
|
35101
|
+
constructor(client, main_client) {
|
|
34953
35102
|
this.client = client;
|
|
35103
|
+
this.main_client = main_client;
|
|
34954
35104
|
}
|
|
34955
35105
|
async placeStopOrders(payload) {
|
|
34956
35106
|
if (payload.place) {
|
|
@@ -35094,6 +35244,27 @@ class BybitExchange {
|
|
|
35094
35244
|
}
|
|
35095
35245
|
async closePosition(payload) {
|
|
35096
35246
|
}
|
|
35247
|
+
async getAllOpenSymbols() {
|
|
35248
|
+
return [];
|
|
35249
|
+
}
|
|
35250
|
+
async createLimitPurchaseOrders(payload) {
|
|
35251
|
+
const {
|
|
35252
|
+
orders,
|
|
35253
|
+
kind,
|
|
35254
|
+
decimal_places = "%.3f",
|
|
35255
|
+
price_places = "%.1f",
|
|
35256
|
+
symbol
|
|
35257
|
+
} = payload;
|
|
35258
|
+
return await createLimitPurchaseOrders2(this.client, symbol, price_places, decimal_places, orders.map((order) => ({
|
|
35259
|
+
...order,
|
|
35260
|
+
price: order.entry,
|
|
35261
|
+
kind,
|
|
35262
|
+
side: kind.toLowerCase() === "long" ? "buy" : "sell"
|
|
35263
|
+
})));
|
|
35264
|
+
}
|
|
35265
|
+
async getDelistedSpotSymbols() {
|
|
35266
|
+
return [];
|
|
35267
|
+
}
|
|
35097
35268
|
}
|
|
35098
35269
|
|
|
35099
35270
|
// src/helpers/accounts.ts
|
|
@@ -35497,6 +35668,7 @@ function buildConfig(app_config, {
|
|
|
35497
35668
|
if (kind === "short") {
|
|
35498
35669
|
console.log("condition", condition, entry === stop);
|
|
35499
35670
|
}
|
|
35671
|
+
console.log({ entry, support: app_config.support, stop });
|
|
35500
35672
|
const result = entry === stop ? [] : condition ? instance.build_entry({
|
|
35501
35673
|
current_price: entry,
|
|
35502
35674
|
stop_loss: stop,
|
|
@@ -35530,8 +35702,8 @@ function get_app_config_and_max_size(config, payload) {
|
|
|
35530
35702
|
stop: payload.stop,
|
|
35531
35703
|
risk_per_trade: config.risk,
|
|
35532
35704
|
risk_reward: config.risk_reward || 199,
|
|
35533
|
-
support: config.support,
|
|
35534
|
-
resistance: config.resistance,
|
|
35705
|
+
support: to_f(config.support, config.price_places),
|
|
35706
|
+
resistance: to_f(config.resistance, config.price_places),
|
|
35535
35707
|
focus: payload.entry,
|
|
35536
35708
|
fee: 0,
|
|
35537
35709
|
percent_change: config.stop_percent / 100,
|
|
@@ -35555,13 +35727,23 @@ function get_app_config_and_max_size(config, payload) {
|
|
|
35555
35727
|
decimal_places: app_config.decimal_places
|
|
35556
35728
|
});
|
|
35557
35729
|
const max_size = initialResult[0]?.avg_size;
|
|
35730
|
+
const last_value = initialResult[0];
|
|
35731
|
+
const entries = initialResult.map((x) => ({
|
|
35732
|
+
entry: x.entry,
|
|
35733
|
+
avg_entry: x.avg_entry,
|
|
35734
|
+
avg_size: x.avg_size,
|
|
35735
|
+
neg_pnl: x.neg_pnl,
|
|
35736
|
+
quantity: x.quantity
|
|
35737
|
+
}));
|
|
35558
35738
|
return {
|
|
35559
35739
|
app_config,
|
|
35560
|
-
max_size
|
|
35740
|
+
max_size,
|
|
35741
|
+
last_value,
|
|
35742
|
+
entries
|
|
35561
35743
|
};
|
|
35562
35744
|
}
|
|
35563
35745
|
function buildAppConfig(config, payload) {
|
|
35564
|
-
const { app_config, max_size } = get_app_config_and_max_size({
|
|
35746
|
+
const { app_config, max_size, last_value, entries } = get_app_config_and_max_size({
|
|
35565
35747
|
...config,
|
|
35566
35748
|
risk: payload.risk,
|
|
35567
35749
|
profit: payload.profit || 500,
|
|
@@ -35580,6 +35762,8 @@ function buildAppConfig(config, payload) {
|
|
|
35580
35762
|
app_config.max_size = max_size;
|
|
35581
35763
|
app_config.entry = payload.entry || app_config.entry;
|
|
35582
35764
|
app_config.stop = payload.stop || app_config.stop;
|
|
35765
|
+
app_config.last_value = last_value;
|
|
35766
|
+
app_config.entries = entries;
|
|
35583
35767
|
return app_config;
|
|
35584
35768
|
}
|
|
35585
35769
|
function getOptimumStopAndRisk(app_config, params) {
|
|
@@ -35753,11 +35937,16 @@ function generate_config_params(app_config, payload) {
|
|
|
35753
35937
|
class ExchangeAccount {
|
|
35754
35938
|
instance;
|
|
35755
35939
|
exchange;
|
|
35940
|
+
main_exchange;
|
|
35756
35941
|
app_db;
|
|
35757
35942
|
constructor(payload, options) {
|
|
35758
35943
|
this.instance = payload;
|
|
35759
35944
|
this.exchange = options.exchange;
|
|
35760
35945
|
this.app_db = options.app_db;
|
|
35946
|
+
this.main_exchange = options.main_exchange;
|
|
35947
|
+
}
|
|
35948
|
+
getDBInstance() {
|
|
35949
|
+
return this.app_db;
|
|
35761
35950
|
}
|
|
35762
35951
|
async getLiveExchangeInstance(payload) {
|
|
35763
35952
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
@@ -36018,6 +36207,54 @@ class ExchangeAccount {
|
|
|
36018
36207
|
trades
|
|
36019
36208
|
};
|
|
36020
36209
|
}
|
|
36210
|
+
async determineAmountToBuy(payload) {
|
|
36211
|
+
const { orders, kind, decimal_places = "%.3f", symbol } = payload;
|
|
36212
|
+
const totalQuantity = orders.reduce((sum, order) => sum + (order.quantity || 0), 0);
|
|
36213
|
+
let runningTotal = to_f(totalQuantity, decimal_places);
|
|
36214
|
+
let sortedOrders = [...orders].sort((a, b) => (a.entry || 0) - (b.entry || 0));
|
|
36215
|
+
if (kind === "short") {
|
|
36216
|
+
sortedOrders.reverse();
|
|
36217
|
+
}
|
|
36218
|
+
const withCumulative = [];
|
|
36219
|
+
for (const order of sortedOrders) {
|
|
36220
|
+
withCumulative.push({
|
|
36221
|
+
...order,
|
|
36222
|
+
cumulative_quantity: runningTotal
|
|
36223
|
+
});
|
|
36224
|
+
runningTotal -= order.quantity;
|
|
36225
|
+
runningTotal = to_f(runningTotal, decimal_places);
|
|
36226
|
+
}
|
|
36227
|
+
const position2 = await this.syncAccount({
|
|
36228
|
+
symbol,
|
|
36229
|
+
kind,
|
|
36230
|
+
live_refresh: true,
|
|
36231
|
+
update: true
|
|
36232
|
+
});
|
|
36233
|
+
let existingOrders = await this.syncOrders({
|
|
36234
|
+
symbol,
|
|
36235
|
+
kind,
|
|
36236
|
+
update: true
|
|
36237
|
+
});
|
|
36238
|
+
let filteredOrders = withCumulative.filter((order) => (order.cumulative_quantity || 0) > position2?.quantity).map((order) => ({
|
|
36239
|
+
...order,
|
|
36240
|
+
price: order.entry,
|
|
36241
|
+
kind,
|
|
36242
|
+
side: kind.toLowerCase() === "long" ? "buy" : "sell"
|
|
36243
|
+
}));
|
|
36244
|
+
filteredOrders = filteredOrders.filter((k) => !existingOrders.map((j) => j.price).includes(k.price));
|
|
36245
|
+
const side = kind.toLowerCase() === "long" ? "buy" : "sell";
|
|
36246
|
+
const shouldCancel = existingOrders.filter((k) => !orders.map((j) => j.entry).includes(k.price) && k.side === side).map((u) => u.price);
|
|
36247
|
+
if (shouldCancel.length > 0) {
|
|
36248
|
+
const pp = kind === "long" ? Math.max(...shouldCancel) : Math.min(...shouldCancel);
|
|
36249
|
+
const cancel_orders = await this.cancelOrders({
|
|
36250
|
+
symbol,
|
|
36251
|
+
kind,
|
|
36252
|
+
price: pp
|
|
36253
|
+
});
|
|
36254
|
+
console.log("cancel_orders", cancel_orders);
|
|
36255
|
+
}
|
|
36256
|
+
return filteredOrders;
|
|
36257
|
+
}
|
|
36021
36258
|
async placeSharedOrder(action, payload) {
|
|
36022
36259
|
const app_config = await this.buildAppConfig({
|
|
36023
36260
|
entry: payload.entry,
|
|
@@ -36038,6 +36275,29 @@ class ExchangeAccount {
|
|
|
36038
36275
|
min_size: app_config.min_size,
|
|
36039
36276
|
symbol: payload.symbol
|
|
36040
36277
|
}, false);
|
|
36278
|
+
if (payload.raw) {
|
|
36279
|
+
let actual_orders_to_buy = await this.determineAmountToBuy({
|
|
36280
|
+
orders: trades,
|
|
36281
|
+
kind: app_config.kind,
|
|
36282
|
+
decimal_places: app_config.decimal_places,
|
|
36283
|
+
price_places: app_config.price_places,
|
|
36284
|
+
symbol: payload.symbol,
|
|
36285
|
+
place: payload.place
|
|
36286
|
+
});
|
|
36287
|
+
if (action === "place_limit_orders" && payload.place) {
|
|
36288
|
+
return await this.exchange.createLimitPurchaseOrders({
|
|
36289
|
+
orders: actual_orders_to_buy.map((x) => ({
|
|
36290
|
+
entry: x.entry,
|
|
36291
|
+
quantity: x.quantity
|
|
36292
|
+
})),
|
|
36293
|
+
kind: app_config.kind,
|
|
36294
|
+
decimal_places: app_config.decimal_places,
|
|
36295
|
+
price_places: app_config.price_places,
|
|
36296
|
+
symbol: payload.symbol
|
|
36297
|
+
});
|
|
36298
|
+
}
|
|
36299
|
+
return actual_orders_to_buy;
|
|
36300
|
+
}
|
|
36041
36301
|
if (action === "place_limit_orders" && payload.place) {
|
|
36042
36302
|
let result = await this.exchange.bulkPlaceLimitOrders({
|
|
36043
36303
|
orders: trades.map((x) => ({
|
|
@@ -36084,7 +36344,17 @@ class ExchangeAccount {
|
|
|
36084
36344
|
kind: payload.kind
|
|
36085
36345
|
});
|
|
36086
36346
|
if (db_position) {
|
|
36087
|
-
|
|
36347
|
+
const config = db_position.expand?.config;
|
|
36348
|
+
const params = {
|
|
36349
|
+
entry: payload.params.entry !== undefined ? payload.params.entry : config.entry,
|
|
36350
|
+
stop: payload.params.stop !== undefined ? payload.params.stop : config.stop,
|
|
36351
|
+
risk_reward: payload.params.risk_reward !== undefined ? payload.params.risk_reward : config.risk_reward,
|
|
36352
|
+
risk: payload.params.risk !== undefined ? payload.params.risk : config.risk,
|
|
36353
|
+
profit_percent: payload.params.profit_percent !== undefined ? payload.params.profit_percent : config.profit_percent,
|
|
36354
|
+
place_tp: payload.params.place_tp !== undefined ? payload.params.place_tp : true,
|
|
36355
|
+
profit: payload.params.profit !== undefined ? payload.params.profit : config.profit
|
|
36356
|
+
};
|
|
36357
|
+
return await this.app_db.createOrUpdatePositionConfig(db_position, params);
|
|
36088
36358
|
}
|
|
36089
36359
|
}
|
|
36090
36360
|
return await this.app_db.getPositionConfig({
|
|
@@ -36271,15 +36541,15 @@ class ExchangeAccount {
|
|
|
36271
36541
|
}
|
|
36272
36542
|
}
|
|
36273
36543
|
async generate_config_params(payload) {
|
|
36274
|
-
const {
|
|
36275
|
-
const app_config = await this.buildAppConfig({
|
|
36544
|
+
const {
|
|
36276
36545
|
entry,
|
|
36277
36546
|
stop,
|
|
36278
36547
|
risk_reward,
|
|
36279
36548
|
risk,
|
|
36280
|
-
symbol
|
|
36281
|
-
|
|
36282
|
-
|
|
36549
|
+
symbol,
|
|
36550
|
+
with_trades = false
|
|
36551
|
+
} = payload;
|
|
36552
|
+
const app_config = await this.buildAppConfig({
|
|
36283
36553
|
entry,
|
|
36284
36554
|
stop,
|
|
36285
36555
|
risk_reward,
|
|
@@ -36293,8 +36563,79 @@ class ExchangeAccount {
|
|
|
36293
36563
|
risk,
|
|
36294
36564
|
symbol
|
|
36295
36565
|
});
|
|
36566
|
+
if (with_trades) {
|
|
36567
|
+
const app_config2 = await this.buildAppConfig({
|
|
36568
|
+
entry: config.entry,
|
|
36569
|
+
stop: config.stop,
|
|
36570
|
+
risk_reward: config.risk_reward,
|
|
36571
|
+
risk: config.risk,
|
|
36572
|
+
symbol,
|
|
36573
|
+
profit: 0,
|
|
36574
|
+
update_db: false
|
|
36575
|
+
});
|
|
36576
|
+
const { trades } = await this.placeConfigOrders(app_config2, {
|
|
36577
|
+
risk_reward: config.risk_reward,
|
|
36578
|
+
entry: config.entry,
|
|
36579
|
+
stop: config.stop,
|
|
36580
|
+
risk_per_trade: config.risk,
|
|
36581
|
+
avg_size: 0,
|
|
36582
|
+
neg_pnl: 0,
|
|
36583
|
+
min_size: app_config2.min_size,
|
|
36584
|
+
symbol
|
|
36585
|
+
}, false);
|
|
36586
|
+
config.trades = trades;
|
|
36587
|
+
}
|
|
36296
36588
|
return { ...config, place_stop: false, profit_percent: 0 };
|
|
36297
36589
|
}
|
|
36590
|
+
async build_short_order(payload) {
|
|
36591
|
+
const { symbol, kind } = payload;
|
|
36592
|
+
await this.syncOrders({
|
|
36593
|
+
symbol,
|
|
36594
|
+
kind,
|
|
36595
|
+
update: true
|
|
36596
|
+
});
|
|
36597
|
+
const position2 = await this.syncAccount({
|
|
36598
|
+
symbol,
|
|
36599
|
+
kind,
|
|
36600
|
+
as_view: true
|
|
36601
|
+
});
|
|
36602
|
+
const position_config = await this.getPositionConfig({
|
|
36603
|
+
symbol,
|
|
36604
|
+
kind
|
|
36605
|
+
});
|
|
36606
|
+
if (position2 && position_config && position2.entry > 0) {
|
|
36607
|
+
let next_order = position2.next_order;
|
|
36608
|
+
let take_profit = position2.take_profit;
|
|
36609
|
+
if (next_order && take_profit) {
|
|
36610
|
+
let config = await this.buildConfigForSymbol({
|
|
36611
|
+
symbol,
|
|
36612
|
+
risk: position_config.risk,
|
|
36613
|
+
risk_reward: position_config.risk_reward,
|
|
36614
|
+
kind,
|
|
36615
|
+
as_config: true
|
|
36616
|
+
});
|
|
36617
|
+
const focus = config.entries.filter((x) => {
|
|
36618
|
+
if (kind == "long") {
|
|
36619
|
+
return x.entry <= next_order;
|
|
36620
|
+
} else {
|
|
36621
|
+
return x.entry >= next_order;
|
|
36622
|
+
}
|
|
36623
|
+
});
|
|
36624
|
+
const focus_entry = focus.at(-1);
|
|
36625
|
+
if (focus_entry) {
|
|
36626
|
+
let entry = focus_entry.entry;
|
|
36627
|
+
let risk = Math.abs(focus_entry.neg_pnl);
|
|
36628
|
+
return await this.generate_config_params({
|
|
36629
|
+
entry,
|
|
36630
|
+
stop: take_profit,
|
|
36631
|
+
risk_reward: position_config.risk_reward,
|
|
36632
|
+
risk,
|
|
36633
|
+
symbol
|
|
36634
|
+
});
|
|
36635
|
+
}
|
|
36636
|
+
}
|
|
36637
|
+
}
|
|
36638
|
+
}
|
|
36298
36639
|
async extrapolateShortConfig(payload) {
|
|
36299
36640
|
const { symbol, risk_reward = 199, kind, risk } = payload;
|
|
36300
36641
|
let reverse_kind = kind === "long" ? "short" : "long";
|
|
@@ -36329,13 +36670,15 @@ class ExchangeAccount {
|
|
|
36329
36670
|
});
|
|
36330
36671
|
if (position2?.config) {
|
|
36331
36672
|
const config = position2.expand.config;
|
|
36673
|
+
let entry = payload.tp ? position2.entry || config.entry : config.entry;
|
|
36332
36674
|
return await this.placeSharedOrder("place_limit_orders", {
|
|
36333
36675
|
symbol,
|
|
36334
|
-
entry
|
|
36676
|
+
entry,
|
|
36335
36677
|
stop: config.stop,
|
|
36336
36678
|
risk_reward: config.risk_reward,
|
|
36337
36679
|
risk: config.risk,
|
|
36338
|
-
place
|
|
36680
|
+
place,
|
|
36681
|
+
raw: payload.raw
|
|
36339
36682
|
});
|
|
36340
36683
|
}
|
|
36341
36684
|
}
|
|
@@ -36561,7 +36904,8 @@ class ExchangeAccount {
|
|
|
36561
36904
|
}
|
|
36562
36905
|
const config = await this.exchange.generateConfig({
|
|
36563
36906
|
symbol,
|
|
36564
|
-
limit: _config?.candle_count || 5
|
|
36907
|
+
limit: _config?.candle_count || 5,
|
|
36908
|
+
interval: _config?.interval || "1w"
|
|
36565
36909
|
});
|
|
36566
36910
|
await this.app_db.updateSymbolConfigs({
|
|
36567
36911
|
configs: [
|
|
@@ -36578,27 +36922,55 @@ class ExchangeAccount {
|
|
|
36578
36922
|
});
|
|
36579
36923
|
return this.app_db.getSymbolConfigFromDB(symbol);
|
|
36580
36924
|
}
|
|
36925
|
+
async buildConfigForSymbol(payload) {
|
|
36926
|
+
const {
|
|
36927
|
+
symbol,
|
|
36928
|
+
risk,
|
|
36929
|
+
risk_reward = 199,
|
|
36930
|
+
as_config = false,
|
|
36931
|
+
kind = "long"
|
|
36932
|
+
} = payload;
|
|
36933
|
+
const symbol_config = await this.recomputeSymbolConfig({
|
|
36934
|
+
symbol
|
|
36935
|
+
});
|
|
36936
|
+
const long_config = await this.generate_config_params({
|
|
36937
|
+
entry: kind === "long" ? symbol_config.resistance : symbol_config.support,
|
|
36938
|
+
stop: kind === "long" ? symbol_config.support : symbol_config.resistance,
|
|
36939
|
+
risk_reward,
|
|
36940
|
+
risk,
|
|
36941
|
+
symbol
|
|
36942
|
+
});
|
|
36943
|
+
if (as_config) {
|
|
36944
|
+
const app_config = buildAppConfig(symbol_config, {
|
|
36945
|
+
entry: long_config.entry,
|
|
36946
|
+
stop: long_config.stop,
|
|
36947
|
+
risk_reward,
|
|
36948
|
+
risk: long_config.risk,
|
|
36949
|
+
symbol
|
|
36950
|
+
});
|
|
36951
|
+
return app_config;
|
|
36952
|
+
}
|
|
36953
|
+
return long_config;
|
|
36954
|
+
}
|
|
36581
36955
|
async triggerBullishMarket(payload) {
|
|
36582
|
-
|
|
36956
|
+
let { symbol, profit_percent = 10, risk_reward = 199 } = payload;
|
|
36583
36957
|
const bullish_instance = await this.app_db.getBullishMarket(symbol);
|
|
36584
36958
|
if (!bullish_instance) {
|
|
36585
36959
|
return false;
|
|
36586
36960
|
}
|
|
36587
|
-
const
|
|
36588
|
-
|
|
36589
|
-
|
|
36590
|
-
}
|
|
36961
|
+
const db_instance = await this.app_db.get_exchange_db_instance(this.instance);
|
|
36962
|
+
if (db_instance.profit_percent) {
|
|
36963
|
+
profit_percent = db_instance.profit_percent;
|
|
36964
|
+
}
|
|
36591
36965
|
const position2 = await this.syncAccount({
|
|
36592
36966
|
symbol,
|
|
36593
36967
|
update: true,
|
|
36594
36968
|
kind: "long"
|
|
36595
36969
|
});
|
|
36596
|
-
const long_config = await this.
|
|
36597
|
-
|
|
36598
|
-
stop: symbol_config.support,
|
|
36599
|
-
risk_reward,
|
|
36970
|
+
const long_config = await this.buildConfigForSymbol({
|
|
36971
|
+
symbol,
|
|
36600
36972
|
risk: bullish_instance.risk,
|
|
36601
|
-
|
|
36973
|
+
risk_reward
|
|
36602
36974
|
});
|
|
36603
36975
|
let changed = false;
|
|
36604
36976
|
if (!position2?.config) {
|
|
@@ -36629,30 +37001,56 @@ class ExchangeAccount {
|
|
|
36629
37001
|
}
|
|
36630
37002
|
const short_position = await this.syncAccount({
|
|
36631
37003
|
symbol,
|
|
36632
|
-
kind: "short"
|
|
37004
|
+
kind: "short",
|
|
37005
|
+
update: true
|
|
36633
37006
|
});
|
|
36634
|
-
|
|
37007
|
+
const orders = await this.getOrders({
|
|
37008
|
+
symbol,
|
|
37009
|
+
kind: "short",
|
|
37010
|
+
type: "limit",
|
|
37011
|
+
refresh: true
|
|
37012
|
+
});
|
|
37013
|
+
if (orders.length > 0) {
|
|
36635
37014
|
await this.toggleStopBuying({
|
|
36636
37015
|
should_stop: true,
|
|
36637
37016
|
kind: "short",
|
|
36638
37017
|
symbol
|
|
36639
37018
|
});
|
|
37019
|
+
await this.cancelOrders({
|
|
37020
|
+
symbol,
|
|
37021
|
+
kind: "short",
|
|
37022
|
+
all: true
|
|
37023
|
+
});
|
|
37024
|
+
}
|
|
37025
|
+
if (short_position?.config) {
|
|
37026
|
+
await this.app_db.update_db_position(short_position, {
|
|
37027
|
+
config: null,
|
|
37028
|
+
reduce_ratio: 0.9
|
|
37029
|
+
});
|
|
36640
37030
|
}
|
|
36641
37031
|
if (changed) {
|
|
36642
37032
|
const rr = await this.triggerTradeFromConfig({
|
|
36643
37033
|
symbol,
|
|
36644
|
-
kind: "long"
|
|
37034
|
+
kind: "long",
|
|
37035
|
+
tp: true
|
|
37036
|
+
});
|
|
37037
|
+
await this.placeProfitAndStop({
|
|
37038
|
+
symbol,
|
|
37039
|
+
trigger: true
|
|
36645
37040
|
});
|
|
36646
37041
|
return rr;
|
|
36647
37042
|
}
|
|
36648
37043
|
return false;
|
|
36649
37044
|
}
|
|
36650
|
-
async updateAllActiveSymbols() {
|
|
37045
|
+
async updateAllActiveSymbols(payload) {
|
|
37046
|
+
const { interval = 5 } = payload;
|
|
36651
37047
|
const symbols = await this.app_db.getAllSymbolsFromPositions({
|
|
36652
37048
|
no_position: true,
|
|
36653
37049
|
kind: "long"
|
|
36654
37050
|
});
|
|
36655
|
-
|
|
37051
|
+
const all_open_symbols = await this.exchange.getAllOpenSymbols();
|
|
37052
|
+
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
|
37053
|
+
for (const symbol of Array.from(new Set(symbols.concat(all_open_symbols)))) {
|
|
36656
37054
|
await this.getLiveExchangeInstance({ symbol, refresh: true });
|
|
36657
37055
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
36658
37056
|
}
|
|
@@ -36672,10 +37070,27 @@ class ExchangeAccount {
|
|
|
36672
37070
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
36673
37071
|
}
|
|
36674
37072
|
}
|
|
36675
|
-
async
|
|
36676
|
-
const
|
|
36677
|
-
custom_filter: `
|
|
37073
|
+
async getSymbolsForPositions() {
|
|
37074
|
+
const positions = await this.app_db.getPositions({
|
|
37075
|
+
custom_filter: `account.owner:lower = "${this.instance.owner.toLowerCase()}" && account.exchange:lower = "${this.instance.exchange.toLowerCase()}" && quantity > 0`,
|
|
37076
|
+
symbol: "",
|
|
37077
|
+
account: {
|
|
37078
|
+
owner: "",
|
|
37079
|
+
exchange: ""
|
|
37080
|
+
}
|
|
36678
37081
|
});
|
|
37082
|
+
return Array.from(new Set(positions.map((p) => p.symbol)));
|
|
37083
|
+
}
|
|
37084
|
+
async getNonEssentialSymbols() {
|
|
37085
|
+
const [all_open_symbols, essential_symbols] = await Promise.all([
|
|
37086
|
+
this.exchange.getAllOpenSymbols(),
|
|
37087
|
+
this.app_db.getAllSymbolConfigs({
|
|
37088
|
+
custom_filter: `essential = true`
|
|
37089
|
+
})
|
|
37090
|
+
]);
|
|
37091
|
+
const essential_symbols_set = new Set(essential_symbols.map((s2) => s2.symbol));
|
|
37092
|
+
const open_symbols_set = new Set(all_open_symbols);
|
|
37093
|
+
const non_essential_symbols = Array.from(essential_symbols_set).filter((s2) => !open_symbols_set.has(s2));
|
|
36679
37094
|
const bullish_markets = await this.app_db.getBullishMarkets();
|
|
36680
37095
|
const bullish_symbols = Array.from(new Set(bullish_markets.updated_bullish.map((m) => m.symbol)));
|
|
36681
37096
|
const symbols = Array.from(new Set(essential_symbols.map((s2) => s2.symbol))).concat(bullish_symbols);
|
|
@@ -36688,19 +37103,24 @@ class ExchangeAccount {
|
|
|
36688
37103
|
exchange: ""
|
|
36689
37104
|
}
|
|
36690
37105
|
});
|
|
36691
|
-
return Array.from(new Set(positions.map((p) => p.symbol)));
|
|
37106
|
+
return Array.from(new Set(positions.map((p) => p.symbol).concat(non_essential_symbols)));
|
|
36692
37107
|
}
|
|
36693
|
-
async
|
|
37108
|
+
async _terminatePositions(payload) {
|
|
36694
37109
|
const { symbol } = payload;
|
|
36695
|
-
const symbol_config = await this.app_db.getSymbolConfigFromDB(symbol);
|
|
36696
37110
|
let db_positions = await this.syncAccount({
|
|
36697
37111
|
symbol,
|
|
36698
37112
|
update: true,
|
|
36699
37113
|
live_refresh: true
|
|
36700
37114
|
});
|
|
37115
|
+
const symbol_config = await this.app_db.getSymbolConfigFromDB(symbol);
|
|
36701
37116
|
let long_position = db_positions.find((x) => x.kind === "long");
|
|
36702
37117
|
let short_position = db_positions.find((x) => x.kind === "short");
|
|
36703
37118
|
if (long_position && long_position.quantity > 0 && symbol_config) {
|
|
37119
|
+
await this.toggleStopBuying({
|
|
37120
|
+
symbol,
|
|
37121
|
+
kind: "long",
|
|
37122
|
+
should_stop: true
|
|
37123
|
+
});
|
|
36704
37124
|
await this.exchange.closePosition({
|
|
36705
37125
|
symbol,
|
|
36706
37126
|
kind: "long",
|
|
@@ -36714,6 +37134,11 @@ class ExchangeAccount {
|
|
|
36714
37134
|
});
|
|
36715
37135
|
}
|
|
36716
37136
|
if (short_position && short_position.quantity > 0 && symbol_config) {
|
|
37137
|
+
await this.toggleStopBuying({
|
|
37138
|
+
symbol,
|
|
37139
|
+
kind: "short",
|
|
37140
|
+
should_stop: true
|
|
37141
|
+
});
|
|
36717
37142
|
await this.exchange.closePosition({
|
|
36718
37143
|
symbol,
|
|
36719
37144
|
kind: "short",
|
|
@@ -36741,6 +37166,158 @@ class ExchangeAccount {
|
|
|
36741
37166
|
await this.app_db.removePosition(short_position);
|
|
36742
37167
|
}
|
|
36743
37168
|
}
|
|
37169
|
+
async getOrders(payload) {
|
|
37170
|
+
const { symbol, kind, type, refresh = false } = payload;
|
|
37171
|
+
if (refresh) {
|
|
37172
|
+
await this.syncOrders({
|
|
37173
|
+
symbol,
|
|
37174
|
+
kind
|
|
37175
|
+
});
|
|
37176
|
+
}
|
|
37177
|
+
let side;
|
|
37178
|
+
if (kind == "long") {
|
|
37179
|
+
if (type === "limit") {
|
|
37180
|
+
side = "buy";
|
|
37181
|
+
} else {
|
|
37182
|
+
side = "sell";
|
|
37183
|
+
}
|
|
37184
|
+
} else {
|
|
37185
|
+
if (type === "limit") {
|
|
37186
|
+
side = "sell";
|
|
37187
|
+
} else {
|
|
37188
|
+
side = "buy";
|
|
37189
|
+
}
|
|
37190
|
+
}
|
|
37191
|
+
const orders = await this.app_db.getOrders(this.instance, {
|
|
37192
|
+
symbol,
|
|
37193
|
+
kind
|
|
37194
|
+
});
|
|
37195
|
+
return orders.filter((x) => {
|
|
37196
|
+
if (type === "stop") {
|
|
37197
|
+
return x.side === side && x.stop > 0;
|
|
37198
|
+
}
|
|
37199
|
+
if (type === "tp") {
|
|
37200
|
+
return x.side === side && x.stop === 0;
|
|
37201
|
+
}
|
|
37202
|
+
return x.side === side;
|
|
37203
|
+
});
|
|
37204
|
+
}
|
|
37205
|
+
async syncPositionConfigs(payload) {
|
|
37206
|
+
const { symbol, kind, refresh = false } = payload;
|
|
37207
|
+
const symbol_config = await this.recomputeSymbolConfig({
|
|
37208
|
+
symbol,
|
|
37209
|
+
refresh
|
|
37210
|
+
});
|
|
37211
|
+
const long_config = await this.getPositionConfig({
|
|
37212
|
+
symbol,
|
|
37213
|
+
kind: "long"
|
|
37214
|
+
});
|
|
37215
|
+
const short_config = await this.getPositionConfig({
|
|
37216
|
+
symbol,
|
|
37217
|
+
kind: "short"
|
|
37218
|
+
});
|
|
37219
|
+
if (long_config && kind === "long") {
|
|
37220
|
+
await this.app_db.updateScheduledTrade(long_config.id, {
|
|
37221
|
+
entry: symbol_config.resistance,
|
|
37222
|
+
stop: symbol_config.support
|
|
37223
|
+
});
|
|
37224
|
+
}
|
|
37225
|
+
if (short_config && kind === "short") {
|
|
37226
|
+
await this.app_db.updateScheduledTrade(short_config.id, {
|
|
37227
|
+
entry: symbol_config.support,
|
|
37228
|
+
stop: symbol_config.resistance
|
|
37229
|
+
});
|
|
37230
|
+
}
|
|
37231
|
+
}
|
|
37232
|
+
async terminatePositions(payload) {
|
|
37233
|
+
const { symbol } = payload;
|
|
37234
|
+
let db_positions = await this.syncAccount({
|
|
37235
|
+
symbol,
|
|
37236
|
+
update: true,
|
|
37237
|
+
live_refresh: true
|
|
37238
|
+
});
|
|
37239
|
+
const symbol_config = await this.recomputeSymbolConfig({
|
|
37240
|
+
symbol
|
|
37241
|
+
});
|
|
37242
|
+
let long_position = db_positions.find((x) => x.kind === "long");
|
|
37243
|
+
let short_position = db_positions.find((x) => x.kind === "short");
|
|
37244
|
+
if (long_position && long_position.quantity > 0 && symbol_config) {
|
|
37245
|
+
await this.toggleStopBuying({
|
|
37246
|
+
symbol,
|
|
37247
|
+
kind: "long",
|
|
37248
|
+
should_stop: true
|
|
37249
|
+
});
|
|
37250
|
+
await this.cancelOrders({
|
|
37251
|
+
symbol,
|
|
37252
|
+
kind: "long",
|
|
37253
|
+
all: true
|
|
37254
|
+
});
|
|
37255
|
+
await this.app_db.update_db_position(long_position, {
|
|
37256
|
+
config: null,
|
|
37257
|
+
reduce_ratio: 0.9
|
|
37258
|
+
});
|
|
37259
|
+
}
|
|
37260
|
+
const long_config = await this.getPositionConfig({
|
|
37261
|
+
symbol,
|
|
37262
|
+
kind: "long"
|
|
37263
|
+
});
|
|
37264
|
+
let the_same_config = false;
|
|
37265
|
+
if (long_config && long_position && long_position.quantity > 0) {
|
|
37266
|
+
const existing_short_config = await this.getPositionConfig({
|
|
37267
|
+
symbol,
|
|
37268
|
+
kind: "short"
|
|
37269
|
+
});
|
|
37270
|
+
const diff = (long_position.entry - long_config.stop) * long_position.quantity;
|
|
37271
|
+
if (existing_short_config) {
|
|
37272
|
+
the_same_config = long_config.entry === existing_short_config.stop && long_config.stop === existing_short_config.entry;
|
|
37273
|
+
}
|
|
37274
|
+
const short_config = await this.getPositionConfig({
|
|
37275
|
+
symbol,
|
|
37276
|
+
kind: "short",
|
|
37277
|
+
params: {
|
|
37278
|
+
entry: long_config.stop,
|
|
37279
|
+
stop: long_config.entry,
|
|
37280
|
+
risk_reward: long_config.risk_reward,
|
|
37281
|
+
profit_percent: 0,
|
|
37282
|
+
risk: long_config.risk,
|
|
37283
|
+
profit: Math.abs(diff),
|
|
37284
|
+
place_tp: false
|
|
37285
|
+
}
|
|
37286
|
+
});
|
|
37287
|
+
if (short_config) {
|
|
37288
|
+
await this.app_db.update_db_position(short_position, {
|
|
37289
|
+
config: short_config.id,
|
|
37290
|
+
reduce_ratio: 0.9
|
|
37291
|
+
});
|
|
37292
|
+
if (!the_same_config) {
|
|
37293
|
+
await this.placeTrade({
|
|
37294
|
+
symbol,
|
|
37295
|
+
kind: "short",
|
|
37296
|
+
place: true
|
|
37297
|
+
});
|
|
37298
|
+
}
|
|
37299
|
+
}
|
|
37300
|
+
await this.placeProfitAndStop({
|
|
37301
|
+
symbol,
|
|
37302
|
+
kind: "short"
|
|
37303
|
+
});
|
|
37304
|
+
}
|
|
37305
|
+
if (long_position && short_position && long_position.quantity === 0 && short_position.quantity === 0) {
|
|
37306
|
+
await this.cancelOrders({
|
|
37307
|
+
symbol,
|
|
37308
|
+
kind: "long",
|
|
37309
|
+
all: true
|
|
37310
|
+
});
|
|
37311
|
+
await this.cancelOrders({
|
|
37312
|
+
symbol,
|
|
37313
|
+
kind: "short",
|
|
37314
|
+
all: true
|
|
37315
|
+
});
|
|
37316
|
+
await this.app_db.removePosition(long_position);
|
|
37317
|
+
await this.app_db.removePosition(short_position);
|
|
37318
|
+
await this.app_db.unwindSymbolFromDB(symbol);
|
|
37319
|
+
}
|
|
37320
|
+
}
|
|
36744
37321
|
async fetchAndUpdateTopMovers() {
|
|
36745
37322
|
const db_instance = await this.app_db.get_exchange_db_instance(this.instance);
|
|
36746
37323
|
const {
|
|
@@ -36748,8 +37325,10 @@ class ExchangeAccount {
|
|
|
36748
37325
|
bearish: _bearish,
|
|
36749
37326
|
movePercent,
|
|
36750
37327
|
totalRisk,
|
|
36751
|
-
max_non_essential = 0
|
|
37328
|
+
max_non_essential = 0,
|
|
37329
|
+
exclude_coins
|
|
36752
37330
|
} = db_instance;
|
|
37331
|
+
let dont_trade = exclude_coins?.bullish || [];
|
|
36753
37332
|
let bullishMarkets = [];
|
|
36754
37333
|
if (bullish) {
|
|
36755
37334
|
const { movers } = await this.exchange.checkDelistedMovers({
|
|
@@ -36758,7 +37337,7 @@ class ExchangeAccount {
|
|
|
36758
37337
|
bullishMarkets = movers;
|
|
36759
37338
|
}
|
|
36760
37339
|
const non_essential_symbols = await this.getNonEssentialSymbols();
|
|
36761
|
-
let symbols_to_remove = non_essential_symbols.filter((k) => !bullishMarkets.map((m) => m.symbol).includes(k)).slice(0, max_non_essential);
|
|
37340
|
+
let symbols_to_remove = non_essential_symbols.filter((k) => !bullishMarkets.map((m) => m.symbol).includes(k)).slice(0, max_non_essential).concat(dont_trade);
|
|
36762
37341
|
bullishMarkets = bullishMarkets.filter((m) => !symbols_to_remove.includes(m.symbol));
|
|
36763
37342
|
if (symbols_to_remove.length > 0) {
|
|
36764
37343
|
for (const symbol of symbols_to_remove) {
|
|
@@ -36773,6 +37352,12 @@ class ExchangeAccount {
|
|
|
36773
37352
|
totalRisk,
|
|
36774
37353
|
max_count: max_non_essential
|
|
36775
37354
|
});
|
|
37355
|
+
if (result.updated_bullish.length > max_non_essential) {
|
|
37356
|
+
return {
|
|
37357
|
+
updated_bullish: [],
|
|
37358
|
+
moved_to_winding: []
|
|
37359
|
+
};
|
|
37360
|
+
}
|
|
36776
37361
|
return result;
|
|
36777
37362
|
}
|
|
36778
37363
|
async computeTargetPnl(payload) {
|
|
@@ -36807,20 +37392,57 @@ class ExchangeAccount {
|
|
|
36807
37392
|
}
|
|
36808
37393
|
return 0;
|
|
36809
37394
|
}
|
|
37395
|
+
async placeTrade(payload) {
|
|
37396
|
+
const { symbol, kind, place, tp } = payload;
|
|
37397
|
+
if (place) {
|
|
37398
|
+
return await this.triggerTradeFromConfig({
|
|
37399
|
+
symbol,
|
|
37400
|
+
kind
|
|
37401
|
+
});
|
|
37402
|
+
}
|
|
37403
|
+
await this.syncAccount({
|
|
37404
|
+
symbol,
|
|
37405
|
+
live_refresh: true,
|
|
37406
|
+
update: true
|
|
37407
|
+
});
|
|
37408
|
+
const result = await this.syncOrders({
|
|
37409
|
+
symbol,
|
|
37410
|
+
kind,
|
|
37411
|
+
update: true
|
|
37412
|
+
});
|
|
37413
|
+
await this.updateTargetPnl({
|
|
37414
|
+
symbol,
|
|
37415
|
+
kind
|
|
37416
|
+
});
|
|
37417
|
+
if (tp) {
|
|
37418
|
+
await this.placeProfitAndStop({
|
|
37419
|
+
symbol,
|
|
37420
|
+
trigger: true
|
|
37421
|
+
});
|
|
37422
|
+
}
|
|
37423
|
+
return result;
|
|
37424
|
+
}
|
|
36810
37425
|
}
|
|
36811
37426
|
function getExchangeKlass(exchange) {
|
|
36812
37427
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
36813
37428
|
const clientFunc = exchange === "binance" ? initClient : initClient2;
|
|
36814
37429
|
return async (payload) => {
|
|
36815
|
-
const credentials =
|
|
37430
|
+
const credentials = payload.getCredentials(payload.account, exchange);
|
|
36816
37431
|
const client = await clientFunc(credentials, {
|
|
36817
37432
|
type: "future",
|
|
36818
37433
|
proxyAgent: payload.proxyAgent
|
|
36819
37434
|
});
|
|
37435
|
+
let main_client = null;
|
|
37436
|
+
if (exchange === "binance") {
|
|
37437
|
+
main_client = await initClient(credentials, {
|
|
37438
|
+
type: "spot",
|
|
37439
|
+
proxyAgent: payload.proxyAgent
|
|
37440
|
+
});
|
|
37441
|
+
}
|
|
36820
37442
|
if (!client) {
|
|
36821
37443
|
throw new Error(`Failed to initialize ${exchange} client`);
|
|
36822
37444
|
}
|
|
36823
|
-
return new func(client);
|
|
37445
|
+
return new func(client, main_client);
|
|
36824
37446
|
};
|
|
36825
37447
|
}
|
|
36826
37448
|
async function getExchangeAccount(payload) {
|
|
@@ -36846,11 +37468,10 @@ class App {
|
|
|
36846
37468
|
this.getCredentials = getCredentials;
|
|
36847
37469
|
}
|
|
36848
37470
|
async getExchangeAccount(account) {
|
|
36849
|
-
const credentials = this.getCredentials(account.owner, account.exchange);
|
|
36850
37471
|
return await getExchangeAccount({
|
|
36851
37472
|
account,
|
|
36852
37473
|
app_db: this.app_db,
|
|
36853
|
-
getCredentials:
|
|
37474
|
+
getCredentials: this.getCredentials
|
|
36854
37475
|
});
|
|
36855
37476
|
}
|
|
36856
37477
|
async syncAccount(payload) {
|
|
@@ -37044,7 +37665,7 @@ class App {
|
|
|
37044
37665
|
}
|
|
37045
37666
|
}
|
|
37046
37667
|
async windDownSymbol(payload) {
|
|
37047
|
-
const { symbol, risk } = payload;
|
|
37668
|
+
const { symbol, risk: _risk } = payload;
|
|
37048
37669
|
let winding_instance = await this.app_db.getWindingDownMarkets(symbol);
|
|
37049
37670
|
if (winding_instance && winding_instance.length > 0) {
|
|
37050
37671
|
winding_instance = winding_instance[0];
|
|
@@ -37069,6 +37690,17 @@ class App {
|
|
|
37069
37690
|
if (position_pairs.length > 0) {
|
|
37070
37691
|
console.log("removing position pairs");
|
|
37071
37692
|
for (const pair of position_pairs) {
|
|
37693
|
+
const exchange_account = await this.getExchangeAccount(pair.expand.account);
|
|
37694
|
+
await exchange_account.cancelOrders({
|
|
37695
|
+
symbol: pair.symbol,
|
|
37696
|
+
kind: "long",
|
|
37697
|
+
all: true
|
|
37698
|
+
});
|
|
37699
|
+
await exchange_account.cancelOrders({
|
|
37700
|
+
symbol: pair.symbol,
|
|
37701
|
+
kind: "short",
|
|
37702
|
+
all: true
|
|
37703
|
+
});
|
|
37072
37704
|
await this.app_db.removePosition(pair);
|
|
37073
37705
|
}
|
|
37074
37706
|
}
|
|
@@ -37076,10 +37708,8 @@ class App {
|
|
|
37076
37708
|
console.log("winding down not paired positions");
|
|
37077
37709
|
for (const pair of not_paired_positions) {
|
|
37078
37710
|
const exchange_account = await this.getExchangeAccount(pair.expand.account);
|
|
37079
|
-
const result = await exchange_account.
|
|
37080
|
-
symbol: pair.symbol
|
|
37081
|
-
risk_reward: winding_instance?.risk_reward || 199,
|
|
37082
|
-
risk
|
|
37711
|
+
const result = await exchange_account.terminatePositions({
|
|
37712
|
+
symbol: pair.symbol
|
|
37083
37713
|
});
|
|
37084
37714
|
console.log("result", result);
|
|
37085
37715
|
}
|
|
@@ -37139,14 +37769,86 @@ class App {
|
|
|
37139
37769
|
});
|
|
37140
37770
|
}
|
|
37141
37771
|
}
|
|
37772
|
+
async getMoverExchangeInstances() {
|
|
37773
|
+
return await this.app_db.getMoverExchangeInstances();
|
|
37774
|
+
}
|
|
37775
|
+
async updateTpOnAllMarkets() {
|
|
37776
|
+
const move_instances = await this.getMoverExchangeInstances();
|
|
37777
|
+
for (const instance of move_instances) {
|
|
37778
|
+
const params = {
|
|
37779
|
+
account: {
|
|
37780
|
+
owner: instance.owner,
|
|
37781
|
+
exchange: instance.exchange
|
|
37782
|
+
}
|
|
37783
|
+
};
|
|
37784
|
+
const exchange_account = await this.getExchangeAccount(params.account);
|
|
37785
|
+
const symbols = await exchange_account.getSymbolsForPositions();
|
|
37786
|
+
for (const symbol of symbols) {
|
|
37787
|
+
await exchange_account.placeTrade({
|
|
37788
|
+
symbol,
|
|
37789
|
+
kind: "long",
|
|
37790
|
+
tp: true
|
|
37791
|
+
});
|
|
37792
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
37793
|
+
}
|
|
37794
|
+
}
|
|
37795
|
+
}
|
|
37796
|
+
async triggerMoverTask(payload) {
|
|
37797
|
+
const { callback, removeCallback } = payload;
|
|
37798
|
+
const move_instances = await this.getMoverExchangeInstances();
|
|
37799
|
+
for (const instance of move_instances) {
|
|
37800
|
+
const params = {
|
|
37801
|
+
account: {
|
|
37802
|
+
owner: instance.owner,
|
|
37803
|
+
exchange: instance.exchange
|
|
37804
|
+
}
|
|
37805
|
+
};
|
|
37806
|
+
const exchange_account = await this.getExchangeAccount(params.account);
|
|
37807
|
+
const result = await exchange_account.fetchAndUpdateTopMovers();
|
|
37808
|
+
const { updated_bullish } = result;
|
|
37809
|
+
for (const m of updated_bullish) {
|
|
37810
|
+
await callback({
|
|
37811
|
+
symbol: m.symbol,
|
|
37812
|
+
account: params.account
|
|
37813
|
+
});
|
|
37814
|
+
}
|
|
37815
|
+
const winding_down_symbols = await this.app_db.getWindingDownMarkets();
|
|
37816
|
+
for (const w of winding_down_symbols) {
|
|
37817
|
+
await removeCallback({
|
|
37818
|
+
symbol: w.symbol,
|
|
37819
|
+
account: params.account
|
|
37820
|
+
});
|
|
37821
|
+
}
|
|
37822
|
+
}
|
|
37823
|
+
}
|
|
37142
37824
|
}
|
|
37143
37825
|
async function initApp(payload) {
|
|
37144
37826
|
const pb = await initPocketBaseClient(payload.db);
|
|
37145
37827
|
const app_db = new AppDatabase(pb);
|
|
37146
|
-
|
|
37828
|
+
let _getCredentials = payload.getCredentials;
|
|
37829
|
+
if (payload.password) {
|
|
37830
|
+
try {
|
|
37831
|
+
const credentials = app_db.getCredentials(payload.password);
|
|
37832
|
+
if (credentials) {
|
|
37833
|
+
_getCredentials = (account, exchange) => {
|
|
37834
|
+
const credential = credentials.find((c) => c.name === account && c.exchange === exchange);
|
|
37835
|
+
if (!credential) {
|
|
37836
|
+
throw new Error(`Missing API Key or Secret for account '${account}' in .env file. Please check your environment variables.`);
|
|
37837
|
+
}
|
|
37838
|
+
return {
|
|
37839
|
+
api_key: credential?.api_key,
|
|
37840
|
+
api_secret: credential?.api_secret
|
|
37841
|
+
};
|
|
37842
|
+
};
|
|
37843
|
+
}
|
|
37844
|
+
} catch (error) {
|
|
37845
|
+
console.log("error", error);
|
|
37846
|
+
}
|
|
37847
|
+
}
|
|
37848
|
+
const app = new App(app_db, _getCredentials);
|
|
37147
37849
|
return app;
|
|
37148
37850
|
}
|
|
37149
|
-
|
|
37851
|
+
function getCredentials(account, exchange) {
|
|
37150
37852
|
console.log(`Fetching credentials for account: ${account}, exchange: ${exchange}`);
|
|
37151
37853
|
let apiKey;
|
|
37152
37854
|
let apiSecret;
|
|
@@ -37181,14 +37883,15 @@ async function getCredentials(account, exchange) {
|
|
|
37181
37883
|
api_secret: apiSecret
|
|
37182
37884
|
};
|
|
37183
37885
|
}
|
|
37184
|
-
async function initialize() {
|
|
37886
|
+
async function initialize(password) {
|
|
37185
37887
|
const app = await initApp({
|
|
37186
37888
|
db: {
|
|
37187
37889
|
host: process.env.POCKETBASE_HOST,
|
|
37188
37890
|
email: process.env.POCKETBASE_EMAIL,
|
|
37189
37891
|
password: process.env.POCKETBASE_PASSWORD
|
|
37190
37892
|
},
|
|
37191
|
-
|
|
37893
|
+
password,
|
|
37894
|
+
getCredentials
|
|
37192
37895
|
});
|
|
37193
37896
|
return app;
|
|
37194
37897
|
}
|