diff --git a/app/src/main/java/com/example/dcav2gui/InstanceInterface.java b/app/src/main/java/com/example/dcav2gui/InstanceInterface.java index 7f25a4f..5da1185 100644 --- a/app/src/main/java/com/example/dcav2gui/InstanceInterface.java +++ b/app/src/main/java/com/example/dcav2gui/InstanceInterface.java @@ -2,11 +2,6 @@ package com.example.dcav2gui; import static com.example.dcav2gui.MainActivity.globalSettings; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.style.ForegroundColorSpan; - import androidx.annotation.NonNull; import com.example.dcav2gui.ui.exchanges.WorkerData; @@ -21,7 +16,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Locale; import java.util.concurrent.CompletableFuture; import okhttp3.OkHttpClient; @@ -356,6 +350,31 @@ public class InstanceInterface { } + 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() @@ -391,9 +410,9 @@ public class InstanceInterface { 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++) { diff --git a/app/src/main/java/com/example/dcav2gui/ui/home/HomeFragment.java b/app/src/main/java/com/example/dcav2gui/ui/home/HomeFragment.java index f5b8399..9b1609b 100644 --- a/app/src/main/java/com/example/dcav2gui/ui/home/HomeFragment.java +++ b/app/src/main/java/com/example/dcav2gui/ui/home/HomeFragment.java @@ -34,9 +34,8 @@ import com.example.dcav2gui.InstanceInterface; import com.example.dcav2gui.MainActivity; import com.example.dcav2gui.R; import com.example.dcav2gui.TickerTracker; -import com.example.dcav2gui.databinding.FragmentHomeBinding; -import com.example.dcav2gui.ui.exchanges.WorkerData; import com.example.dcav2gui.ui.exchanges.adapters.WorkerCardAdapter; +import com.google.gson.JsonObject; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -49,7 +48,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.io.IOException; -import java.util.stream.Collectors; public class HomeFragment extends Fragment { private HomeViewModel homeViewModel; @@ -317,7 +315,7 @@ public class HomeFragment extends Fragment { @Override public boolean onMenuItemClick(MenuItem item) { if (item.getItemId() == R.id.today_this_month_details) { - // TODO: Implement as is profit_report.py + fetchProfitsReport(); return true; } return false; @@ -398,7 +396,19 @@ public class HomeFragment extends Fragment { new Handler(Looper.getMainLooper()).post(() -> showExtendedLogsDialog(exchange, result)); } catch (IOException e) { e.printStackTrace(); - new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(getContext(), "Failed to fetch missing pairs", Toast.LENGTH_SHORT).show()); + new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(getContext(), "Failed to fetch all logs", Toast.LENGTH_SHORT).show()); + } + }).start(); + } + + private void fetchProfitsReport() { + new Thread(() -> { + try { + JsonObject result = InstanceInterface.getProfitReport(true); + new Handler(Looper.getMainLooper()).post(() -> showProfitsReport(result)); + } catch (IOException e) { + e.printStackTrace(); + new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(getContext(), "Failed to fetch profits report", Toast.LENGTH_SHORT).show()); } }).start(); } @@ -574,6 +584,53 @@ public class HomeFragment extends Fragment { builder.show(); } + private void showProfitsReport(JsonObject result) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + + LayoutInflater inflater = LayoutInflater.from(getContext()); + // Reuse trades_detail_dialog layout + View customLayout = inflater.inflate(R.layout.trades_detail_dialog, null); + TextView titleTextView = customLayout.findViewById(R.id.trades_details_title); + TextView detailsTextView = customLayout.findViewById(R.id.trades_details_content); + + String last60Days = ""; + for (String key: result.get("Last 60 days").getAsJsonObject().keySet()) { + Object value = result.get("Last 60 days").getAsJsonObject().get(key).getAsDouble(); + last60Days = last60Days + key + ": " + String.format(Locale.ROOT, "%.2f",value) + "\n"; + } + String last18Months = ""; + for (String key: result.get("Last 18 months").getAsJsonObject().keySet()) { + Object value = result.get("Last 18 months").getAsJsonObject().get(key).getAsDouble(); + last18Months = last18Months + key + ": " + String.format(Locale.ROOT, "%.2f",value) + "\n"; + } + + String title = "Profits report"; + titleTextView.setText(title); + String dialogBody = "Last 60 days:\n" + + last60Days + "\n" + + "Last 18 months:\n" + + last18Months + "\n" + + "Last 30 days average: " + String.format(Locale.ROOT, "%.2f", result.get("Last 30 days average").getAsDouble()) + "\n" + + "Last 7 days average: " + String.format(Locale.ROOT, "%.2f", result.get("Last 7 days average").getAsDouble()) + "\n" + + "This month projection: " + String.format(Locale.ROOT, "%.2f", result.get("This month projection").getAsDouble()) + "\n\n" + + "Per exchange: \n" + + "Binance: " + String.format(Locale.ROOT, "%.2f", result.get("Binance").getAsDouble()) + " (" + + String.format(Locale.ROOT, "%.2f", result.get("Binance percentage").getAsDouble()) + "%)\n" + + "Gate.io: " + String.format(Locale.ROOT, "%.2f", result.get("Gateio").getAsDouble()) + " (" + + String.format(Locale.ROOT, "%.2f", result.get("Gateio percentage").getAsDouble()) + "%)\n" + + "Kucoin: " + String.format(Locale.ROOT, "%.2f", result.get("Kucoin").getAsDouble()) + " (" + + String.format(Locale.ROOT, "%.2f", result.get("Kucoin percentage").getAsDouble()) + "%)\n" + + "OKX: " + String.format(Locale.ROOT, "%.2f", result.get("OKX").getAsDouble()) + " (" + + String.format(Locale.ROOT, "%.2f", result.get("OKX percentage").getAsDouble()) + "%)\n\n" + + "Total: " + String.format(Locale.ROOT, "%.2f", result.get("Total profit").getAsDouble()); + + detailsTextView.setText(dialogBody); + + builder.setView(customLayout); + builder.setPositiveButton("OK", null); + builder.show(); + } + private void showInstanceDetailsDialog(InstanceInterface.InstanceGlobalStatsData result) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setTitle(result.getName() + " details");