All Downloads are FREE. Search and download functionalities are using the official Maven repository.

quant.fans.tools.StockList Maven / Gradle / Ivy

The newest version!
package quant.fans.tools;

import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import quant.fans.common.ParallelProcesser;
import quant.fans.common.Utils;
import quant.fans.downloader.Downloader;
import quant.fans.model.StockData;
import quant.fans.provider.RealTimeDataProvider;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 非线程安全
 */
public class StockList {
    private static final Logger LOG = LoggerFactory.getLogger(StockList.class);

    private static String stockURL = "http://quote.eastmoney.com/stocklist.html";

    private static String stockDetailURL = "http://hqchart.eastmoney.com/hq20/js/%s.js?%s";

    private static String marginTradingURL = "http://app.finance.ifeng.com/hq/trade/rzrq_list.php?type=day";

    private static int RETRY_TIMES = 3;
    private static int SLEEP_INTERVAL_MS = 3000;

    private Map stockMap;
    private List stockDataList;
    private List blackList = Lists.newLinkedList();

    public static StockList create(){
        return new StockList();
    }

    public StockList(){
        init();
    }

    private StockList init() {
        if (stockMap == null) {
            stockMap = getStockName();
        }
        if (stockDataList == null){
            stockDataList = getStockDataList(Lists.newArrayList(stockMap.keySet()));
        }
        blackList = Lists.newLinkedList();
        return this;
    }

    /**
     * @param column column name
     * @param order  取值为asc或desc
     * @return stocklist
     */
    public StockList orderBy(String column, String order) {
        if ("asc".equals("order") || "desc".equals("order")) {
            Ordering ordering = new Ordering() {
                @Override
                public int compare(StockData sd1, StockData sd2) {
                    if (sd1.get(column) != null && sd2.get(column) != null) {
                        return sd1.get(column).compareTo(sd2.get(column));
                    } else {
                        return 0;
                    }
                }
            };

            List results = Lists.newLinkedList();
            if (order.equals("desc")) {
                results.addAll(ordering.reverse().sortedCopy(stockDataList));
            } else {
                results.addAll(ordering.sortedCopy(stockDataList));
            }
            stockDataList = results;
        }
        return this;
    }

    public StockList blacklist(String symbol){
        blackList.add(symbol);
        return this;
    }

    public StockList blacklist(List symbols){
        blackList.addAll(symbols);
        return this;
    }

    public StockList filter(List filters) {
        List stockDatas = Lists.newLinkedList();
        for(StockData stockData:stockDataList){
            if(filter(stockData,filters)){
                stockDatas.add(stockData);
            }
        }
        stockDataList = stockDatas;
        return this;
    }

    public StockList condition(Conditions conditions) {
        List stockDatas = Collections.synchronizedList(new LinkedList());
        for (StockData stockData : stockDataList) {
            if (conditions.check(stockData)) {
                stockDatas.add(stockData);
            }
        }
        stockDataList = stockDatas;
        return this;
    }

    public List get() {
        return stockDataList;
    }

