You are viewing a single comment's thread from:

RE: 바이낸스 자동매매 하는 프로그램

in #trading7 years ago

기능을 좀 보강했습니다.

  1. 프로퍼티로 변수들을 입력하고 자동으로 반영되도록 했습니다.
  2. 비트코인 외에도 다른 통화를 가지고 사고 팔 수 있도록 했습니다.
    휴~ 쉽지는 않네요...^^
package com.binance.trading;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.binance.api.client.BinanceApiClientFactory;
import com.binance.api.client.BinanceApiRestClient;
import com.binance.api.client.domain.account.Account;
import com.binance.api.client.domain.account.AssetBalance;
import com.binance.api.client.domain.account.NewOrder;
import com.binance.api.client.domain.account.Order;
import com.binance.api.client.domain.account.Trade;
import com.binance.api.client.domain.account.request.OrderRequest;
import com.binance.api.client.domain.general.ExchangeInfo;
import com.binance.api.client.domain.general.FilterType;
import com.binance.api.client.domain.general.SymbolFilter;
import com.binance.api.client.domain.general.SymbolInfo;
import com.binance.api.client.domain.market.TickerPrice;
import com.binance.api.client.exception.BinanceApiException;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.pengrad.telegrambot.TelegramBot;
import com.pengrad.telegrambot.request.SendMessage;

/**
 * Examples on how to get market data information such as the latest price of a
 * symbol, etc.
 */
public class MarketOrderTrading extends TimerTask {
    private final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    private final String ITEMS_FILE_NAME = "trading_items.json";
    private final String CONFIG_FILE_NAME = "tradingBinance.properties";
    private final long CONFIG_REFLESH_DELAY_MILLISECONDS = 60000;
    private final Type ITEMS_TYPE = new TypeToken<Map<String, TradingItems>>() {}.getType();
    
    private final String API_KEY = "API_KEY";
    private final String SECRET_KEY = "SECRET_KEY";
    private final String TELEGRAM_TOKEN = "TELEGRAM_TOKEN";
    private final String TELEGRAM_CHAT_ID = "TELEGRAM_CHAT_ID";
    
    private final PropertiesConfiguration config = new PropertiesConfiguration();
    private final FileChangedReloadingStrategy fileChangedReloadingStrategy =new FileChangedReloadingStrategy();

    private static String[] PAIR_SYMBOLS = null;
    private final Map<String, Double> CURRENCY_INVEST_PRICES = new HashMap<String, Double>();
    
    private double GAP_TRADING_PERCENT = 70.0;
    private boolean IS_RISK_TASKING = false;
    private double FEE_PERCENT = 0.05;
    private double TAKE_EXTRA_PERCENT = 2.0;
    private double MIN_START_PERCENT = 1.0;
    private static int TIME_PERIOD_MILLISECONDS = 500;
    private final Map<String, Double> MIN_ORDER_VALUES = new HashMap<String, Double>();
    private int STATUS_LOGGING_CYCLE = 600;

    private final BinanceApiClientFactory factory = BinanceApiClientFactory.newInstance(API_KEY, SECRET_KEY);
    private final BinanceApiRestClient client = factory.newRestClient();
    private final TelegramBot telegramBot = new TelegramBot(TELEGRAM_TOKEN);

    private Map<String, TradingItems> items = null;
    private long exeCount = 0;
    private FileWriter fw;

    public MarketOrderTrading() {
        loadConfig();
        initialSettings();
    }

    private void loadConfig() {
        config.setFileName(CONFIG_FILE_NAME);
        try {
            config.load();
            setConfig();
        } catch (ConfigurationException e) {
            sendLogging(e.getMessage());
            e.printStackTrace();
        }
        
        fileChangedReloadingStrategy.setRefreshDelay(CONFIG_REFLESH_DELAY_MILLISECONDS);
        config.setReloadingStrategy(fileChangedReloadingStrategy);
    }
    
