positoy / pe

0 stars 0 forks source link

암호화폐 차익 안드로이드 앱 #2

Open positoy opened 2 months ago

positoy commented 2 months ago
positoy commented 2 months ago

한국과 해외 거래소의 이더리움 시세차익을 이용해서 수익을 만드는 안드로이드 애플리케이션을 만들고 싶어. 서비스에서는 지속적으로 빗썸과 바이낸스의 이더리움 시세를 확인하다가, 모니터링을 시작하는 차액 A가 발생하면 알림을 보내서 상황을 공유해주고 매수를 시작하는 차액 B가 발생하면 한 거래소에서 매수 후 다른 거래소에서 매도하여 차익을 만들어줘.

위와 같은 애플리케이션을 만들기 위해 의사결정이 필요한 부분이 있다면 나에게 확인하고 진행해줘.

이더리움 시세차익을 이용한 안드로이드 애플리케이션을 개발하기 위해서는 여러 가지 의사결정이 필요합니다. 다음은 주요 의사결정 포인트와 고려해야 할 사항들입니다:

1. API 선택

2. 알림 시스템

3. 매수/매도 조건

4. 애플리케이션 구조

5. 보안

6. 테스트 및 배포

구체적인 결정 및 진행 방식

다음은 각 단계별 구체적인 결정 및 진행 방식을 나열한 것입니다:

API 선택 및 시세 모니터링

알림 시스템 구현

매수/매도 조건 설정

애플리케이션 개발

보안 및 테스트

배포

진행 중 확인할 사항

개발 중간에 발생하는 문제나 결정해야 할 사항이 있으면 언제든지 확인 요청을 해주세요. 예를 들어, 특정 기능 구현 방법이나 디자인 관련 질문 등이 있을 수 있습니다.

이런 절차를 통해 안드로이드 애플리케이션을 개발할 수 있습니다. 필요한 추가 정보나 세부 사항이 있다면 말씀해 주세요.

positoy commented 2 months ago

위에서 제안한 내용 모두 좋아. 다만 애플리케이션은 안드로이드 앱으로 작성할거야.

  1. 서비스: 10초마다 가격 확인
  2. 알림 : 매수/매도 조건이 발생했을 때 안드로이드 앱 알림 발생
  3. 앱 액티비티는 매수/매도 이력을 보여주는 리스트 화면

코드 작성해줘

안드로이드 애플리케이션을 개발하기 위한 전체적인 구조와 필요한 코드를 제공하겠습니다. 주요 부분은 다음과 같습니다:

  1. 시세 확인 서비스: 10초마다 가격을 확인하는 서비스
  2. 알림 시스템: 매수/매도 조건이 발생했을 때 알림 발생
  3. 매수/매도 이력 화면: 매수/매도 이력을 보여주는 리스트 화면

1. 시세 확인 서비스

PriceCheckService.java 파일을 생성하여 시세 확인 작업을 수행합니다.

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import androidx.annotation.Nullable;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;

public class PriceCheckService extends Service {
    private final Handler handler = new Handler();
    private final OkHttpClient client = new OkHttpClient();
    private final int interval = 10000; // 10 seconds

