diff --git a/app/src/main/java/com/example/dcav2gui/InstanceInterface.java b/app/src/main/java/com/example/dcav2gui/InstanceInterface.java index 8cc80d6..86d7556 100644 --- a/app/src/main/java/com/example/dcav2gui/InstanceInterface.java +++ b/app/src/main/java/com/example/dcav2gui/InstanceInterface.java @@ -2,6 +2,11 @@ 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; @@ -16,6 +21,7 @@ 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; @@ -220,6 +226,53 @@ public class InstanceInterface { return allDeals.subList(0,globalSettings.amountOfLastTrades); } + public static List 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 dealDataList = new ArrayList<>(); + for (int i=0; i getLastTradesFromExchange(String exchange, boolean retry) throws IOException { Request dealsRequest = new Request.Builder() 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 0afc9dd..400b78a 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 @@ -1,5 +1,7 @@ package com.example.dcav2gui.ui.home; +import static com.example.dcav2gui.InstanceInterface.getLastNTrades; + import android.annotation.SuppressLint; import android.app.AlertDialog; import android.graphics.Color; @@ -33,6 +35,7 @@ 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 java.text.SimpleDateFormat; @@ -46,6 +49,7 @@ 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; @@ -361,8 +365,7 @@ public class HomeFragment extends Fragment { @Override public boolean onMenuItemClick(MenuItem item) { if (item.getItemId() == R.id.last_trades_details) { - // Handle the "Details..." option - // For example, navigate to a details fragment + fetchLastNDeals(100); return true; } return false; @@ -494,6 +497,59 @@ public class HomeFragment extends Fragment { } } + private void fetchLastNDeals(int amount) { + new Thread(() -> { + try { + List result = getLastNTrades(amount, true); + new Handler(Looper.getMainLooper()).post(() -> showLastTradesDetailsDialog(result)); + } catch (IOException e) { + e.printStackTrace(); + new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(getContext(), "Failed to fetch details", Toast.LENGTH_SHORT).show()); + } + }).start(); + } + + private List getShortWorkers(String exchange, List workerList) { + List shortWorkers = new ArrayList<>(); + for (InstanceInterface.WorkerStatsData worker : MainActivity.getInstanceCache(exchange).getWorkers()) { + if (worker.getIsShort()) { + shortWorkers.add(worker.getPair()); + } + } + return shortWorkers; + } + + private void showLastTradesDetailsDialog(List result) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + List shortWorkers = new ArrayList<>(); + if (MainActivity.getInstanceCache("binance")!=null) { + shortWorkers.addAll(getShortWorkers("binance", MainActivity.getInstanceCache("binance").getWorkers())); + } + if (MainActivity.getInstanceCache("gateio")!=null) { + shortWorkers.addAll(getShortWorkers("binance", MainActivity.getInstanceCache("gateio").getWorkers())); + } + if (MainActivity.getInstanceCache("kucoin")!=null) { + shortWorkers.addAll(getShortWorkers("binance", MainActivity.getInstanceCache("kucoin").getWorkers())); + } + if (MainActivity.getInstanceCache("okex")!=null) { + shortWorkers.addAll(getShortWorkers("binance", MainActivity.getInstanceCache("okex").getWorkers())); + } + + LayoutInflater inflater = LayoutInflater.from(getContext()); + 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 title = "Last " + result.size() + " trades"; + titleTextView.setText(title); + detailsTextView.setText(parseDeals(result, shortWorkers)); + + 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"); @@ -744,6 +800,35 @@ public class HomeFragment extends Fragment { }); } + public SpannableStringBuilder parseDeals(List dealData, List shortWorkers) { + int shortColor = ContextCompat.getColor(requireContext(), R.color.orange); + int LongColor = ContextCompat.getColor(requireContext(), R.color.dark_cyan); + if (dealData != null) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); + for (InstanceInterface.DealData deal : dealData) { + String timestamp = timeStampConverter(deal.getTimestamp()); + String pair = deal.getPair(); + String amount = String.format(Locale.ROOT, "%.2f", deal.getAmount()); + String exchange = Character.toUpperCase(deal.getExchangeName().charAt(0)) + deal.getExchangeName().substring(1); + String dealString = timestamp + " | " + pair + " | " + amount + " USDT | " + exchange + "\n"; + SpannableString spannableString = new SpannableString(dealString); + int startIndex = dealString.indexOf(pair); + int endIndex = startIndex + pair.length(); + if ((startIndex != -1) && (endIndex != -1)) { + //If pair in shortWorkers, set color to yellow, else cyan + if (shortWorkers.contains(pair)) { + spannableString.setSpan(new ForegroundColorSpan(shortColor), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + spannableString.setSpan(new ForegroundColorSpan(LongColor), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + spannableStringBuilder.append(spannableString); + } + return spannableStringBuilder; + } + return null; + } + public void populateHome(TickerTracker.PriceChangeData firstTickerChangeData, TickerTracker.PriceChangeData secondTickerChangeData, TickerTracker.PriceChangeData thirdTickerChangeData, @@ -758,9 +843,6 @@ public class HomeFragment extends Fragment { String kucoinLogs, String okexLogs) { - int shortColor = ContextCompat.getColor(requireContext(), R.color.orange); - int LongColor = ContextCompat.getColor(requireContext(), R.color.dark_cyan); - List shortWorkers = new ArrayList<>(); //Populate short traders list @@ -960,29 +1042,9 @@ public class HomeFragment extends Fragment { exchange4WorkersLongShort.setText(longShortWorkers); } + // Deals list if (dealData != null) { - SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); - String dealsList = ""; - for (InstanceInterface.DealData deal : dealData) { - String timestamp = timeStampConverter(deal.getTimestamp()); - String pair = deal.getPair(); - String amount = String.format(Locale.ROOT, "%.2f", deal.getAmount()); - String exchange = Character.toUpperCase(deal.getExchangeName().charAt(0)) + deal.getExchangeName().substring(1); - String dealString = timestamp + " | " + pair + " | " + amount + " USDT | " + exchange + "\n"; - SpannableString spannableString = new SpannableString(dealString); - int startIndex = dealString.indexOf(pair); - int endIndex = startIndex + pair.length(); - if ((startIndex != -1) && (endIndex != -1)) { - //If pair in shortWorkers, set color to yellow, else cyan - if (shortWorkers.contains(pair)) { - spannableString.setSpan(new ForegroundColorSpan(shortColor), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } else { - spannableString.setSpan(new ForegroundColorSpan(LongColor), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - spannableStringBuilder.append(spannableString); - } - lastTrades.setText(spannableStringBuilder); + lastTrades.setText(parseDeals(dealData, shortWorkers)); } //Populate logs diff --git a/app/src/main/res/layout/trades_detail_dialog.xml b/app/src/main/res/layout/trades_detail_dialog.xml new file mode 100644 index 0000000..48eefc4 --- /dev/null +++ b/app/src/main/res/layout/trades_detail_dialog.xml @@ -0,0 +1,29 @@ + + + + + + + + + \ No newline at end of file