涉及多維度的業務如何落地實現

最近公司的產品系統要重構,但是時間又比較緊迫,所以底層的結構如mongo mq的連接操作就直接用之前的jar包了,目前系統採用的maven多module模塊模式,最可恥的是webframework是用的play,而且大家也覺得替換spring在短時間內不好實現。所以只好去把paly服務作爲module集成到maven中了,這塊任務交到我這裏那就做吧,相關資料少之又少,通過git源碼及聯繫原作者,終於用maven play plugin的方式將play集成到maven中去了。下一步我的開發任務便是業務指標的梳理,整體上包括三部分:取數據的dateOperation層、algorithm層及指標計算層:即指標計算層調用其它兩層從而計算出指標。
dataOperation層: 1)簡單的mongo的crud操作,寫成工具類的形式;2)策略模式實現不同的取數及計算規則,返回指標計算想要的中間值。
algor層:僅僅的指標計算的算法,其實就是公式了。沒有多餘的取數及邏輯
index計算層:在指標計算過程中,用不同的策略計算中間值,然後調用相關算法來計算指標。

以上三層均定義接口及抽象類,java的多態還是蠻有用的。

然後最近在梳理業務,因爲有些指標計算不僅是簡單的入參然後輸出一個指標。譬如:
基金公司的某組合id 一段時間範圍內所有的持倉數據
1)根據持倉計算個券的指標
2)將所有的持倉根據type(股票、期貨、債券等等)分類,然後計算每一層的指標(sum(權重*個券) )。
3)股票層還要根據行業分類標準分成不同的行業層,每一層計算指標(sum(權重*個券))

呵呵,然後用了二天半時間,不斷地推翻重建,終於形成了一套初步的方式。這裏分享下,源碼如下:


            clientParam.setCollection("bond_position");
            Set<String> keySet = new HashSet<>();
            keySet.add("combination_id");
            keySet.add("date");
            keySet.add("type");
            keySet.add("code");
            keySet.add("name");
            keySet.add("value");
            keySet.add("hq_close_price");
            clientParam.setKey(keySet);
            List<Map<String, Object>> totalBondList = (List<Map<String, Object>>) context.handle(params, clientParam);

            //預處理 dimension的industryNames 及 map<code, level1_name>
            clientParam.clearQueryParameters();
            clientParam.setCollection("stock_category");
            clientParam.setParam("industry_name_cd", "SW");

            params.setParam("keyName", "ticker");
            params.setParam("valName", "level1_name");
            // map<code, level1_name>
            Map<String, String> code_industry = (Map<String, String>) context.handle(params, clientParam);
            //dimension的industryNames
            clientParam.setGroup("$level1_name");
            Stragtegy stragtegy2 = strategy.get("AggregateDataHandleStra");
            context.setStrategy(stragtegy2);
            List<String> industrayList = (List<String>) context.handle(params, clientParam);

            //第一步 根據入參date及type及dimension將結構搭好

            //date結構裏放入type結構
            DateModel dateModel = new DateModel();
            for(int type : new ArrayList<Integer>()) {
                TypeModel typemodel = new TypeModel(type, "typeName");
                dateModel.setTypeModel(type, typemodel);
            }
            //type爲股票的放入行業結構
            TypeModel typeModel = dateModel.getTypeMap().get(1);
            for(String industryName : industrayList) {
                IndustryModel industryModel = new IndustryModel(industryName);
                typeModel.setIndustryModel(industryName, industryModel);
            }
            //組合層次放入日期結構,此時用到了之前構建好的dateModel結構。
            CombIdModel combIdModel = new CombIdModel(119);

            for(Date date :new ArrayList<Date>()) {
                combIdModel.setDateModel(date, dateModel);
            }

            //第二步 將範圍內個券轉換成singleBond對象,並將個體對象放到已搭建好的結構combIdModel合適位置
            for(Map<String, Object> single1 : totalBondList) {
                SingleBondModel single = new SingleBondModel();
                //type date用來分類
                int type = Integer.parseInt(single1.get("type").toString());
                Date date = DateUtil.stringToDateGL(single1.get("date").toString());
                String code = single1.get("code").toString();
                //基本信息直接copy
                single.setType(type);
                single.setCode(code);
                single.setDate(date);
                //需要計算的指標計算完畢再set進對象,指標計算走之前的設計邏輯
                Double initWeight = 0.001;
                single.setInitWeight(initWeight);
                single.setIdx(123.0);

                //開始往combIdModel結構中放入 個券放入過程中在相應的層級累加initweight
                if(1== type) {
                    combIdModel.getDateModel(date).addInitWeight(initWeight).getTypeModel(type).addInitWeight(initWeight).getIndustryModel(code_industry.get(code)).addInitWeight(initWeight).addSingle(single);
                } else {
                    combIdModel.getDateModel(date).addInitWeight(initWeight).getTypeModel(type).addInitWeight(initWeight).addSingle(single);
                }
            }

            //第三步 開始計算各維度的idx
            combIdModel.calIdx();

一頭霧水對吧,其實思想層次是這樣:
先根據date type industry 把存放數據的結構建好,就好比你要把一羣乞丐放進收容所,你得先把收容所根據性別、年齡等建好男女宿舍、廁所等。
ok,存放數據的結構建好了,下一步就是把數據庫取出來的數據封裝成SingleBondModel對象並放入合適的位置,這裏在循環遍歷每一條數據的時候做了兩件事:
1、計算個券的指標idx,計算其權重initWeight。連同重要數據一併放入對象中
2、放到它本來應該在的位置,那就根據該單個對象的date、type、industry依次查找直至合適它的位置,在尋找過程中,將包括它的相應層級的initWeight加之

//開始往combIdModel結構中放入 個券放入過程中在相應的層級累加initweight
                if(1== type) {
                    combIdModel.getDateModel(date).addInitWeight(initWeight).getTypeModel(type).addInitWeight(initWeight).getIndustryModel(code_industry.get(code)).addInitWeight(initWeight).addSingle(single);
                } else {
                    combIdModel.getDateModel(date).addInitWeight(initWeight).getTypeModel(type).addInitWeight(initWeight).addSingle(single);
                }

最後計算每一層的指標,這裏看似只調用了

//第三步 開始計算各維度的idx
            combIdModel.calIdx();

但內部實現原理是這樣的:

首先combIdModel不同的層級包含了對應的對象,當調用combIdModel.calIdx()時,首先是計算每一日期的指標,每一日期的指標計算方法中會觸發下一級別層次的指標計算,直至找到最底層的SingleBondModel並計算之。

CombIdModel:

//觸發該組合下每一個DateModel的指標計算
    @Override
    public void calIdx() {
        for(Date date : dateMap.keySet())
            dateMap.get(date).calIdx();
    }

DateModel:

//計算該DateModel的指標,由CombIdModel層觸發
    @Override
    public void calIdx() {
        Double initSum = 0.0D;
        for(int type : typeMap.keySet()) {
            TypeModel typeModel = typeMap.get(type);
            initSum += typeModel.calAndGetIdx() * typeModel.getInitWeight();
        }
        idx =  initSum / initWeight;

TypeModel:


    //在DateModel層調用calAndGetIdx()觸發
    @Override
    public void calIdx() {
        if(null!=industryMap)
            calIndustryMap();
        else
            calSingleList();
    }

    public void calIndustryMap() {
        Double initSum = 0.0D;
        for(String industryName : industryMap.keySet()) {
            IndustryModel industryModel = industryMap.get(industryName);
            initSum += industryModel.calAndGetIdx() * industryModel.getInitWeight();
        }
        idx = initSum / initWeight;
    }

    public void calSingleList() {
        Double initSum = 0.0D;
        for(SingleBondModel single : singleList) {
            initSum += single.getIdx() * single.getInitWeight();
        }
        idx = initSum / initWeight;
    }

IndustryModel:

//在TypeModel層調用calAndIdx方法觸發
    @Override
    public void calIdx() {
        Double initSum = 0.0D;
        for(SingleBondModel single : singleList) {
            initSum += single.getIdx() * single.getInitWeight();
        }
        idx = initSum / initWeight;
    }

這些model均實現了抽象類

/**
 * model抽象層
 * @author fujian
 *
 */
public abstract class AbstrateModel  implements Model {
    Double idx;
    @Override
    public Double calAndGetIdx() {
        calIdx(); //當前層級下觸發計算指標動作
        return idx; //返回指標
    }

    public abstract void calIdx();
}

ok,好久不更博,總結mark下


然後被老闆嫌棄 棄用了…………….

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章