    private final Runnable priceCheckRunnable = new Runnable() {
        @Override
        public void run() {
            checkPrices();
            handler.postDelayed(this, interval);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handler.post(priceCheckRunnable);
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(priceCheckRunnable);
    }

    private void checkPrices() {
        // 빗썸 API 호출
        Request requestBithumb = new Request.Builder()
                .url("https://api.bithumb.com/public/ticker/ETH")
                .build();
        client.newCall(requestBithumb).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 실패 처리
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String bithumbPrice = response.body().string();
                    // 바이낸스 API 호출
                    Request requestBinance = new Request.Builder()
                            .url("https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT")
                            .build();
                    client.newCall(requestBinance).enqueue(new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {
                            // 실패 처리
                        }

                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            if (response.isSuccessful()) {
                                String binancePrice = response.body().string();
                                // 여기서 bithumbPrice와 binancePrice를 비교하고 조건을 체크합니다.
                                checkForArbitrage(bithumbPrice, binancePrice);
                            }
                        }
                    });
                }
            }
        });
    }

    private void checkForArbitrage(String bithumbPrice, String binancePrice) {
        // 시세 차익 계산 및 조건 확인 로직을 작성합니다.
        // 조건을 만족할 경우, 알림을 보냅니다.
        double bithumb = Double.parseDouble(bithumbPrice); // 실제로는 JSON 파싱 필요
        double binance = Double.parseDouble(binancePrice); // 실제로는 JSON 파싱 필요

        double difference = Math.abs(bithumb - binance);
        if (difference >= YOUR_ALERT_THRESHOLD) {
            sendNotification("Arbitrage opportunity detected");
        }
    }

    private void sendNotification(String message) {
        // 알림을 생성합니다.
        // NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        //        .setSmallIcon(R.drawable.notification_icon)
        //        .setContentTitle("Price Alert")
        //        .setContentText(message)
        //        .setPriority(NotificationCompat.PRIORITY_HIGH);
        // NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
        // notificationManager.notify(NOTIFICATION_ID, builder.build());
    }
}

2. 알림 시스템

위의 sendNotification 메서드에서 알림을 발생시키는 코드를 작성합니다. NotificationManagerCompat을 사용하여 알림을 보내도록 합니다.

3. 매수/매도 이력 화면

MainActivity.javahistory_item.xml 파일을 생성하여 이력을 보여주는 리스트 화면을 구현합니다.

MainActivity.java

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private HistoryAdapter adapter;
    private List<TradeHistory> tradeHistoryList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        tradeHistoryList = new ArrayList<>();
        adapter = new HistoryAdapter(tradeHistoryList);
        recyclerView.setAdapter(adapter);

        // 예시 데이터 추가
        tradeHistoryList.add(new TradeHistory("Buy", "ETH", "Bithumb", "Binance", 0.02));
        adapter.notifyDataSetChanged();
    }
}

HistoryAdapter.java

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.widget.TextView;
import java.util.List;

public class HistoryAdapter extends RecyclerView.Adapter<HistoryAdapter.ViewHolder> {
    private List<TradeHistory> tradeHistoryList;

    public HistoryAdapter(List<TradeHistory> tradeHistoryList) {
        this.tradeHistoryList = tradeHistoryList;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.history_item, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        TradeHistory tradeHistory = tradeHistoryList.get(position);
        holder.typeTextView.setText(tradeHistory.getType());
        holder.coinTextView.setText(tradeHistory.getCoin());
        holder.fromTextView.setText(tradeHistory.getFromExchange());
        holder.toTextView.setText(tradeHistory.getToExchange());
        holder.amountTextView.setText(String.valueOf(tradeHistory.getAmount()));
    }

    @Override
    public int getItemCount() {
        return tradeHistoryList.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView typeTextView, coinTextView, fromTextView, toTextView, amountTextView;

        ViewHolder(View itemView) {
            super(itemView);
            typeTextView = itemView.findViewById(R.id.typeTextView);
            coinTextView = itemView.findViewById(R.id.coinTextView);
            fromTextView = itemView.findViewById(R.id.fromTextView);
            toTextView = itemView.findViewById(R.id.toTextView);
            amountTextView = itemView.findViewById(R.id.amountTextView);
        }
    }
}

TradeHistory.java

public class TradeHistory {
    private String type;
    private String coin;
    private String fromExchange;
    private String toExchange;
    private double amount;

    public TradeHistory(String type, String coin, String fromExchange, String toExchange, double amount) {
        this.type = type;
        this.coin = coin;
        this.fromExchange = fromExchange;
        this.toExchange = toExchange;
        this.amount = amount;
    }

    public String getType() {
        return type;
    }

    public String getCoin() {
        return coin;
    }

    public String getFromExchange() {
        return fromExchange;
    }

    public String getToExchange() {
        return toExchange;
    }

    public double getAmount() {
        return amount;
    }
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp"/>
</RelativeLayout>

history_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/typeTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Type"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/coinTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Coin"/>

    <TextView
        android:id="@+

id/fromTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="From"/>

