DCAv2GUI/app/src/main/java/com/example/dcav2gui/InstanceInterface.java

1201 lines
58 KiB
Java

package com.example.dcav2gui;
import static com.example.dcav2gui.MainActivity.globalSettings;
import android.util.Log;
import androidx.annotation.NonNull;
import com.example.dcav2gui.ui.exchanges.WorkerData;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class InstanceInterface {
private static final String API_BASE_URL = globalSettings.apiUrl;
private static final String API_KEY = globalSettings.apiKey;
private static final String EARN_API_KEY = globalSettings.earnApiKey;
private static final OkHttpClient httpClient = new OkHttpClient();
public static ProfitStatsData getProfitStatsData(boolean retry) throws IOException {
Request profitRequest = new Request.Builder()
.url(API_BASE_URL + "/statistics_server/combined_totals")
.header("X-API-KEY", API_KEY)
.build();
try (Response statsResponse = httpClient.newCall(profitRequest).execute()) {
if (!statsResponse.isSuccessful()) {
if (statsResponse.code() == 503 && retry) {
return getProfitStatsData(false);
}
throw new IOException("Unexpected code " + statsResponse);
}
String stockResponseBody = statsResponse.body().string();
double todaysProfit = getTodaysProfit(stockResponseBody);
double thisMonthProfit = getThisMonthsProfit(stockResponseBody);
// Return the stock quote data
return new ProfitStatsData(
todaysProfit,
thisMonthProfit
);
}
}
private static double getTodaysProfit(String jsonResponse) {
try {
JsonElement jsonElement = JsonParser.parseString(jsonResponse);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return 0.0;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("combined")) {
JsonArray combinedArray = jsonObject.get("combined").getAsJsonArray();
return combinedArray.get(0).getAsDouble();
} else {
System.err.println("The parsed JSON response does not contain a 'combined' array.");
return 0.0;
}
} catch (Exception e) {
System.err.println("Error processing combined profit data: " + e.getMessage());
return 0.0;
}
}
private static double getThisMonthsProfit(String jsonResponse) {
try {
JsonElement jsonElement = JsonParser.parseString(jsonResponse);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return 0.0;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("combined")) {
JsonArray combinedArray = jsonObject.get("combined").getAsJsonArray();
return combinedArray.get(1).getAsDouble();
} else {
System.err.println("The parsed JSON response does not contain a 'combined' array.");
return 0.0;
}
} catch (Exception e) {
System.err.println("Error processing combined profit data: " + e.getMessage());
return 0.0;
}
}
public static double getTraderTime(String exchange, boolean retry) throws IOException {
Request uptimeRequest = new Request.Builder()
.url(API_BASE_URL + "/" + exchange + "/trader_time")
.header("X-API-KEY", API_KEY)
.build();
try (Response uptimeResponse = httpClient.newCall(uptimeRequest).execute()) {
if (!uptimeResponse.isSuccessful()) {
if (uptimeResponse.code() == 503 && retry) {
return getTraderTime(exchange,false);
}
throw new IOException("Unexpected code " + uptimeResponse);
}
String stockResponseBody = uptimeResponse.body().string();
try {
JsonElement jsonElement = JsonParser.parseString(stockResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return 0.0;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("Time")) {
return jsonObject.get("Time").getAsDouble();
} else {
System.err.println("The parsed JSON response does not contain a 'Time' entry.");
return 0.0;
}
} catch (Exception e) {
System.err.println("Error processing uptime data: " + e.getMessage());
return 0.0;
}
}
}
public static double getServerTime(String exchange, boolean retry) throws IOException {
Request serverTimeRequest = new Request.Builder()
.url(API_BASE_URL + "/" + exchange + "/server_time")
.header("X-API-KEY", API_KEY)
.build();
try (Response serverTimeResponse = httpClient.newCall(serverTimeRequest).execute()) {
if (!serverTimeResponse.isSuccessful()) {
if (serverTimeResponse.code() == 503 && retry) {
return getServerTime(exchange,false);
}
throw new IOException("Unexpected code " + serverTimeResponse);
}
String serverTimeResponseBody = serverTimeResponse.body().string();
try {
JsonElement jsonElement = JsonParser.parseString(serverTimeResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return 0.0;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("Time")) {
return jsonObject.get("Time").getAsDouble();
} else {
System.err.println("The parsed JSON response does not contain a 'Time' entry.");
return 0.0;
}
} catch (Exception e) {
System.err.println("Error querying server time: " + e.getMessage());
return 0.0;
}
}
}
public static List<DealData> getAllTrades() throws IOException {
CompletableFuture<List<DealData>> binanceFuture = CompletableFuture.supplyAsync(() -> {
try {
return getLastTradesFromExchange("binance", true);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<List<DealData>> gateioFuture = CompletableFuture.supplyAsync(() -> {
try {
return getLastTradesFromExchange("gateio", true);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<List<DealData>> kucoinFuture = CompletableFuture.supplyAsync(() -> {
try {
return getLastTradesFromExchange("kucoin", true);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<List<DealData>> okxFuture = CompletableFuture.supplyAsync(() -> {
try {
return getLastTradesFromExchange("okex", true);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<Void> allFutures = CompletableFuture.allOf(binanceFuture, gateioFuture, kucoinFuture, okxFuture);
List<DealData> binanceDeals = binanceFuture.join();
List<DealData> gateioDeals = gateioFuture.join();
List<DealData> kucoinDeals = kucoinFuture.join();
List<DealData> okxDeals = okxFuture.join();
List<DealData> allDeals = new ArrayList<>();
allDeals.addAll(binanceDeals);
allDeals.addAll(gateioDeals);
allDeals.addAll(kucoinDeals);
allDeals.addAll(okxDeals);
//Sort deals by timestamp
allDeals.sort(Comparator.comparingDouble(DealData::getTimestamp));
Collections.reverse(allDeals);
return allDeals.subList(0,globalSettings.amountOfLastTrades);
}
public static List<DealData> getLastNTrades(int amount, boolean retry) throws IOException {
// Reads the data directly from the db, instead of from the cache
Request dealsRequest = new Request.Builder()
.url(API_BASE_URL + "/statistics_server/fetch_last_n_deals_without_history?amount_of_deals=" + amount)
.header("X-API-KEY", API_KEY)
.build();
try (Response dealsResponse = httpClient.newCall(dealsRequest).execute()) {
if (!dealsResponse.isSuccessful()) {
if (dealsResponse.code() == 503 && retry) {
return getLastNTrades(amount,false);
}
throw new IOException("Unexpected code " + dealsResponse); }
String dealsResponseBody = dealsResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(dealsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return null;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (!jsonObject.has("last_deals")) {
System.err.println("The parsed JSON response does not contain the last_deals key.");
return null;
}
//Return a list of items of the format:
//(timestamp,pair,amount,exchange_name,order_id,order_history)
//order history always is an empty string when querying the deals cache
//It can be safely ignored
JsonArray dealsArray = jsonObject.getAsJsonArray("last_deals");
List<DealData> dealDataList = new ArrayList<>();
for (int i=0; i<dealsArray.size(); i++){
JsonElement stringToMince = dealsArray.get(i);
dealDataList.add(new DealData(
stringToMince.getAsJsonArray().get(0).getAsDouble(),
stringToMince.getAsJsonArray().get(1).getAsString(),
stringToMince.getAsJsonArray().get(2).getAsDouble(),
stringToMince.getAsJsonArray().get(3).getAsString(),
stringToMince.getAsJsonArray().get(4).getAsString(),
""));
}
return dealDataList;
}
}
public static List<DealData> getLastTradesFromExchange(String exchange, boolean retry) throws IOException {
Request dealsRequest = new Request.Builder()
.url(API_BASE_URL + "/" + exchange + "/get_deals_cache")
.header("X-API-KEY", API_KEY)
.build();
try (Response dealsResponse = httpClient.newCall(dealsRequest).execute()) {
if (!dealsResponse.isSuccessful()) {
if (dealsResponse.code() == 503 && retry) {
return getLastTradesFromExchange(exchange,false);
}
throw new IOException("Unexpected code " + dealsResponse); }
String dealsResponseBody = dealsResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(dealsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return null;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (!jsonObject.has("Deals")) {
System.err.println("The parsed JSON response does not contain the deals key.");
return null;
}
//Return a list of items of the format:
//(timestamp,pair,amount,exchange_name,order_id,order_history)
//order history always is an empty string when querying the deals cache
//It can be safely ignored
JsonArray dealsArray = jsonObject.getAsJsonArray("Deals");
List<DealData> dealDataList = new ArrayList<>();
for (int i=0; i<dealsArray.size(); i++){
JsonElement stringToMince = dealsArray.get(i);
dealDataList.add(new DealData(
stringToMince.getAsJsonArray().get(0).getAsDouble(),
stringToMince.getAsJsonArray().get(1).getAsString(),
stringToMince.getAsJsonArray().get(2).getAsDouble(),
stringToMince.getAsJsonArray().get(3).getAsString(),
stringToMince.getAsJsonArray().get(4).getAsString(),
""));
}
return dealDataList;
}
}
public static String getLastLogs(String exchange, boolean retry) throws IOException {
Request logRequest = new Request.Builder()
.url(API_BASE_URL + "/" + exchange + "/get_log_list")
.header("X-API-KEY", API_KEY)
.build();
try (Response logResponse = httpClient.newCall(logRequest).execute()) {
if (!logResponse.isSuccessful()) {
if (logResponse.code() == 503 && retry) {
return getLastLogs(exchange,false);
}
throw new IOException("Unexpected code " + logResponse);
}
String dealsResponseBody = logResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(dealsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return null;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (!jsonObject.has("Logs")) {
System.err.println("The parsed JSON response does not contain the logs key.");
return null;
}
JsonArray logsArray = jsonObject.getAsJsonArray("Logs");
List<String> logList = new ArrayList<>();
for (int i = logsArray.size()-1; i >= 0; i--) {
logList.add(logsArray.get(i).getAsString());
}
return joinWithLineLimit("\n",globalSettings.amountOfLogLines,logList);
}
}
public static JsonObject getProfitReport(boolean retry) throws IOException {
Request profitsReportRequest = new Request.Builder()
.url(API_BASE_URL + "/statistics_server/fetch_profit_report")
.header("X-API-KEY", API_KEY)
.build();
try (Response profitsReportResponse = httpClient.newCall(profitsReportRequest).execute()) {
if (!profitsReportResponse.isSuccessful()) {
if (profitsReportResponse.code() == 503 && retry) {
return getProfitReport(false);
}
throw new IOException("Unexpected code " + profitsReportResponse);
}
String profitsReportResponseBody = profitsReportResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(profitsReportResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return null;
}
return jsonElement.getAsJsonObject();
}
}
public static String getAllLogs(String exchange, boolean retry) throws IOException {
//200 lines hard limit
Request logRequest = new Request.Builder()
.url(API_BASE_URL + "/statistics_server/fetch_full_log?exchange_name=" + exchange)
.header("X-API-KEY", API_KEY)
.build();
try (Response logResponse = httpClient.newCall(logRequest).execute()) {
if (!logResponse.isSuccessful()) {
if (logResponse.code() == 503 && retry) {
return getAllLogs(exchange,false);
}
throw new IOException("Unexpected code " + logResponse);
}
String dealsResponseBody = logResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(dealsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return null;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (!jsonObject.has("line")) {
System.err.println("The parsed JSON response does not contain the last_lines key.");
return null;
}
JsonArray logsArray = jsonObject.getAsJsonArray("line");
List<String> logList = new ArrayList<>();
for (int i = logsArray.size()-1; i >= 0; i--) {
logList.add(logsArray.get(i).getAsString());
}
return String.join("\n", logList);
}
}
private static double gibSoSize(double orderSize, int n, double safetyOrderScale) {
double targetOrderSize = orderSize;
for (int i = 0; i < n; i++) {
targetOrderSize = targetOrderSize*safetyOrderScale*100;
}
return targetOrderSize;
}
public static ExchangeStatsData getExchangeStatsData(String exchange) throws IOException {
double lastSeen;
double fundsAvailable;
double fundsNeeded = 0;
int onlineWorkers = 0;
int longWorkers = 0;
int shortWorkers = 0;
int safetyOrdersSent = 0;
int maxSafetyOrders = 0;
List<WorkerStatsData> workers;
InstanceGlobalStatsData instanceGlobalStats;
CompletableFuture<Double> traderTimeFuture = CompletableFuture.supplyAsync(() -> {
try {
return getTraderTime(exchange, true);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<Double> fundsAvailableFuture = CompletableFuture.supplyAsync(() -> {
try {
if (globalSettings.useEarn) {
return getFundsAvailableWithEarn(exchange,"USDT", true);
} else {
return getFundsAvailable(exchange,"USDT", true);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<List<WorkerStatsData>> workerStatsFuture = CompletableFuture.supplyAsync(() -> {
try {
return getAllWorkersStats(exchange, true);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<InstanceGlobalStatsData> instanceGlobalStatsFuture = CompletableFuture.supplyAsync(() -> {
try {
return getInstanceGlobalStatus(exchange, true);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<Void> allFutures = CompletableFuture.allOf(traderTimeFuture, fundsAvailableFuture, workerStatsFuture, instanceGlobalStatsFuture);
lastSeen = traderTimeFuture.join();
fundsAvailable = fundsAvailableFuture.join();
workers = workerStatsFuture.join();
instanceGlobalStats = instanceGlobalStatsFuture.join();
if (workers != null) {
for (WorkerStatsData worker : workers) {
if (!worker.isShort) {
longWorkers++;
//Funds needed
for (int i = worker.getSoAmount(); i <= worker.getNumberOfSafetyOrders(); i++) {
fundsNeeded += gibSoSize(worker.getOrderSize(),i,0.0105); // I know I know
}
//Safety orders
safetyOrdersSent+=worker.getSoAmount()-1;
maxSafetyOrders+=worker.getNumberOfSafetyOrders();
} else {
shortWorkers++;
}
}
onlineWorkers = workers.size();
}
return new ExchangeStatsData(lastSeen,fundsAvailable,fundsNeeded,onlineWorkers,longWorkers,shortWorkers,safetyOrdersSent,maxSafetyOrders,instanceGlobalStats,workers);
}
private static double getFundsAvailableWithEarn(String exchange, String coin, boolean retry) throws IOException {
String parsedBroker = exchange;
if (Objects.equals(parsedBroker, "okex")) {
parsedBroker = "okx";
}
Request fundsRequest = new Request.Builder()
.url(API_BASE_URL + "/earn/get_total_balance?broker=" + parsedBroker)
.header("X-API-KEY", EARN_API_KEY)
.build();
try (Response fundsResponse = httpClient.newCall(fundsRequest).execute()) {
if (!fundsResponse.isSuccessful()) {
if (fundsResponse.code() == 503 && retry) {
return getFundsAvailableWithEarn(exchange, coin, false);
}
throw new IOException("Unexpected code " + fundsResponse);
}
String fundsResponseBody = fundsResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(fundsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return 0.0;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (!jsonObject.has("trading_balance")) {
System.err.println("The parsed JSON response does not contain the balances requested.");
return 0.0;
}
return jsonObject.get("trading_balance").getAsDouble() + jsonObject.get("earning_balance").getAsDouble();
}
}
private static double getFundsAvailable(String exchange, String coin, boolean retry) throws IOException {
Request fundsRequest = new Request.Builder()
.url(API_BASE_URL + "/" + exchange + "/get_balance?coin=" + coin)
.header("X-API-KEY", API_KEY)
.build();
try (Response fundsResponse = httpClient.newCall(fundsRequest).execute()) {
if (!fundsResponse.isSuccessful()) {
if (fundsResponse.code() == 503 && retry) {
return getFundsAvailable(exchange, coin, false);
}
throw new IOException("Unexpected code " + fundsResponse);
}
String fundsResponseBody = fundsResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(fundsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return 0.0;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (!jsonObject.has(coin)) {
System.err.println("The parsed JSON response does not contain the coin requested.");
return 0.0;
}
return jsonObject.get(coin).getAsDouble();
}
}
public static List<String> getMissingPairs(String exchange, boolean retry) throws IOException {
List<String> valueToReturn = new ArrayList<>();
Request missingPairsRequest = new Request.Builder()
.url(API_BASE_URL + "/" + exchange + "/missing_pairs")
.header("X-API-KEY", API_KEY)
.build();
try (Response missingPairsResponse = httpClient.newCall(missingPairsRequest).execute()) {
if (!missingPairsResponse.isSuccessful()) {
if (missingPairsResponse.code() == 503 && retry) {
return getMissingPairs(exchange, false);
}
throw new IOException("Unexpected code " + missingPairsResponse);
}
String missingPairsResponseBody = missingPairsResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(missingPairsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return null;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("Error")) {
System.err.println("The parsed JSON response contains an Error field");
return null;
}
if (jsonObject.has("MissingPairs")) {
//Parse the string as a Json array and add it to response
JsonArray jsonArray = jsonObject.getAsJsonArray("MissingPairs");
for (JsonElement jsonElement1 : jsonArray) {
String missingPair = jsonElement1.getAsString();
valueToReturn.add(missingPair);
}
}
return valueToReturn;
}
}
public static List<WorkerStatsData> getAllWorkersStats(String exchange, boolean retry) throws IOException {
List<WorkerStatsData> valueToReturn = new ArrayList<>();
Request workersStatsRequest = new Request.Builder()
.url(API_BASE_URL + "/" + exchange + "/get_all_worker_status")
.header("X-API-KEY", API_KEY)
.build();
try (Response workersStatsResponse = httpClient.newCall(workersStatsRequest).execute()) {
if (!workersStatsResponse.isSuccessful()) {
if (workersStatsResponse.code() == 503 && retry) {
return getAllWorkersStats(exchange, false);
}
throw new IOException("Unexpected code " + workersStatsResponse);
}
String workersStatsResponseBody = workersStatsResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(workersStatsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return null;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("Error")) {
System.err.println("The parsed JSON response contains Error");
return null;
}
for (String key : jsonObject.keySet()) {
JsonObject value = jsonObject.get(key).getAsJsonObject();
try {
boolean isShort = value.has("is_short") && value.get("is_short").isJsonPrimitive() && value.get("is_short").getAsBoolean();
boolean isBoosted = value.has("is_boosted") && value.get("is_boosted").isJsonPrimitive() && value.get("is_boosted").getAsBoolean();
boolean isAutoSwitchEnabled = value.has("autoswitch") && value.get("autoswitch").isJsonPrimitive() && value.get("autoswitch").getAsBoolean();
boolean isPaused = value.has("is_paused") && value.get("is_paused").isJsonPrimitive() && value.get("is_paused").getAsBoolean();
boolean stopWhenProfit = value.has("stop_when_profit") && value.get("stop_when_profit").isJsonPrimitive() && value.get("stop_when_profit").getAsBoolean();
double orderSize = value.has("order_size") && value.get("order_size").isJsonPrimitive() ? value.get("order_size").getAsDouble() : 0.0;
double quoteSpent = value.has("quote_spent") && value.get("quote_spent").isJsonPrimitive() ? value.get("quote_spent").getAsDouble() : 0.0;
double baseBought = value.has("base_bought") && value.get("base_bought").isJsonPrimitive() ? value.get("base_bought").getAsDouble() : 0.0;
int soAmount = value.has("so_amount") && value.get("so_amount").isJsonPrimitive() ? value.get("so_amount").getAsInt() : 0;
int numberOfSafetyOrders = value.has("no_of_safety_orders") && value.get("no_of_safety_orders").isJsonPrimitive() ? value.get("no_of_safety_orders").getAsInt() : 0;
int tpMode = value.has("tp_mode") && value.get("tp_mode").isJsonPrimitive() ? value.get("tp_mode").getAsInt() : 0;
String profitTable = value.has("profit_table") && value.get("profit_table").isJsonPrimitive() ? value.get("profit_table").getAsString() : null;
double startTime = value.has("start_time") && value.get("start_time").isJsonPrimitive() ? value.get("start_time").getAsDouble() : 0.0;
double startPrice = value.has("start_price") && value.get("start_price").isJsonPrimitive() ? value.get("start_price").getAsDouble() : 0.0;
double dealStartTime = value.has("deal_start_time") && value.get("deal_start_time").isJsonPrimitive() ? value.get("deal_start_time").getAsDouble() : 0.0;
double dealUptime = value.has("deal_uptime") && value.get("deal_uptime").isJsonPrimitive() ? value.get("deal_uptime").getAsDouble() : 0.0;
double totalUptime = value.has("total_uptime") && value.get("total_uptime").isJsonPrimitive() ? value.get("total_uptime").getAsDouble() : 0.0;
double price = value.has("price") && value.get("price").isJsonPrimitive() ? value.get("price").getAsDouble() : 0.0;
double takeProfitPrice = value.has("take_profit_price") && value.get("take_profit_price").isJsonPrimitive() ? value.get("take_profit_price").getAsDouble() : 0.0;
double nextSoPrice = value.has("next_so_price") && value.get("next_so_price").isJsonPrimitive() ? value.get("next_so_price").getAsDouble() : 0.0;
JsonObject takeProfitOrder = null;
String tpOrderId = "";
JsonArray safetyOrders = null;
String safetyOrderId = "";
JsonElement takeProfitOrderElement = value.get("take_profit_order");
if (takeProfitOrderElement!=null && !takeProfitOrderElement.isJsonNull() && takeProfitOrderElement.isJsonObject()) {
takeProfitOrder = takeProfitOrderElement.getAsJsonObject();
tpOrderId = takeProfitOrder.has("id") && takeProfitOrder.get("id").isJsonPrimitive() ? takeProfitOrder.get("id").getAsString() : "";
}
JsonElement safetyOrderElements = value.get("safety_orders");
if (safetyOrderElements!=null && !safetyOrderElements.isJsonNull() && safetyOrderElements.isJsonArray()) {
safetyOrders = safetyOrderElements.getAsJsonArray();
if (!safetyOrders.isEmpty()) {
JsonObject firstSafetyOrder = safetyOrders.get(0).getAsJsonObject();
safetyOrderId = firstSafetyOrder.has("id") && firstSafetyOrder.get("id").isJsonPrimitive() ? firstSafetyOrder.get("id").getAsString() : "";
}
}
double feesPaidInBase = value.has("fees_paid_in_base") && value.get("fees_paid_in_base").isJsonPrimitive() ? value.get("fees_paid_in_base").getAsDouble() : 0.0;
double feesPaidInQuote = value.has("fees_paid_in_quote") && value.get("fees_paid_in_quote").isJsonPrimitive() ? value.get("fees_paid_in_quote").getAsDouble() : 0.0;
double partialProfit = value.has("partial_profit") && value.get("partial_profit").isJsonPrimitive() ? value.get("partial_profit").getAsDouble() : 0.0;
String safetyPriceTable = value.has("safety_price_table") && value.get("safety_price_table").isJsonPrimitive() ? value.get("safety_price_table").getAsString() : null;
String dealOrderHistory = value.has("deal_order_history") && value.get("deal_order_history").isJsonPrimitive() ? value.get("deal_order_history").getAsString() : null;
String pauseReason = value.has("pause_reason") && value.get("pause_reason").isJsonPrimitive() ? value.get("pause_reason").getAsString() : null;
String statusString = value.has("status_string") && value.get("status_string").isJsonPrimitive() ? value.get("status_string").getAsString() : null;
JsonObject oldLong;
OldLongDictionary oldLongDictionary = null;
if (value.has("old_long")) {
oldLong = value.get("old_long").getAsJsonObject();
if (!oldLong.entrySet().isEmpty()) {
oldLongDictionary = new OldLongDictionary(
oldLong.get("datetime").getAsString(),
oldLong.get("fees_paid_in_quote").getAsDouble(),
oldLong.get("quote_spent").getAsDouble(),
oldLong.get("tp_amount").getAsDouble(),
oldLong.get("tp_price").getAsDouble()
);
}
}
valueToReturn.add(new WorkerStatsData(
key,
isShort,
isBoosted,
isPaused,
isAutoSwitchEnabled,
stopWhenProfit,
orderSize,
quoteSpent,
baseBought,
soAmount,
numberOfSafetyOrders,
tpMode,
profitTable,
startTime,
startPrice,
dealStartTime,
dealUptime,
totalUptime,
price,
takeProfitPrice,
nextSoPrice,
tpOrderId,
takeProfitOrder,
safetyOrderId,
safetyOrders,
feesPaidInBase,
feesPaidInQuote,
partialProfit,
safetyPriceTable,
dealOrderHistory,
pauseReason,
oldLongDictionary,
statusString
));
} catch (Exception e) {
System.err.println("In getAllWorkersStats: Error processing JSON for key '" + key + "': " + e.getMessage());
}
}
}
return valueToReturn;
}
public static String joinWithLineLimit(String delimiter, int maxLines, Iterable<String> elements) {
StringBuilder result = new StringBuilder();
int currentLineCount = 0;
for (String element : elements) {
if (currentLineCount >= maxLines) {
break;
}
if (result.length() > 0) {
result.append(delimiter);
}
result.append(element);
currentLineCount++;
}
return result.toString();
}
public static InstanceGlobalStatsData getInstanceGlobalStatus(String exchange, boolean retry) throws IOException {
Request globalStatsRequest = new Request.Builder()
.url(API_BASE_URL + "/" + exchange + "/global_status")
.header("X-API-KEY", API_KEY)
.build();
try (Response globalStatsResponse = httpClient.newCall(globalStatsRequest).execute()) {
if (!globalStatsResponse.isSuccessful()) {
if (globalStatsResponse.code() == 503 && retry) {
return getInstanceGlobalStatus(exchange, false);
}
throw new IOException("Unexpected code " + globalStatsResponse.code());
}
String globalStatsResponseBody = globalStatsResponse.body().string();
JsonElement jsonElement = JsonParser.parseString(globalStatsResponseBody);
if (!jsonElement.isJsonObject()) {
System.err.println("The parsed JSON response is not a JsonObject.");
return null;
}
if (jsonElement.getAsJsonObject().has("Error")) {
System.err.println("There is an Error field in the JSON response." + jsonElement.getAsJsonObject().get("Error").getAsString());
return null;
}
JsonObject jsonObject = jsonElement.getAsJsonObject();
// Extracting online_workers and paused_traders from the response
JsonArray onlineWorkersJsonArray = jsonObject.get("online_workers").getAsJsonArray();
List<String> onlineWorkers = new ArrayList<>();
for (int i = 0; i < onlineWorkersJsonArray.size(); i++) {
onlineWorkers.add(onlineWorkersJsonArray.get(i).getAsString());
}
JsonArray pausedTradersJsonArray = jsonObject.get("paused_traders").getAsJsonArray();
List<String> pausedTraders = new ArrayList<>();
for (int i = 0; i < pausedTradersJsonArray.size(); i++) {
pausedTraders.add(pausedTradersJsonArray.get(i).getAsString());
}
// Assuming jsonObject is already defined and represents the root JSON object
JsonObject generalDataObject = jsonObject.getAsJsonObject("config");
JsonArray pairJsonArray = generalDataObject.get("pairs").getAsJsonArray();
List<String> pairsInConfig = new ArrayList<>();
for (int i = 0; i < pairJsonArray.size(); i++) {
pairsInConfig.add(pairJsonArray.get(i).getAsString());
}
//Extract BrokerConfigData from the JSON response
BrokerConfigData brokerConfigData;
try {
String exchangeName = generalDataObject.has("exchange") && generalDataObject.get("exchange").isJsonPrimitive() ? generalDataObject.get("exchange").getAsString() : null;
boolean isSandbox = generalDataObject.has("is_sandbox") && generalDataObject.get("is_sandbox").isJsonPrimitive() && generalDataObject.get("is_sandbox").getAsBoolean();
boolean simulateMarketOrders = generalDataObject.has("simulate_market_orders") && generalDataObject.get("simulate_market_orders").isJsonPrimitive() && generalDataObject.get("simulate_market_orders").getAsBoolean();
//pairsInConfig
double lap_time = generalDataObject.has("lap_time") && generalDataObject.get("lap_time").isJsonPrimitive() ? generalDataObject.get("lap_time").getAsDouble() : 2.0;
double cooldown_multiplier = generalDataObject.has("cooldown_multiplier") && generalDataObject.get("cooldown_multiplier").isJsonPrimitive() ? generalDataObject.get("cooldown_multiplier").getAsDouble() : 1.0;
double wait_before_new_safety_order = generalDataObject.has("wait_before_new_safety_order") && generalDataObject.get("wait_before_new_safety_order").isJsonPrimitive() ? generalDataObject.get("wait_before_new_safety_order").getAsDouble() : 0.0;
boolean telegram = generalDataObject.has("telegram") && generalDataObject.get("telegram").isJsonPrimitive() && generalDataObject.get("telegram").getAsBoolean();
boolean attempt_to_restart = !generalDataObject.has("attempt_to_restart") || !generalDataObject.get("attempt_to_restart").isJsonPrimitive() || generalDataObject.get("attempt_to_restart").getAsBoolean();
double default_order_size = generalDataObject.has("default_order_size") && generalDataObject.get("default_order_size").isJsonPrimitive() ? generalDataObject.get("default_order_size").getAsDouble() : 12.0;
boolean unifiedOrderQuery = !generalDataObject.has("unifiedOrderQuery") || !generalDataObject.get("unifiedOrderQuery").isJsonPrimitive() || generalDataObject.get("unifiedOrderQuery").getAsBoolean();
brokerConfigData = new BrokerConfigData(exchangeName, isSandbox, simulateMarketOrders, pairsInConfig, lap_time, cooldown_multiplier, wait_before_new_safety_order, telegram, attempt_to_restart, default_order_size, unifiedOrderQuery);
} catch (Exception e) {
e.printStackTrace();
return null;
}
InstanceGlobalStatsData returnValue;
try {
returnValue = new InstanceGlobalStatsData(
jsonObject.get("name").getAsString(),
jsonObject.get("instance_uptime").getAsDouble(),
onlineWorkers,
pausedTraders,
jsonObject.get("version").getAsString(),
jsonObject.get("ccxt_version").getAsString(),
brokerConfigData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return returnValue;
} catch (JsonSyntaxException e) {
System.err.println("The JSON response is not valid.");
return null;
}
}
public static List<WorkerData> translateToWorkerData(List<InstanceInterface.WorkerStatsData> workerStatsDataList) {
//Translates from WorkerStatsData to WorkerData (for cards)
List<WorkerData> workerDataList = new ArrayList<>();
if (workerStatsDataList != null) {
for (InstanceInterface.WorkerStatsData workerStatsData : workerStatsDataList) {
WorkerData workerData = new WorkerData(
workerStatsData.getPair(),
workerStatsData.getSoAmount(),
workerStatsData.getNumberOfSafetyOrders(),
workerStatsData.getDealUptime(),
workerStatsData.getNextSoPrice(),
workerStatsData.getPrice(),
workerStatsData.getTakeProfitPrice(),
workerStatsData.getQuoteSpent(),
workerStatsData.getBaseBought(),
workerStatsData.getIsShort(),
workerStatsData.getIsBoosted(),
workerStatsData.getAutoSwitchEnabled(),
workerStatsData.getIsPaused(),
workerStatsData.getStopWhenProfit(),
workerStatsData.getOldLongDictionary());
workerDataList.add(workerData);
}
}
// Sort the list by uptime
workerDataList.sort(new Comparator<WorkerData>() {
@Override
public int compare(WorkerData w1, WorkerData w2) {
return Double.compare(w1.getUptime(), w2.getUptime());
}
});
return workerDataList;
}
public static class DealData {
private final double timestamp;
private final String pair;
private final double amount;
private final String exchangeName;
private final String orderId;
private final String orderHistory;
public DealData(double timestamp, String pair, double amount, String exchangeName, String orderId, String orderHistory) {
this.timestamp = timestamp;
this.pair = pair;
this.amount = amount;
this.exchangeName = exchangeName;
this.orderId = orderId;
this.orderHistory = orderHistory;
}
public double getTimestamp() { return timestamp; }
public String getPair() { return pair; }
public double getAmount() {return amount; }
public String getExchangeName() { return exchangeName; }
public String getOrderId() { return orderId; }
public String getOrderHistory() { return orderHistory; }
}
public static class InstanceGlobalStatsData {
private final String instanceName;
private final double instanceUptime;
private final List<String> onlineWorkers;
private final List<String> pausedTraders;
private final String version;
private final String ccxtVersion;
private final BrokerConfigData config;
public InstanceGlobalStatsData(String name, double uptime, List<String> onlineWorkers, List<String> pausedTraders, String version, String ccxtVersion, BrokerConfigData config) {
this.instanceName = name;
this.instanceUptime = uptime;
this.onlineWorkers = onlineWorkers;
this.pausedTraders = pausedTraders;
this.version = version;
this.ccxtVersion = ccxtVersion;
this.config = config;
}
public String getName() { return instanceName; }
public double getUptime() { return instanceUptime; }
public List<String> getOnlineWorkers() { return onlineWorkers; }
public List<String> getPausedTraders() { return pausedTraders; }
public String getVersion() { return version; }
public String getCcxtVersion() { return ccxtVersion; }
public BrokerConfigData getConfig() { return config; }
}
public static class BrokerConfigData {
// Some fields are ignored, even though they are in the JSON
private final String brokerName;
private final Boolean isSandbox;
private final Boolean simulateMarketOrders;
private final List<String> pairs;
private final double laptime;
private final double cooldownMultiplier;
private final double waitBeforeNewSafetyOrder;
private final Boolean sendTelegram;
private final Boolean attemptToRestart;
private final double defaultOrderSize;
private final Boolean unifiedOrderQuery;
public BrokerConfigData(String brokerName, Boolean isSandbox, Boolean simulateMarketOrders, List<String> pairs, double laptime, double cooldownMultiplier, double waitBeforeNewSafetyOrder, Boolean sendTelegram, Boolean attemptToRestart, double defaultOrderSize, Boolean unifiedOrderQuery) {
this.brokerName = brokerName;
this.isSandbox = isSandbox;
this.simulateMarketOrders = simulateMarketOrders;
this.pairs = pairs;
this.laptime = laptime;
this.cooldownMultiplier = cooldownMultiplier;
this.waitBeforeNewSafetyOrder = waitBeforeNewSafetyOrder;
this.sendTelegram = sendTelegram;
this.attemptToRestart = attemptToRestart;
this.defaultOrderSize = defaultOrderSize;
this.unifiedOrderQuery = unifiedOrderQuery;
}
public String getBrokerName() { return brokerName; }
public Boolean getIsSandbox() { return isSandbox; }
public Boolean getSimulateMarketOrders() { return simulateMarketOrders; }
public List<String> getPairs() { return pairs; }
public double getLaptime() { return laptime; }
public double getCooldownMultiplier() { return cooldownMultiplier; }
public double getWaitBeforeNewSafetyOrder() { return waitBeforeNewSafetyOrder; }
public Boolean getSendTelegram() { return sendTelegram; }
public Boolean getAttemptToRestart() { return attemptToRestart; }
public double getDefaultOrderSize() { return defaultOrderSize; }
public Boolean getUnifiedOrderQuery() { return unifiedOrderQuery; }
@NonNull
@Override
public String toString() {
return "Name: " + brokerName + "\n" +
"Testnet: " + isSandbox.toString() + "\n" +
"Simulate market orders: " + simulateMarketOrders.toString() + "\n" +
"Pairs: " + pairs + "\n" +
"Lap time: " + laptime + " seconds \n" +
"Cooldown multiplier: " + cooldownMultiplier + "\n" +
"Wait time before a new safety order: " + waitBeforeNewSafetyOrder + "\n" +
"Telegram: " + sendTelegram.toString() + "\n" +
"Restart: " + attemptToRestart.toString() + "\n" +
"Default order size: " + defaultOrderSize + "\n" +
"Unified order query: " + unifiedOrderQuery;
}
}
public static class ProfitStatsData {
private final double profitsToday;
private final double profitsThisMonth;
public ProfitStatsData(double profitsToday, double profitsThisMonth) {
this.profitsToday = profitsToday;
this.profitsThisMonth = profitsThisMonth;
}
public double getProfitsToday() { return profitsToday; }
public double getProfitsThisMonth() { return profitsThisMonth; }
}
public static class ExchangeStatsData {
private final double lastSeen;
private final double fundsAvailable;
private final double fundsNeeded;
private final int onlineWorkers;
private final int longWorkers;
private final int shortWorkers;
private final int safetyOrdersSent;
private final int maxSafetyOrders;
private final InstanceGlobalStatsData globalStats;
private final List<WorkerStatsData> workers;
public ExchangeStatsData(double lastSeen, double fundsAvailable, double fundsNeeded, int onlineWorkers, int longWorkers, int shortWorkers, int safetyOrdersSent, int maxSafetyOrders, InstanceGlobalStatsData globalStats, List<WorkerStatsData> workers) {
this.lastSeen = lastSeen;
this.fundsAvailable = fundsAvailable;
this.fundsNeeded = fundsNeeded;
this.onlineWorkers = onlineWorkers;
this.longWorkers = longWorkers;
this.shortWorkers = shortWorkers;
this.safetyOrdersSent = safetyOrdersSent;
this.maxSafetyOrders = maxSafetyOrders;
this.globalStats = globalStats;
this.workers = workers;
}
public double getLastSeen() { return lastSeen; }
public double getFundsAvailable() { return fundsAvailable; }
public double getFundsNeeded() { return fundsNeeded; }
public int getOnlineWorkers() { return onlineWorkers; }
public int getLongWorkers() { return longWorkers; }
public int getShortWorkers() { return shortWorkers; }
public int getSafetyOrdersSent() {return safetyOrdersSent; }
public int getMaxSafetyOrders() {return maxSafetyOrders; }
public InstanceGlobalStatsData getGlobalStats() { return globalStats; }
public List<WorkerStatsData> getWorkers() { return workers; }
}
public static class WorkerStatsData {
private final String pair;
private final boolean isShort;
private final boolean isBoosted;
private final boolean isPaused;
private final boolean autoSwitchEnabled;
private final boolean stopWhenProfit;
private final double orderSize;
private final double quoteSpent;
private final double baseBought;
private final int soAmount;
private final int numberOfSafetyOrders;
private final int tpMode;
private final String profitTable;
private final double startTime;
private final double startPrice;
private final double dealStartTime;
private final double dealUptime;
private final double totalUptime;
private final double price;
private final double takeProfitPrice;
private final double nextSoPrice;
private final String tpOrderId;
private final JsonObject takeProfitOrder;
private final String soOrderId;
private final JsonArray safetyOrders;
private final double feesPaidInBase;
private final double feesPaidInQuote;
private final double partialProfit;
private final String safetyPriceTable;
private final String dealOrderHistory;
private final String pauseReason;
private final OldLongDictionary oldLongDictionary; //Change type
private final String statusString;
public WorkerStatsData(String pair, boolean isShort, boolean isBoosted, boolean isPaused, boolean autoSwitchEnabled, boolean stopWhenProfit, double orderSize, double quoteSpent, double baseBought, int soAmount, int numberOfSafetyOrders, int tpMode, String profitTable, double startTime, double startPrice, double dealStartTime, double dealUptime, double totalUptime, double price, double takeProfitPrice, double nextSoPrice, String tpOrderId, JsonObject takeProfitOrder, String soOrderId, JsonArray safetyOrders, double feesPaidInBase, double feesPaidInQuote, double partialProfit, String safetyPriceTable, String dealOrderHistory, String pauseReason, OldLongDictionary oldLong, String statusString) {
this.pair = pair;
this.isShort = isShort;
this.isBoosted = isBoosted;
this.isPaused = isPaused;
this.autoSwitchEnabled = autoSwitchEnabled;
this.stopWhenProfit = stopWhenProfit;
this.orderSize = orderSize;
this.quoteSpent = quoteSpent;
this.baseBought = baseBought;
this.soAmount = soAmount;
this.numberOfSafetyOrders = numberOfSafetyOrders;
this.tpMode = tpMode;
this.profitTable = profitTable;
this.startTime = startTime;
this.startPrice = startPrice;
this.dealStartTime = dealStartTime;
this.dealUptime = dealUptime;
this.totalUptime = totalUptime;
this.price = price;
this.takeProfitPrice = takeProfitPrice;
this.nextSoPrice = nextSoPrice;
this.tpOrderId = tpOrderId;
this.takeProfitOrder = takeProfitOrder;
this.soOrderId = soOrderId;
this.safetyOrders = safetyOrders;
this.feesPaidInBase = feesPaidInBase;
this.feesPaidInQuote = feesPaidInQuote;
this.partialProfit = partialProfit;
this.safetyPriceTable = safetyPriceTable;
this.dealOrderHistory = dealOrderHistory;
this.pauseReason = pauseReason;
this.oldLongDictionary = oldLong;
this.statusString = statusString;
}
public String getPair() {return pair; }
public boolean getIsShort() { return isShort; }
public boolean getIsBoosted() { return isBoosted; }
public boolean getAutoSwitchEnabled() { return autoSwitchEnabled; }
public boolean getStopWhenProfit() { return stopWhenProfit; }
public double getOrderSize() { return orderSize; }
public double getQuoteSpent() { return quoteSpent; }
public double getBaseBought() { return baseBought; }
public int getSoAmount() { return soAmount; }
public int getNumberOfSafetyOrders() {return numberOfSafetyOrders; }
public int getTpMode() { return tpMode; }
public String getProfitTable() { return profitTable; }
public double getStartTime() { return startTime; }
public double getStartPrice() { return startPrice; }
public double getDealStartTime() { return dealStartTime; }
public double getDealUptime() { return dealUptime; }
public double getTotalUptime() { return totalUptime; }
public double getPrice() { return price; }
public double getTakeProfitPrice() { return takeProfitPrice; }
public double getNextSoPrice() { return nextSoPrice; }
public String getTpOrderId() { return tpOrderId; }
public JsonObject getTakeProfitOrder() { return takeProfitOrder; }
public String getSoOrderId() { return soOrderId; }
public JsonArray getSafetyOrders() { return safetyOrders; }
public double getFeesPaidInBase() { return feesPaidInBase; }
public double getFeesPaidInQuote() { return feesPaidInQuote; }
public double getPartialProfit() { return partialProfit; }
public String getSafetyPriceTable() { return safetyPriceTable; }
public String getDealOrderHistory() { return dealOrderHistory; }
public String getPauseReason() { return pauseReason; }
public OldLongDictionary getOldLongDictionary() { return oldLongDictionary; }
public String getStatusString() { return statusString; }
public boolean getIsPaused() { return isPaused; }
}
public static class Order {
private final String id;
private final String status;
private final double filled;
private final double remaining;
private final double price;
private final double cost;
private final String fees;
private final String symbol;
public Order(String id, String status, double filled, double remaining, double price, double cost, String fees, String symbol) {
this.id = id;
this.status = status;
this.filled = filled;
this.remaining = remaining;
this.price = price;
this.cost = cost;
this.fees = fees;
this.symbol = symbol;
}
public String getId() { return id; }
public String getStatus() { return status; }
public double getFilled() { return filled; }
public double getRemaining() { return remaining; }
public double getPrice() { return price; }
public double getCost() { return cost; }
public String getFees() { return fees; }
public String getSymbol() { return symbol; }
}
public static class OldLongDictionary {
private final String datetime;
private final double feesPaidInQuote;
private final double quoteSpent;
private final double tpAmount;
private final double tpPrice;
public OldLongDictionary(String datetime, double feesPaidInQuote, double quoteSpent, double tpAmount, double tpPrice) {
this.datetime = datetime;
this.feesPaidInQuote = feesPaidInQuote;
this.quoteSpent = quoteSpent;
this.tpAmount = tpAmount;
this.tpPrice = tpPrice;
}
public String getDatetime() { return datetime; }
public double getFeesPaidInQuote() { return feesPaidInQuote; }
public double getQuoteSpent() { return quoteSpent; }
public double getTpAmount() { return tpAmount; }
public double getTpPrice() { return tpPrice; }
}
}