    private void setConfig() {
        PAIR_SYMBOLS = config.getStringArray("PAIR_SYMBOLS");
        
        CURRENCY_INVEST_PRICES.put("BTC", config.getDouble("CURRENCY_INVEST_PRICES.BTC", 0.002));
        CURRENCY_INVEST_PRICES.put("ETH", config.getDouble("CURRENCY_INVEST_PRICES.ETH", 0.02));
        CURRENCY_INVEST_PRICES.put("USDT", config.getDouble("CURRENCY_INVEST_PRICES.USDT", 11.0));
        CURRENCY_INVEST_PRICES.put("BNB", config.getDouble("CURRENCY_INVEST_PRICES.BNB", 2.0));
        
        GAP_TRADING_PERCENT  = config.getDouble("GAP_TRADING_PERCENT", 70.0);
        IS_RISK_TASKING = config.getBoolean("IS_RISK_TASKING", false);
        FEE_PERCENT = config.getDouble("FEE_PERCENT", 0.05);
        TAKE_EXTRA_PERCENT = config.getDouble("TAKE_EXTRA_PERCENT", 0.05);
        MIN_START_PERCENT = config.getDouble("MIN_START_PERCENT", 1.0);
        TIME_PERIOD_MILLISECONDS = config.getInt("TIME_PERIOD_MILLISECONDS", 500);
        
        MIN_ORDER_VALUES.put("BTC", config.getDouble("MIN_ORDER_VALUES.BTC", 0.001));
        MIN_ORDER_VALUES.put("ETH", config.getDouble("MIN_ORDER_VALUES.ETH", 0.01));
        MIN_ORDER_VALUES.put("USDT", config.getDouble("MIN_ORDER_VALUES.USDT", 10.0));
        MIN_ORDER_VALUES.put("BNB", config.getDouble("MIN_ORDER_VALUES.BNB", 1.0));

        STATUS_LOGGING_CYCLE = config.getInt("STATUS_LOGGING_CYCLE");   
        
    }
    
    private void initialSettings() {
        if (items == null) {
            loadItems();
        }
        
        final Iterator<String> itemKeys = items.keySet().iterator();
        while (itemKeys.hasNext()) {
            final String key = itemKeys.next();
            if ( ArrayUtils.contains(PAIR_SYMBOLS, key) == false ) {
                itemKeys.remove();
            }
        }
        
        for (int i = 0; i < PAIR_SYMBOLS.length; i++) {
            final String pairSymbol = PAIR_SYMBOLS[i];
            if (items.containsKey(pairSymbol) == false) {
                initialBasicSetting(pairSymbol);
                initialSetting(items.get(pairSymbol));
            }
        }
    }
    
    private void initialBasicSetting(final String pairSymbol) {
        TradingItems item;
        if (items.containsKey(pairSymbol)) {
            item = this.items.get(pairSymbol);
        } else {
            item = new TradingItems();
            items.put(pairSymbol, item);
        }
        final String[] symbols = StringUtils.split(pairSymbol, '/');
        item.setTargetSymbol(symbols[0]);
        item.setCurrencySymbol(symbols[1]);
        item.setPairSymbol(symbols[0] + symbols[1]);
        getLotSize(item);
    }

    private void initialSetting(final TradingItems item) {
        final List<Order> openOrders = getOpenOrders(item.getPairSymbol());
        if (openOrders.size() > 0) {
            item.setWaiting(true);
            return;
        } else {
            item.setWaiting(false);
        }

        final TickerPrice nowTickerPrice = getPrice(item.getPairSymbol());
        final double nowPrice = Double.parseDouble(nowTickerPrice.getPrice());

        Trade trade = getLastTrade(item.getPairSymbol());
        double bagicPrice = 0.0;
        if (trade != null) {
            bagicPrice = Double.parseDouble(trade.getPrice());
        } else {
            bagicPrice = nowPrice;
        }

        item.setTopPrice(bagicPrice);
        item.setBottomPrice(bagicPrice);
        item.setLastOrderPrice(bagicPrice);

        sendLogging(item.getPairSymbol() +  ", Bagic Price : " + toString(bagicPrice), false);
    }

    private void getLotSize(final TradingItems item) {
        final ExchangeInfo exchangeInfo = client.getExchangeInfo();
        final SymbolInfo symbolInfo = exchangeInfo.getSymbolInfo(item.getPairSymbol());
        final SymbolFilter priceFilter = symbolInfo.getSymbolFilter(FilterType.LOT_SIZE);
        item.setMinQty(Double.parseDouble(priceFilter.getMinQty()));
        item.setStepSize(Double.parseDouble(priceFilter.getStepSize()));
    }

    private TickerPrice getPrice(final String pairSymbol) {
        return client.getPrice(pairSymbol);
    }

    private List<Order> getOpenOrders(final String pairSymbol) {
        return client.getOpenOrders(new OrderRequest(pairSymbol));
    }

    private Trade getLastTrade(final String pairSymbol) {
        Trade trade = null;
        List<Trade> trades = client.getMyTrades(pairSymbol, 1);
        if (trades.size() > 0) {
            trade = trades.get(0);
        }
        return trade;
    }