    <TextView
        android:id="@+id/toTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="To"/>

    <TextView
        android:id="@+id/amountTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Amount"/>

</LinearLayout>

이 코드를 기반으로 안드로이드 애플리케이션을 개발할 수 있습니다. 추가적인 기능이나 개선이 필요하다면 언제든지 말씀해 주세요.

positoy commented 2 months ago

변동성을 계산하는 EthereumVolatility 클래스와 매수,전송,매도를 진행하는 클래스를 분리해줘. EthereumVolatility 클래스에서 변동성을 구하는 메소드는 get 이라는 단일한 메소드로 통일해주고 파라미터로 변동성을 계산하는 방법을 넘길 수 있게 구현해줘.

EthereumVolatility 클래스에서 변동성을 계산하는 메서드를 단일 get 메서드로 통일하고, 매수, 전송, 매도를 진행하는 로직을 별도의 클래스로 분리하겠습니다. 이를 통해 코드의 구조를 더 명확하게 할 수 있습니다.

1. EthereumVolatility 클래스

이 클래스는 변동성을 계산하는 메서드들을 포함하며, 단일 get 메서드를 통해 변동성을 계산하는 방법을 파라미터로 받을 수 있도록 합니다.

EthereumVolatility.java

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

public class EthereumVolatility {
    private OkHttpClient client;
    private String binancePriceUrl;
    private Queue<Double> prices;
    private int windowSize;
    private double prevClose;
    private DescriptiveStatistics stats;

    public EthereumVolatility(int windowSize) {
        this.client = new OkHttpClient();
        this.binancePriceUrl = "https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT";
        this.prices = new LinkedList<>();
        this.windowSize = windowSize;
        this.stats = new DescriptiveStatistics(windowSize);
    }

    public double getCurrentPrice() throws IOException {
        Request request = new Request.Builder().url(binancePriceUrl).build();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                String json = response.body().string();
                String priceString = json.split(":")[1].replace("}", "").replace("\"", "");
                return Double.parseDouble(priceString);
            } else {
                throw new IOException("Failed to fetch price from API");
            }
        }
    }

    public void updatePrice(double price) {
        if (prices.size() == windowSize) {
            prices.poll();
        }
        prices.add(price);
        stats.addValue(price);
    }

    public double get(String method) {
        switch (method) {
            case "MA":
                return calculateMovingAverage();
            case "STD":
                return calculateStandardDeviation();
            case "BOLLINGER_UPPER":
                return calculateBollingerBands()[0];
            case "BOLLINGER_LOWER":
                return calculateBollingerBands()[1];
            case "EMA":
                return calculateEMA();
            case "ATR":
                return calculateATR();
            default:
                throw new IllegalArgumentException("Unknown method: " + method);
        }
    }

    private double calculateMovingAverage() {
        return stats.getMean();
    }

    private double calculateStandardDeviation() {
        return stats.getStandardDeviation();
    }

    private double[] calculateBollingerBands() {
        double ma = calculateMovingAverage();
        double std = calculateStandardDeviation();
        return new double[]{ma + 2 * std, ma - 2 * std};
    }

    private double calculateEMA() {
        double alpha = 2.0 / (windowSize + 1);
        double ema = prices.peek(); // 초기값 설정
        for (double price : prices) {
            ema = alpha * price + (1 - alpha) * ema;
        }
        return ema;
    }

    private double calculateATR() {
        double sum = 0.0;
        int count = 0;
        for (double price : prices) {
            sum += Math.abs(price - prevClose);
            prevClose = price;
            count++;
        }
        return sum / count;
    }
}

2. TradeExecutor 클래스

이 클래스는 매수, 전송, 매도 로직을 포함합니다.

TradeExecutor.java

public class TradeExecutor {
    public void executeTrade(String action, double price, String exchange) {
        // 거래 API를 호출하여 매수 또는 매도 요청을 보냅니다.
        // 여기에 실제 거래소 API 호출 코드를 추가합니다.
        System.out.println(action + "ing on " + exchange + " at price: " + price);
    }

    public void executeTransfer(String fromExchange, String toExchange) {
        // 송금 API를 호출하여 한 거래소에서 다른 거래소로 전송합니다.
        // 여기에 실제 송금 API 호출 코드를 추가합니다.
        System.out.println("Transferring from " + fromExchange + " to " + toExchange);
    }
}

3. Main 클래스

이 클래스는 변동성을 평가하고, 거래를 수행하는 메인 클래스입니다.

Main.java

import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        int windowSize = 10;
        double alertThreshold = 10.0; // 알림 기준 차액
        double transactionThreshold = 20.0; // 매수/매도 기준 차액
        EthereumVolatility ev = new EthereumVolatility(windowSize);
        TradeExecutor executor = new TradeExecutor();

        while (true) {
            try {
                double binancePrice = ev.getCurrentPrice();
                double bithumbPrice = ev.getCurrentPrice(); // 이 부분은 실제 Bithumb API를 사용해야 함

                ev.updatePrice(binancePrice);

                double ma = ev.get("MA");
                double std = ev.get("STD");
                double[] bollingerBands = {ev.get("BOLLINGER_UPPER"), ev.get("BOLLINGER_LOWER")};
                double ema = ev.get("EMA");
                double atr = ev.get("ATR");

                System.out.println("Binance Price: " + binancePrice);
                System.out.println("Bithumb Price: " + bithumbPrice);
                System.out.println("Moving Average: " + ma);
                System.out.println("Standard Deviation: " + std);
                System.out.println("Bollinger Bands: Upper - " + bollingerBands[0] + ", Lower - " + bollingerBands[1]);
                System.out.println("EMA: " + ema);
                System.out.println("ATR: " + atr);
                System.out.println("=".repeat(50));

                double priceDifference = bithumbPrice - binancePrice;
                if (Math.abs(priceDifference) >= alertThreshold) {
                    System.out.println("Alert: Significant price difference detected!");
                }

                if (Math.abs(priceDifference) >= transactionThreshold) {
                    if (priceDifference > 0) {
                        System.out.println("Executing Arbitrage: Buying on Binance, Selling on Bithumb");
                        executor.executeTrade("buy", binancePrice, "Binance");
                        executor.executeTransfer("Binance", "Bithumb");
                        executor.executeTrade("sell", bithumbPrice, "Bithumb");
                    } else {
                        System.out.println("Executing Arbitrage: Buying on Bithumb, Selling on Binance");
                        executor.executeTrade("buy", bithumbPrice, "Bithumb");
                        executor.executeTransfer("Bithumb", "Binance");
                        executor.executeTrade("sell", binancePrice, "Binance");
                    }
                }

                Thread.sleep(10000); // 10초 간격
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

설명

  1. EthereumVolatility 클래스: 변동성을 계산하는 메서드를 포함하며, 단일 get 메서드를 통해 변동성 계산 방법을 파라미터로 받아서 처리합니다.
  2. TradeExecutor 클래스: 매수, 전송, 매도 로직을 처리합니다.
  3. Main 클래스: 변동성을 평가하고 매수/전송/매도 로직을 실행하는 메인 루프를 포함합니다.

이 구조는 코드의 가독성을 높이고 유지보수를 용이하게 합니다. 각 클래스는 자신의 책임을 명확히 가지고 있으며, 이를 통해 코드의 모듈화를 달성할 수 있습니다.

positoy commented 2 months ago

checkPrice 라는 메소드를 가진 거래소 인터페이스를 만들어줘. 이 메소드의 파라미터로는 가상화폐 자산의 티커를 enum 타입으로 전달할 수 있어. 이 인터페이스를 구현해서 빗썸 클래스를 만들어줘.

거래소 인터페이스와 이를 구현하는 Bithumb 클래스를 설계하겠습니다. 가상화폐 자산의 티커를 전달할 수 있는 enum 타입도 정의하겠습니다.

1. Ticker Enum

가상화폐 자산의 티커를 정의하는 enum 타입입니다.

Ticker.java

public enum Ticker {
    BTC, ETH, XRP, LTC, BCH, // 필요에 따라 다른 티커를 추가할 수 있습니다.
    // 예: ETH, XRP, BCH 등
}

2. Exchange Interface

거래소 인터페이스를 정의하고, checkPrice 메서드를 포함합니다.

Exchange.java

public interface Exchange {
    double checkPrice(Ticker ticker) throws IOException;
}

3. Bithumb 클래스

거래소 인터페이스를 구현하는 Bithumb 클래스를 정의합니다.

Bithumb.java

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;

public class Bithumb implements Exchange {
    private OkHttpClient client;
    private String baseUrl;

    public Bithumb() {
        this.client = new OkHttpClient();
        this.baseUrl = "https://api.bithumb.com/public/ticker/";
    }

    @Override
    public double checkPrice(Ticker ticker) throws IOException {
        String url = baseUrl + ticker.name();
        Request request = new Request.Builder().url(url).build();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                String json = response.body().string();
                // JSON 파싱 로직을 추가해야 합니다. 여기서는 간단히 가정합니다.
                String priceString = json.split(":")[1].replace("}", "").replace("\"", "");
                return Double.parseDouble(priceString);
            } else {
                throw new IOException("Failed to fetch price from Bithumb API");
            }
        }
    }
}

4. Main 클래스에서 테스트

Bithumb 클래스를 사용하여 특정 티커의 가격을 확인하는 예제입니다.

Main.java

public class Main {
    public static void main(String[] args) {
        Exchange bithumb = new Bithumb();

        try {
            double ethPrice = bithumb.checkPrice(Ticker.ETH);
            System.out.println("Current ETH price on Bithumb: " + ethPrice);

            double btcPrice = bithumb.checkPrice(Ticker.BTC);
            System.out.println("Current BTC price on Bithumb: " + btcPrice);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

설명

  1. Ticker Enum: 가상화폐 자산의 티커를 정의합니다.
  2. Exchange Interface: 거래소 인터페이스를 정의하고, checkPrice 메서드를 포함합니다.
  3. Bithumb Class: Exchange 인터페이스를 구현하여 Bithumb 거래소의 가격 확인 메서드를 제공합니다.
  4. Main Class: Bithumb 클래스를 사용하여 특정 티커의 가격을 확인하는 예제입니다.

이 구조는 거래소 인터페이스를 통해 다양한 거래소 클래스를 쉽게 확장할 수 있게 합니다. 다른 거래소를 추가하려면 Exchange 인터페이스를 구현하는 새로운 클래스를 작성하면 됩니다.

positoy commented 2 months ago

다음은 빗썸에서 이더리움 가격을 조회했을 때 응답내용이야. 이 응답을 담을 수 있는 도메인 클래스 설계해줘. 중첩된 자료 구조는 inner class 로 선언해서 단일한 클래스로 만들어줘. 롬복의 @Data 를 적용해줘.

{"status":"0000","data":{"opening_price":"4277000","closing_price":"5023000","min_price":"4269000","max_price":"5045000","units_traded":"14886.085980478429923382","acc_trade_value":"71585331229.44479265547145","prev_closing_price":"4278000","units_traded_24H":"19303.315175256399738839","acc_trade_value_24H":"90486373086.988702542480578","fluctate_24H":"763000","fluctate_rate_24H":"17.91","date":"1716250955041"}}

중첩된 자료 구조를 inner class로 선언하고 Lombok의 @Data 어노테이션을 적용하여 단일한 클래스로 만드는 예제를 제공하겠습니다.

도메인 클래스 설계

BithumbResponse.java

import lombok.Data;

@Data
public class BithumbResponse {
    private String status;
    private Data data;

    @Data
    public static class Data {
        private String opening_price;
        private String closing_price;
        private String min_price;
        private String max_price;
        private String units_traded;
        private String acc_trade_value;
        private String prev_closing_price;
        private String units_traded_24H;
        private String acc_trade_value_24H;
        private String fluctate_24H;
        private String fluctate_rate_24H;
        private String date;
    }
}

JSON 파싱 예제

Jackson 라이브러리를 사용하여 JSON 응답을 도메인 객체로 파싱하는 예제입니다.

Main.java

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        String jsonResponse = "{\"status\":\"0000\",\"data\":{\"opening_price\":\"4277000\",\"closing_price\":\"5023000\",\"min_price\":\"4269000\",\"max_price\":\"5045000\",\"units_traded\":\"14886.085980478429923382\",\"acc_trade_value\":\"71585331229.44479265547145\",\"prev_closing_price\":\"4278000\",\"units_traded_24H\":\"19303.315175256399738839\",\"acc_trade_value_24H\":\"90486373086.988702542480578\",\"fluctate_24H\":\"763000\",\"fluctate_rate_24H\":\"17.91\",\"date\":\"1716250955041\"}}";

        ObjectMapper mapper = new ObjectMapper();
        try {
            BithumbResponse response = mapper.readValue(jsonResponse, BithumbResponse.class);
            System.out.println(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

설명

  1. BithumbResponse 클래스: 최상위 구조를 담고 있으며, statusdata 필드를 가집니다.
  2. Data Inner Class: data 필드의 세부 정보를 담고 있습니다. BithumbResponse 클래스 내부에 static inner class로 선언됩니다.
  3. 롬복(@Data): @Data 어노테이션을 사용하여 getter, setter, toString, equals, hashCode 메서드를 자동으로 생성합니다.
  4. Main 클래스: Jackson 라이브러리를 사용하여 JSON 응답을 BithumbResponse 객체로 파싱하는 예제입니다.

이 설계를 통해 빗썸 API의 응답을 손쉽게 파싱하고 사용할 수 있습니다. Inner class를 사용하여 중첩된 자료 구조를 표현하고, Lombok을 사용하여 코드를 간결하게 유지할 수 있습니다.

positoy commented 2 months ago

이제 위의 Bithumb 클래스가 요청 결과를 BithumbResponse 에 담을 수 있도록 수정해줘

Bithumb 클래스가 요청 결과를 BithumbResponse 객체에 담을 수 있도록 수정하겠습니다. 이를 위해 Jackson 라이브러리를 사용하여 JSON 응답을 파싱하도록 하겠습니다.

수정된 Bithumb 클래스

Bithumb.java

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class Bithumb implements Exchange {
    private OkHttpClient client;
    private String baseUrl;
    private ObjectMapper objectMapper;

    public Bithumb() {
        this.client = new OkHttpClient();
        this.baseUrl = "https://api.bithumb.com/public/ticker/";
        this.objectMapper = new ObjectMapper();
    }

    @Override
    public double checkPrice(Ticker ticker) throws IOException {
        String url = baseUrl + ticker.name();
        Request request = new Request.Builder().url(url).build();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                String json = response.body().string();
                BithumbResponse bithumbResponse = objectMapper.readValue(json, BithumbResponse.class);
                return Double.parseDouble(bithumbResponse.getData().getClosing_price());
            } else {
                throw new IOException("Failed to fetch price from Bithumb API");
            }
        }
    }

    public static void main(String[] args) {
        Bithumb bithumb = new Bithumb();
        try {
            double ethPrice = bithumb.checkPrice(Ticker.ETH);
            System.out.println("Current ETH price on Bithumb: " + ethPrice);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

도메인 클래스

BithumbResponse.java

import lombok.Data;

@Data
public class BithumbResponse {
    private String status;
    private Data data;

    @Data
    public static class Data {
        private String opening_price;
        private String closing_price;
        private String min_price;
        private String max_price;
        private String units_traded;
        private String acc_trade_value;
        private String prev_closing_price;
        private String units_traded_24H;
        private String acc_trade_value_24H;
        private String fluctate_24H;
        private String fluctate_rate_24H;
        private String date;
    }
}

설명

  1. Bithumb 클래스: checkPrice 메서드가 이제 JSON 응답을 BithumbResponse 객체로 파싱합니다. Jackson 라이브러리를 사용하여 JSON 응답을 도메인 객체로 변환합니다.
  2. BithumbResponse 클래스: 중첩된 자료 구조를 표현하기 위해 inner class를 사용하고, Lombok의 @Data 어노테이션을 적용하여 getter, setter, toString, equals, hashCode 메서드를 자동으로 생성합니다.
  3. main 메서드: Bithumb 클래스를 테스트하기 위한 간단한 메서드로, ETH 가격을 조회합니다.

이 수정된 코드는 빗썸 API의 응답을 BithumbResponse 객체에 담고, 필요한 정보를 쉽게 추출할 수 있도록 합니다.

positoy commented 2 months ago

위 PriceCheckService 가 Bithumb, Binance 클래스를 활용하도록 개선해줘

PriceCheckService를 Bithumb 및 Binance 클래스를 활용하도록 개선하겠습니다. 이 서비스는 두 거래소의 이더리움 가격을 비교하여, 특정 조건이 충족될 때 알림을 보내고 거래를 수행하는 역할을 합니다.

개선된 PriceCheckService 클래스

PriceCheckService.java

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Queue;
import java.util.LinkedList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class PriceCheckService {
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private final Bithumb bithumb;
    private final Binance binance;
    private final int interval;
    private final TradeExecutor executor;
    private final double alertThreshold;
    private final double transactionThreshold;

    public PriceCheckService(int interval, double alertThreshold, double transactionThreshold) {
        this.bithumb = new Bithumb();
        this.binance = new Binance();
        this.interval = interval;
        this.executor = new TradeExecutor();
        this.alertThreshold = alertThreshold;
        this.transactionThreshold = transactionThreshold;
    }

    public void start() {
        Runnable task = () -> {
            try {
                double bithumbPrice = bithumb.checkPrice(Ticker.ETH);
                double binancePrice = binance.checkPrice(Ticker.ETH);

                System.out.println("Bithumb ETH Price: " + bithumbPrice);
                System.out.println("Binance ETH Price: " + binancePrice);

                double priceDifference = bithumbPrice - binancePrice;
                if (Math.abs(priceDifference) >= alertThreshold) {
                    System.out.println("Alert: Significant price difference detected!");
                }

                if (Math.abs(priceDifference) >= transactionThreshold) {
                    if (priceDifference > 0) {
                        System.out.println("Executing Arbitrage: Buying on Binance, Selling on Bithumb");
                        executor.executeTrade("buy", binancePrice, "Binance");
                        executor.executeTransfer("Binance", "Bithumb");
                        executor.executeTrade("sell", bithumbPrice, "Bithumb");
                    } else {
                        System.out.println("Executing Arbitrage: Buying on Bithumb, Selling on Binance");
                        executor.executeTrade("buy", bithumbPrice, "Bithumb");
                        executor.executeTransfer("Bithumb", "Binance");
                        executor.executeTrade("sell", binancePrice, "Binance");
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        };

        scheduler.scheduleAtFixedRate(task, 0, interval, TimeUnit.SECONDS);
    }

    public static void main(String[] args) {
        int interval = 10; // 10 seconds interval
        double alertThreshold = 10.0; // 알림 기준 차액
        double transactionThreshold = 20.0; // 매수/매도 기준 차액

        PriceCheckService service = new PriceCheckService(interval, alertThreshold, transactionThreshold);
        service.start();
    }
}

설명

  1. PriceCheckService 클래스:

    • Bithumb와 Binance 클래스를 사용하여 두 거래소의 이더리움 가격을 비교합니다.
    • 주기적으로 가격을 확인하고 특정 조건이 충족되면 알림을 보내고 거래를 수행합니다.
  2. start 메서드:

    • ScheduledExecutorService를 사용하여 주기적으로 가격을 확인합니다.
    • bithumb.checkPrice(Ticker.ETH)binance.checkPrice(Ticker.ETH)를 호출하여 가격을 가져옵니다.
    • 가격 차이를 계산하고, 알림 및 거래 조건을 확인하여 필요한 작업을 수행합니다.
  3. main 메서드:

    • PriceCheckService를 초기화하고 시작합니다.

이제 PriceCheckService가 Bithumb 및 Binance 클래스를 활용하여 실시간으로 이더리움 가격을 비교하고, 특정 조건이 충족될 때 알림을 보내고 거래를 수행할 수 있습니다.