    private boolean filter(StockData stockData,List filters){
        for(String filter:filters){
            if("st".equals(filter.toLowerCase().trim())){
                if(Strings.isNullOrEmpty(stockData.name)){
                    LOG.error(stockData.toString());
                    continue;
                }else if(stockData.name.toLowerCase().trim().contains("st")){
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 获取股票状态,分为三种情况:已退市,停牌中,交易中
     * @param symbol stock symbol
     * @throws IOException IOException
     * @return stock status
     */
    public static String getStockStatus(String symbol) throws IOException {
        Random random = new Random();
        String url = String.format(stockDetailURL, symbol, random.nextInt(999999));
        String content = Downloader.download(url);
        if (Strings.isNullOrEmpty(content)) {
            return "delisted"; //退市
        } else {
            Pattern pattern = Pattern.compile("data:\"(.*)\",update");
            Matcher matcher = pattern.matcher(content);
            if (matcher.find() && !matcher.group(1).contains("-")) {
                return "suspended";  //停牌
            }
        }
        return "trading";  //交易中
    }

    public static List getSymbols() {
        return Lists.newArrayList(getStockName().keySet());
    }


    /**
     * key:symbol
     * value:stock name
     */
    private static Map getStockName() {
        int zxb = 0;
        int sh = 0;
        int sz = 0;
        int cyb = 0;
        int other = 0;
        //key:code,val:name
        Map stockMap = Maps.newTreeMap();
        int retryTimes = 0;
        while (retryTimes < RETRY_TIMES) {
            try {
                Document doc = Jsoup.connect(stockURL).get();
                Elements stocks = doc.select("div[id=quotesearch] li a");
                for (Element stock : stocks) {
                    String url = stock.attr("href");
                    if (url.contains("sh6")) {
                        ++sh;
                    } else if (url.contains("sz000")) {
                        ++sz;
                    } else if (url.contains("sz002")) {
                        ++zxb;
                    } else if (url.contains("sz300")) {
                        ++cyb;
                    } else {
                        ++other;
                        continue;
                    }
                    String[] stockArr = stock.text().split("\\(");
                    //key:name,value:symbol
                    if (stockArr.length == 2) {
                        stockMap.put(stockArr[1].replaceAll("\\)", ""), stockArr[0]);
                    } else {
                        LOG.error("can't split:" + stock.text());
                    }
                }
                LOG.info("6:" + sh + ",000:" + sz + ",002:" + zxb + ",300:" + cyb + ",other:" + other);
                LOG.info("total:" + (sh + sz + zxb + cyb));
                return stockMap;
            } catch (IOException e) {
                LOG.error("fail to get stock list", e);
                retryTimes++;
                try {
                    Thread.sleep(SLEEP_INTERVAL_MS);
                } catch (InterruptedException e1) {
                    LOG.error("fail to sleep " + SLEEP_INTERVAL_MS + "ms");
                }
            }
        }
        return Maps.newLinkedHashMap();
    }

    /**
     * 获取中小板股票列表
     */
    private List getSMEStockList() {
        return getSMEStockList(getSymbols());
    }

    private List getSMEStockList(List stockList) {
        Collection list = Collections2.filter(stockList, (String input) -> input.startsWith("002"));
        return Lists.newArrayList(list);
    }

    /**
     * 获取创业板股票列表
     */
    private List getGEMStockList() {
        Collection list = Collections2.filter(getSymbols(), (String input) -> input.startsWith("300"));
        return Lists.newArrayList(list);
    }

    private List getSTStockList() {
        Map map = getStockName();
        List list = Lists.newArrayList();
        for (Map.Entry entry : map.entrySet()) {
            if (entry.getValue().toLowerCase().contains("st"))
                list.add(entry.getKey());
        }
        return list;
    }

    /**
     * 获取股票列表中“交易中”的股票,即未退市和停牌的股票列表
     * @return stock list
     */
    public static List getTradingStockList() {
        return getTradingStockList(getSymbols());
    }

    public static  List getTradingStockList(Conditions conditions) {
        return getStockListWithConditions(getTradingStockList(), conditions);
    }

    public static  List getStockListWithConditions(List stockList, Conditions conditions) {
        ExecutorService service = Executors.newFixedThreadPool(10);

        List tradingStockList = stockList;
        CountDownLatch countDownLatch = new CountDownLatch(tradingStockList.size());
        List stocks = Collections.synchronizedList(new LinkedList());
        for (String stock : tradingStockList) {
            service.execute(() -> {
                StockData stockData = RealTimeDataProvider.get(stock);
                if (conditions.check(stockData)) {
                    stocks.add(stock);
                }
                countDownLatch.countDown();
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Utils.closeThreadPool(service);
        return stocks;
    }

    /**
     * 按照代码获取股票最新状态
     * @param stockList stock list
     * @return stock list
     */
    private static List getStockDataList(List stockList) {
        final List stockDatas = Collections.synchronizedList(new LinkedList());
        CountDownLatch countDownLatch = new CountDownLatch(stockList.size());
        for (String stock : stockList) {
            ParallelProcesser.process(() -> {
                StockData stockData = RealTimeDataProvider.get(stock);
                if (stockData != null) {
                    stockDatas.add(stockData);
                }
                countDownLatch.countDown();
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return stockDatas;
    }

    /**
     * 融资融券股票列表
     * @return stock list
     */
    public static List getMarginTradingStockList() {
        List marginTradingStockList = Lists.newLinkedList();
        String data = Downloader.download(marginTradingURL);
        Elements element = Jsoup.parse(data).select("div[class=tab01]").get(0).getElementsByTag("table").get(0).getElementsByTag("tr");
        for (int i = 1; i < element.size(); i++) {
            String symbol = element.get(i).getElementsByTag("td").get(0).text();
            marginTradingStockList.add(symbol);
        }

        return marginTradingStockList;
    }

    private List getMarginTradingStockList(List list) {
        Set marginTradingSet = Sets.newHashSet(getMarginTradingStockList());
        Collection results = Collections2.filter(list, new Predicate() {
            @Override
            public boolean apply(String symbol) {
                return marginTradingSet.contains(symbol);
            }
        });
        return Lists.newArrayList(results);
    }

    /**
     * 获取指定股票列表中交易中的股票
     * @param list stock list
     * @return stock list
     */
    public static List getTradingStockList(List list) {
        final List stockList = Collections.synchronizedList(new LinkedList());
        ExecutorService threadPool = Executors.newFixedThreadPool(30);
        final CountDownLatch countDownLatch = new CountDownLatch(list.size());
        for (final String symbol : list) {
            threadPool.execute(() -> {
                try {
                    if (getStockStatus(symbol).equals("trading")) {
                        stockList.add(symbol);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Utils.closeThreadPool(threadPool);
        return stockList;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy