diff --git a/app/src/main/java/com/example/dcav2gui/InstanceInterface.java b/app/src/main/java/com/example/dcav2gui/InstanceInterface.java index 5908feb..f252d4e 100644 --- a/app/src/main/java/com/example/dcav2gui/InstanceInterface.java +++ b/app/src/main/java/com/example/dcav2gui/InstanceInterface.java @@ -7,6 +7,7 @@ import android.util.Log; import androidx.annotation.NonNull; import com.example.dcav2gui.ui.exchanges.WorkerData; +import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -21,8 +22,10 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.RequestBody; import okhttp3.Response; @@ -379,6 +382,45 @@ public class InstanceInterface { } } + + public static JsonObject modDefaultOrderSize(String exchange, double newOrderSize, boolean retry) throws IOException { + Gson gson = new Gson(); + JsonObject jsonPayload = new JsonObject(); + jsonPayload.addProperty("amount", String.valueOf(newOrderSize)); + String jsonPayloadString = gson.toJson(jsonPayload); + + RequestBody requestBody = RequestBody.create(jsonPayloadString, MediaType.get("application/json; charset=utf-8")); + Request modDefaultOrderSizeRequest = new Request.Builder() + .url(API_BASE_URL + "/" + exchange + "/mod_default_order_size") + .header("X-API-KEY", API_KEY) + .post(requestBody) + .build(); + + try (Response modDefaultOrderSizeResponse = httpClient.newCall(modDefaultOrderSizeRequest).execute()) { + if (!modDefaultOrderSizeResponse.isSuccessful()) { + if (modDefaultOrderSizeResponse.code() == 503 && retry) { + return modDefaultOrderSize(exchange, newOrderSize,false); + } + throw new IOException("Unexpected code " + modDefaultOrderSizeResponse); + } + String modDefaultOrderSizeResponseBody = modDefaultOrderSizeResponse.body().string(); + JsonElement jsonElement = JsonParser.parseString(modDefaultOrderSizeResponseBody); + 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 jsonObject; + } + + //If no error, the response is {"Success": f"Success. Default order size modified to $amount}"} + return jsonObject; + } + } + + public static String getAllLogs(String exchange, boolean retry) throws IOException { //200 lines hard limit Request logRequest = new Request.Builder() diff --git a/app/src/main/java/com/example/dcav2gui/WorkerInterface.java b/app/src/main/java/com/example/dcav2gui/WorkerInterface.java index feda846..f87730f 100644 --- a/app/src/main/java/com/example/dcav2gui/WorkerInterface.java +++ b/app/src/main/java/com/example/dcav2gui/WorkerInterface.java @@ -694,6 +694,50 @@ public class WorkerInterface { } + public static JsonObject modOrderSize(String exchange, String pair, double newOrderSize, boolean retry) throws IOException { + String[] pairBaseAndQuote = pair.split("/"); + String base = pairBaseAndQuote[0]; + String quote = pairBaseAndQuote[1]; + + Gson gson = new Gson(); + JsonObject jsonPayload = new JsonObject(); + jsonPayload.addProperty("base", base); + jsonPayload.addProperty("quote", quote); + jsonPayload.addProperty("amount", String.valueOf(newOrderSize)); + String jsonPayloadString = gson.toJson(jsonPayload); + + RequestBody requestBody = RequestBody.create(jsonPayloadString, MediaType.get("application/json; charset=utf-8")); + Request modOrderSizeRequest = new Request.Builder() + .url(API_BASE_URL + "/" + exchange + "/mod_order_size") + .header("X-API-KEY", API_KEY) + .post(requestBody) + .build(); + + try (Response modOrderSizeResponse = httpClient.newCall(modOrderSizeRequest).execute()) { + if (!modOrderSizeResponse.isSuccessful()) { + if (modOrderSizeResponse.code() == 503 && retry) { + return modOrderSize(exchange, pair, newOrderSize,false); + } + throw new IOException("Unexpected code " + modOrderSizeResponse); + } + String modOrderSizeResponseBody = modOrderSizeResponse.body().string(); + JsonElement jsonElement = JsonParser.parseString(modOrderSizeResponseBody); + 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 jsonObject; + } + + //If no error, the response is {"Success": "Success. The change will take effect when the next deal is started"} + return jsonObject; + } + } + + public static JsonObject addSafetyOrders(String exchange, String pair, int amount, boolean retry) throws IOException { String[] pairBaseAndQuote = pair.split("/"); String base = pairBaseAndQuote[0]; @@ -1113,6 +1157,42 @@ public class WorkerInterface { } + public static void sendModifyOrderSizeCall(String exchange, String pair, Context context) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle("Modify order size of "+ pair); + + final EditText input = new EditText(context); + input.setInputType(InputType.TYPE_CLASS_NUMBER); + input.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); + builder.setView(input); + + builder.setPositiveButton("Modify", (dialog, which) -> { + final double newOrderSize = Double.parseDouble(input.getText().toString()); + new Thread(() -> { + try { + JsonObject response = WorkerInterface.modOrderSize(exchange, pair, newOrderSize, true); + new Handler(Looper.getMainLooper()).post(() -> { + showToggleDialog(response, context); + }); + } catch (IOException e) { + e.printStackTrace(); + // Show an error dialog on the main thread + new Handler(Looper.getMainLooper()).post(() -> { + AlertDialog.Builder errorBuilder = new AlertDialog.Builder(context); + errorBuilder.setTitle("Error"); + errorBuilder.setMessage("Failed to modify order size: " + e.getMessage()); + errorBuilder.setPositiveButton("OK", null); + errorBuilder.show(); + }); + } + }).start(); + }); + + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); + builder.show(); + } + + public static void sendAddSafetyOrdersCall(String exchange, String pair, Context context) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Add safety orders to "+ pair); diff --git a/app/src/main/java/com/example/dcav2gui/ui/exchanges/BinanceFragment.java b/app/src/main/java/com/example/dcav2gui/ui/exchanges/BinanceFragment.java index ac60af3..c116d6f 100644 --- a/app/src/main/java/com/example/dcav2gui/ui/exchanges/BinanceFragment.java +++ b/app/src/main/java/com/example/dcav2gui/ui/exchanges/BinanceFragment.java @@ -103,6 +103,9 @@ public class BinanceFragment extends Fragment implements WorkerCardAdapter.OnCar } else if (item.getItemId() == R.id.modConcurrentSafetyOrders) { WorkerInterface.sendModifyConcurrentSafetyOrdersCall("binance", pair, getContext()); return true; + } else if (item.getItemId() == R.id.modOrderSize) { + WorkerInterface.sendModifyOrderSizeCall("binance", pair, getContext()); + return true; } else if (item.getItemId() == R.id.addSafetyOrders) { WorkerInterface.sendAddSafetyOrdersCall("binance", pair, getContext()); return true; diff --git a/app/src/main/java/com/example/dcav2gui/ui/exchanges/GateioFragment.java b/app/src/main/java/com/example/dcav2gui/ui/exchanges/GateioFragment.java index 9d992f7..6081a61 100644 --- a/app/src/main/java/com/example/dcav2gui/ui/exchanges/GateioFragment.java +++ b/app/src/main/java/com/example/dcav2gui/ui/exchanges/GateioFragment.java @@ -100,7 +100,10 @@ public class GateioFragment extends Fragment implements WorkerCardAdapter.OnCard WorkerInterface.sendSwitchQuoteCurrencyCall("gateio", pair, getContext()); return true; } else if (item.getItemId() == R.id.modConcurrentSafetyOrders) { - WorkerInterface.sendModifyConcurrentSafetyOrdersCall("binance", pair, getContext()); + WorkerInterface.sendModifyConcurrentSafetyOrdersCall("gateio", pair, getContext()); + return true; + } else if (item.getItemId() == R.id.modOrderSize) { + WorkerInterface.sendModifyOrderSizeCall("gateio", pair, getContext()); return true; } else if (item.getItemId() == R.id.addSafetyOrders) { WorkerInterface.sendAddSafetyOrdersCall("gateio", pair, getContext()); diff --git a/app/src/main/java/com/example/dcav2gui/ui/exchanges/KucoinFragment.java b/app/src/main/java/com/example/dcav2gui/ui/exchanges/KucoinFragment.java index b352335..66bf8c7 100644 --- a/app/src/main/java/com/example/dcav2gui/ui/exchanges/KucoinFragment.java +++ b/app/src/main/java/com/example/dcav2gui/ui/exchanges/KucoinFragment.java @@ -100,7 +100,10 @@ public class KucoinFragment extends Fragment implements WorkerCardAdapter.OnCard WorkerInterface.sendSwitchQuoteCurrencyCall("kucoin", pair, getContext()); return true; } else if (item.getItemId() == R.id.modConcurrentSafetyOrders) { - WorkerInterface.sendModifyConcurrentSafetyOrdersCall("binance", pair, getContext()); + WorkerInterface.sendModifyConcurrentSafetyOrdersCall("kucoin", pair, getContext()); + return true; + } else if (item.getItemId() == R.id.modOrderSize) { + WorkerInterface.sendModifyOrderSizeCall("kucoin", pair, getContext()); return true; } else if (item.getItemId() == R.id.addSafetyOrders) { WorkerInterface.sendAddSafetyOrdersCall("kucoin", pair, getContext()); diff --git a/app/src/main/java/com/example/dcav2gui/ui/exchanges/OkxFragment.java b/app/src/main/java/com/example/dcav2gui/ui/exchanges/OkxFragment.java index d1a5ccd..7f1ccb3 100644 --- a/app/src/main/java/com/example/dcav2gui/ui/exchanges/OkxFragment.java +++ b/app/src/main/java/com/example/dcav2gui/ui/exchanges/OkxFragment.java @@ -100,7 +100,10 @@ public class OkxFragment extends Fragment implements WorkerCardAdapter.OnCardLon WorkerInterface.sendSwitchQuoteCurrencyCall("okex", pair, getContext()); return true; } else if (item.getItemId() == R.id.modConcurrentSafetyOrders) { - WorkerInterface.sendModifyConcurrentSafetyOrdersCall("binance", pair, getContext()); + WorkerInterface.sendModifyConcurrentSafetyOrdersCall("okex", pair, getContext()); + return true; + } else if (item.getItemId() == R.id.modOrderSize) { + WorkerInterface.sendModifyOrderSizeCall("okex", pair, getContext()); return true; } else if (item.getItemId() == R.id.addSafetyOrders) { WorkerInterface.sendAddSafetyOrdersCall("okex", pair, getContext()); 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 5e8ff42..e2e9f1a 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,13 +1,16 @@ package com.example.dcav2gui.ui.home; import static com.example.dcav2gui.InstanceInterface.getLastNTrades; +import static com.example.dcav2gui.WorkerInterface.showToggleDialog; import android.annotation.SuppressLint; import android.app.AlertDialog; +import android.content.Context; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.text.InputType; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -16,6 +19,7 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupMenu; @@ -34,6 +38,7 @@ import com.example.dcav2gui.InstanceInterface; import com.example.dcav2gui.MainActivity; import com.example.dcav2gui.R; import com.example.dcav2gui.TickerTracker; +import com.example.dcav2gui.WorkerInterface; import com.example.dcav2gui.ui.exchanges.adapters.WorkerCardAdapter; import com.google.gson.JsonObject; @@ -348,6 +353,9 @@ public class HomeFragment extends Fragment { } else if (item.getItemId() == R.id.exchangeMenuConfigFile) { fetchExchangeConfig(exchange, true); return true; + } else if (item.getItemId() == R.id.exchangeModDefaultOrderSize) { + sendModifyDefaultOrderSizeCall(exchange, getContext()); + return true; } return false; } @@ -540,6 +548,43 @@ public class HomeFragment extends Fragment { return shortWorkers; } + + public static void sendModifyDefaultOrderSizeCall(String exchange, Context context) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle("Modify default order size of "+ exchange); + + final EditText input = new EditText(context); + input.setInputType(InputType.TYPE_CLASS_NUMBER); + input.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); + builder.setView(input); + + builder.setPositiveButton("Modify", (dialog, which) -> { + final double newOrderSize = Double.parseDouble(input.getText().toString()); + new Thread(() -> { + try { + JsonObject response = InstanceInterface.modDefaultOrderSize(exchange, newOrderSize, true); + new Handler(Looper.getMainLooper()).post(() -> { + showToggleDialog(response, context); + }); + } catch (IOException e) { + e.printStackTrace(); + // Show an error dialog on the main thread + new Handler(Looper.getMainLooper()).post(() -> { + AlertDialog.Builder errorBuilder = new AlertDialog.Builder(context); + errorBuilder.setTitle("Error"); + errorBuilder.setMessage("Failed to modify default order size: " + e.getMessage()); + errorBuilder.setPositiveButton("OK", null); + errorBuilder.show(); + }); + } + }).start(); + }); + + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); + builder.show(); + } + + private void showLastTradesDetailsDialog(List result) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); List shortWorkers = new ArrayList<>(); diff --git a/app/src/main/res/menu/exchange_popup_menu.xml b/app/src/main/res/menu/exchange_popup_menu.xml index d5fd3f7..0b0464a 100644 --- a/app/src/main/res/menu/exchange_popup_menu.xml +++ b/app/src/main/res/menu/exchange_popup_menu.xml @@ -12,4 +12,6 @@ android:title="View paused traders" /> + \ No newline at end of file diff --git a/app/src/main/res/menu/worker_popup_menu.xml b/app/src/main/res/menu/worker_popup_menu.xml index 80356c5..452aef3 100644 --- a/app/src/main/res/menu/worker_popup_menu.xml +++ b/app/src/main/res/menu/worker_popup_menu.xml @@ -42,6 +42,8 @@ + DCAv2GUI DCAv2 - Version 2025.09.05 + Version 2025.09.06 Open navigation drawer Close navigation drawer Navigation header