    private AssetBalance getAssetBalance(final String symbol) {
        final Account account = client.getAccount(6000000L, System.currentTimeMillis());
        return account.getAssetBalance(symbol);
    }

    private void watchTrading(final TradingItems item) {
        if (item.isWaiting()) {
            initialSetting(item);
            return;
        }
        final double lastOrderPrice = item.getLastOrderPrice();
        final TickerPrice nowTickerPrice = getPrice(item.getPairSymbol());
        final double nowPrice = Double.parseDouble(nowTickerPrice.getPrice());
        item.setNowPrice(nowPrice);
        
        if (nowPrice < lastOrderPrice) { // Buy
            item.setBuy(true);
            watchBuyTrading(item);
        } else { // Sell
            item.setBuy(false);
            watchSellTrading(item);
        }
    }

    private void watchBuyTrading(final TradingItems item) {
        final double bottomPrice = item.getBottomPrice();
        final double lastOrderPrice = item.getLastOrderPrice();
        final double nowPrice = item.getNowPrice();

        if (nowPrice < bottomPrice) {
            item.setBottomPrice(nowPrice);
        } else {

            if (this.IS_RISK_TASKING == false) {
                final double breakEvenPrice = lastOrderPrice
                        - nowPrice * (TAKE_EXTRA_PERCENT + FEE_PERCENT) / 100.0;
                if (nowPrice > breakEvenPrice) {
                    return;
                }
            }

            if (nowPrice - bottomPrice < nowPrice * (MIN_START_PERCENT / 100)) {
                return;
            }

            final double maxPrice = lastOrderPrice - (lastOrderPrice - bottomPrice) * (GAP_TRADING_PERCENT / 100.0);
            if (nowPrice < maxPrice) {
                return;
            }
            orderBuying(item, nowPrice);
            initialSetting(item);
        }
    }

    private void watchSellTrading(final TradingItems item) {
        final double topPrice = item.getTopPrice();
        final double lastOrderPrice = item.getLastOrderPrice();
        final double nowPrice = item.getNowPrice();
        
        if (nowPrice > topPrice) {
            item.setTopPrice(nowPrice);
        } else {

            if (this.IS_RISK_TASKING == false) {
                final double breakEvenPrice = lastOrderPrice
                        + nowPrice * (TAKE_EXTRA_PERCENT + FEE_PERCENT * 2) / 100.0;
                if (nowPrice < breakEvenPrice) {
                    return;
                }
            }

            if (topPrice - nowPrice < nowPrice * (MIN_START_PERCENT / 100)) {
                return;
            }

            final double minPrice = lastOrderPrice + (topPrice - lastOrderPrice) * (GAP_TRADING_PERCENT / 100.0);
            if (nowPrice > minPrice) {
                return;
            }
            orderSelling(item, nowPrice);
            initialSetting(item);
        }
    }

    private String getStatusLogging(final TradingItems item) {
        return item.getPairSymbol() + " -- " 
                        + (item.isBuy() ? "Buy" : "Sell") 
                        + ", N:" + toString(item.getNowPrice())
                        + ", T:" + toString(item.getTopPrice()) 
                        + ", B:"+ toString(item.getBottomPrice()) 
                        + ", L:" + toString(item.getLastOrderPrice());
    }
    
    private void sendStatusLogging() {
        if (exeCount % STATUS_LOGGING_CYCLE == 0) {
            final StringBuffer sb = new StringBuffer();
            for (int i = 0; i < PAIR_SYMBOLS.length; i++) {
                final String pairSymbol = PAIR_SYMBOLS[i];
                final TradingItems item = items.get(pairSymbol);
                sb.append(getStatusLogging(item)).append(System.lineSeparator());
            }
            sendLogging(sb.toString(), false);
        }
    }

    private void orderBuying(final TradingItems item, final double price) {
        final String currencySymbol = item.getCurrencySymbol();
        final double minOrderValue = MIN_ORDER_VALUES.get(currencySymbol);
        final AssetBalance currencyBalance = getAssetBalance(currencySymbol);
        final double freeQty = Double.parseDouble(currencyBalance.getFree());
        final double currencyInvestPrice = CURRENCY_INVEST_PRICES.get(item.getCurrencySymbol());

        String buyQty = null;
        if (freeQty < minOrderValue) {
            sendLogging(item.getPairSymbol() + ", BTC Free Qty : " + 
                    freeQty + " --- There is not enough currency.", false);
            return;
        } else if (freeQty < currencyInvestPrice) {
            buyQty = toOrderQty(item, freeQty / price);
        } else {
            buyQty = toOrderQty(item, currencyInvestPrice / price);
        }

        try {
            client.newOrder(NewOrder.marketBuy(item.getPairSymbol(), buyQty));
            sendLogging(item.getPairSymbol() + " -- Buy : " + toString(price) + ", Qty : " + buyQty);
        } catch (BinanceApiException e) {
            sendLogging(e.getMessage());
            e.printStackTrace();
        }
    }

    private void orderSelling(final TradingItems item, final double price) {
        final String currencySymbol = item.getCurrencySymbol();
        final String targetSymbol = item.getTargetSymbol();
        final double currencyInvestPrice = CURRENCY_INVEST_PRICES.get(currencySymbol);
        final double minOrderValue = MIN_ORDER_VALUES.get(currencySymbol);
        
        final AssetBalance balance = getAssetBalance(targetSymbol);
        final double freeQty = Double.parseDouble(balance.getFree());
        final double sellMaxQty =  currencyInvestPrice / price;
        final double sellMinQty =  minOrderValue / price;
        String sellQty = null;
        if (sellMinQty > freeQty) {
            sendLogging(item.getPairSymbol() + ", Free Qty : " + 
                        freeQty + " --- There is not enough target.", false);
            return;
        } else if (sellMaxQty > freeQty) {
            // BNB
            if (StringUtils.equals("BNB", targetSymbol) || StringUtils.equals("BNB", currencySymbol)) {
                sendLogging(item.getPairSymbol() + ", Free Qty : " + 
                        freeQty + " --- There is not enough target.", false);
                return;
            }
            sellQty = toOrderQty(item, freeQty);
        } else {
            sellQty = toOrderQty(item, sellMaxQty);
        }
        
        try {
            client.newOrder(NewOrder.marketSell(item.getPairSymbol(), sellQty));
            sendLogging(item.getPairSymbol() + " -- Sell : " + toString(price) + ", Qty : " + sellQty);
        } catch (BinanceApiException e) {
            sendLogging(e.getMessage());
            e.printStackTrace();
        }
    }

    private String toOrderQty(final TradingItems item, final double qty) {
        String strOrderQty = null;
        if (qty >= item.getMinQty()) {
            double orderQty = qty - qty % item.getStepSize();
            strOrderQty = toString(orderQty);
        }
        return strOrderQty;
    }

    private String toString(double num) {
        return String.format("%.9f", num);
    }

    private void sendLogging(final String message) {
        System.out.println(addDateString(message));
        sendToBot(message);
    }

    private void sendLogging(final String message, final boolean sendBot) {
        if (sendBot) {
            sendLogging(message);
        } else {
            System.out.println(addDateString(message));
        }
    }

    private String addDateString(final String message) {
        final Date nowDate = new Date();
        return message + ":: " + DATE_FORMAT.format(nowDate);
    }

    private void sendToBot(final String message) {
        try {
            final SendMessage request = new SendMessage(TELEGRAM_CHAT_ID, addDateString(message));
            telegramBot.execute(request);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private void saveItems() {
        final Gson gson = new Gson();
        final String jsonItems = gson.toJson(items);
        try {
            fw = new FileWriter(ITEMS_FILE_NAME);
            fw.write(jsonItems);
            fw.flush();
        } catch (IOException e) {
            sendLogging(e.getMessage());
            e.printStackTrace();
        }
    }
    
    private void loadItems() {
        JsonReader reader;
        Gson gson = new Gson();
        try {
            reader = new JsonReader(new FileReader(ITEMS_FILE_NAME));
            this.items = gson.fromJson(reader, ITEMS_TYPE);
        } catch (FileNotFoundException e) {
            items = new HashMap<String, TradingItems>();
            e.printStackTrace();
        }
    }
    
    @Override
    public void run() {
        if (fileChangedReloadingStrategy.reloadingRequired()) {
            setConfig();
            initialSettings();
        }
        
        for (int i = 0; i < PAIR_SYMBOLS.length; i++) {
            final String pairSymbol = PAIR_SYMBOLS[i];
            final TradingItems item = items.get(pairSymbol);
            watchTrading(item);
        }
        saveItems();
        sendStatusLogging();
        
        exeCount++;
    }

    public static void main(String[] args) {
        final TimerTask task = new MarketOrderTrading();
        final ScheduledExecutorService service =
        Executors.newSingleThreadScheduledExecutor();
        service.scheduleWithFixedDelay(task, TIME_PERIOD_MILLISECONDS, 
                TIME_PERIOD_MILLISECONDS, TimeUnit.MILLISECONDS);
    }

}