Helius
2021-01-29 08133a4fa45cb8ed3da68fea587c6e3199743918
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
package com.xcong.excoin.utils;
 
 
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.xcong.excoin.common.contants.AppContants;
import com.xcong.excoin.common.enumerates.CoinTypeEnum;
import com.xcong.excoin.common.enumerates.RabbitPriceTypeEnum;
import com.xcong.excoin.common.exception.GlobalException;
import com.xcong.excoin.modules.contract.dao.ContractHoldOrderDao;
import com.xcong.excoin.modules.contract.entity.ContractHoldOrderEntity;
import com.xcong.excoin.modules.member.dao.MemberWalletContractDao;
import com.xcong.excoin.modules.member.dao.MemberSettingDao;
import com.xcong.excoin.modules.member.entity.MemberEntity;
import com.xcong.excoin.modules.member.entity.MemberSettingEntity;
import com.xcong.excoin.modules.member.entity.MemberWalletContractEntity;
import com.xcong.excoin.modules.platform.dao.TradeSettingDao;
import com.xcong.excoin.modules.platform.entity.PlatformTradeSettingEntity;
import com.xcong.excoin.rabbit.pricequeue.OrderModel;
import com.xcong.excoin.rabbit.producer.OrderProducer;
import lombok.extern.slf4j.Slf4j;
 
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
 
/**
 * @author helius
 */
@Slf4j
public class CalculateUtil {
 
    /**
     * 计算预估强平价
     *
     * @param bondAmount      保证金
     * @param openPrice       开仓价
     * @param symbolSkuNumber 张数
     * @param lotNumber       规格
     * @param type            1:买多2:卖空
     * @return
     */
    public static BigDecimal getForceSetPrice(BigDecimal bondAmount, BigDecimal openPrice, int symbolSkuNumber, BigDecimal lotNumber,
                                              int type, MemberEntity member) {
        MemberSettingDao memberSettingDao = SpringContextHolder.getBean(MemberSettingDao.class);
        BigDecimal forcePrice = BigDecimal.ZERO;
        BigDecimal money = bondAmount.divide(new BigDecimal(symbolSkuNumber).multiply(lotNumber), 8, BigDecimal.ROUND_DOWN);
        if (member.getIsForce() == 1) {
            MemberSettingEntity memberSetting = memberSettingDao.selectMemberSettingByMemberId(member.getId());
            money = money.multiply(memberSetting.getForceParam().multiply(BigDecimal.valueOf(100)));
        }
        //卖空
        if (type == 2) {
            forcePrice = money.add(openPrice);
        } else {//开多
            forcePrice = openPrice.subtract(money);
        }
        if (forcePrice.compareTo(BigDecimal.ZERO) < 0) {
            forcePrice = BigDecimal.ZERO;
        }
        return forcePrice;
    }
 
    /**
     * 计算保证金 -- 建仓价*规格*手数*(1/杠杆倍率)
     *
     * @param openingPrice 开仓价
     * @param lotNumber    规格
     * @param symbolCnt    张数
     * @param leverRatio   杠杆倍率
     * @return
     */
    public static BigDecimal getBondAmount(BigDecimal openingPrice, BigDecimal lotNumber, Integer symbolCnt, Integer leverRatio) {
        return openingPrice.multiply(lotNumber).multiply(new BigDecimal(symbolCnt))
                .multiply(BigDecimal.ONE.divide(new BigDecimal(leverRatio)))
                .setScale(8, BigDecimal.ROUND_DOWN);
    }
 
    /**
     * 全仓模式 -- 预估强平价
     * 开仓价 - (权益 - 其他币种成本)/当前币种成本 * (开仓价 * 杠杆)
     */
    public static BigDecimal getForceSetPriceForWhole(String currentSymbol, @NotNull MemberEntity memberEntity) {
        ContractHoldOrderDao holdOrderDao = SpringContextHolder.getBean(ContractHoldOrderDao.class);
        MemberWalletContractDao walletContractDao = SpringContextHolder.getBean(MemberWalletContractDao.class);
 
        Long memberId = memberEntity.getId();
        MemberWalletContractEntity walletContract = walletContractDao.findWalletContractByMemberIdAndSymbol(memberId, CoinTypeEnum.USDT.name());
        List<ContractHoldOrderEntity> holdOrderEntities = holdOrderDao.selectHoldOrderListForWholeByMemberIdAndSymbol(memberId, null);
        List<String> symbols = holdOrderDao.selectWholeHoldOrderSymbolsByMemberId(memberId);
        
        BigDecimal result = BigDecimal.ZERO;
        if (CollUtil.isNotEmpty(holdOrderEntities)) {
 
            for (String symbol : symbols) {
                // 其他币种成本
                BigDecimal totalBondAmount = BigDecimal.ZERO;
                // 当前币种手续费
                BigDecimal symbolFeeAmount = BigDecimal.ZERO;
                // 当前币种保证金
                BigDecimal symbolBondAmount = BigDecimal.ZERO;
 
                // 开仓均价
                BigDecimal openPrice = BigDecimal.ZERO;
                // 总盈亏
                BigDecimal profitOrLoss = BigDecimal.ZERO;
                // 杠杆
                int leverRatio = 0;
                boolean isAloneLess = true;
                for (ContractHoldOrderEntity holdOrderEntity : holdOrderEntities) {
                    BigDecimal bondAmount = holdOrderEntity.getBondAmount().subtract(holdOrderEntity.getOpeningFeeAmount());
 
                    if (symbol.equalsIgnoreCase(holdOrderEntity.getSymbol())) {
                        if (holdOrderEntity.getOpeningType() == ContractHoldOrderEntity.OPENING_TYPE_MORE) {
                            isAloneLess = false;
                        }
                        symbolFeeAmount = symbolFeeAmount.add(holdOrderEntity.getOpeningFeeAmount());
                        symbolBondAmount = symbolBondAmount.add(bondAmount);
 
                        if (openPrice.compareTo(BigDecimal.ZERO) == 0) {
                            openPrice = holdOrderEntity.getOpeningPrice();
                        } else {
                            openPrice = openPrice.add(holdOrderEntity.getOpeningPrice()).divide(BigDecimal.valueOf(2), 8, BigDecimal.ROUND_DOWN);
                        }
                        leverRatio = holdOrderEntity.getLeverRatio();
                    } else {
                        totalBondAmount = totalBondAmount.add(holdOrderEntity.getBondAmount());
                    }
                    profitOrLoss = profitOrLoss.add(calProfitOrLoss(holdOrderEntity, memberEntity));
                }
 
//                log.info("{}, {}, {}, {}, {}, {}", totalBondAmount, symbolBondAmount, symbolFeeAmount, openPrice, profitOrLoss, leverRatio);
                BigDecimal equity = walletContract.getTotalBalance().add(profitOrLoss);
                BigDecimal sub = equity.subtract(symbolFeeAmount).subtract(totalBondAmount);
//                log.info("sub -- {}", sub);
                if (sub.compareTo(symbolBondAmount) <= 0) {
                    BigDecimal multi = BigDecimal.valueOf(10);
                    BigDecimal divide = equity.divide(equity.add(multi), 8, BigDecimal.ROUND_DOWN);
                    sub = symbolBondAmount.multiply(divide);
                }
 
                BigDecimal divide = sub.divide(symbolBondAmount, 8, BigDecimal.ROUND_DOWN);
//                log.info("divide -- {}", divide);
                BigDecimal divide2 = openPrice.divide(BigDecimal.valueOf(leverRatio), 8, BigDecimal.ROUND_DOWN);
//                log.info("divide2 -- {}", divide2);
 
                BigDecimal forcePrice = BigDecimal.ZERO;
                if (isAloneLess) {
                    forcePrice  = openPrice.add(divide.multiply(divide2));
                } else {
                    forcePrice  = openPrice.subtract(divide.multiply(divide2));
                }
//                log.info("forcePrice -- {}, {}", forcePrice, symbol);
                if (StrUtil.isBlank(currentSymbol)) {
                    holdOrderDao.updateForcePriceBySymbolAndMemberId(forcePrice, memberId, symbol);
                }
 
                if (symbol.equalsIgnoreCase(currentSymbol)) {
                    result = forcePrice;
                }
            }
        }
 
        return result;
    }
 
    private static BigDecimal calProfitOrLoss(ContractHoldOrderEntity holdOrderEntity, MemberEntity memberEntity) {
        CacheSettingUtils cacheSettingUtils = SpringContextHolder.getBean(CacheSettingUtils.class);
        RedisUtils redisUtils = SpringContextHolder.getBean(RedisUtils.class);
        BigDecimal lotNumber = cacheSettingUtils.getSymbolSku(holdOrderEntity.getSymbol());
        BigDecimal newPrice = new BigDecimal(redisUtils.getString(CoinTypeConvert.convertToKey(holdOrderEntity.getSymbol())));
 
        // 盈亏
        BigDecimal rewardRatio = BigDecimal.ZERO;
        // 开多
        if (ContractHoldOrderEntity.OPENING_TYPE_MORE == holdOrderEntity.getOpeningType()) {
            // (最新价-开仓价)*规格*张数
            rewardRatio = newPrice.subtract(holdOrderEntity.getOpeningPrice()).multiply(lotNumber).multiply(new BigDecimal(holdOrderEntity.getSymbolCntSale()));
            // 开空
        } else {
            // (开仓价-最新价)*规格*张数
            rewardRatio = holdOrderEntity.getOpeningPrice().subtract(newPrice).multiply(lotNumber).multiply(new BigDecimal(holdOrderEntity.getSymbolCntSale()));
        }
 
        if (memberEntity.getIsProfit() == MemberEntity.IS_PROFIT_Y) {
            PlatformTradeSettingEntity tradeSettingEntity = cacheSettingUtils.getTradeSetting();
            if (rewardRatio.compareTo(BigDecimal.ZERO) > -1) {
                rewardRatio = rewardRatio.multiply(BigDecimal.ONE.subtract(tradeSettingEntity.getProfitParam()));
            }
        }
 
        return rewardRatio;
    }
 
    private static void sendOrderBombMsg(Long id, int type, BigDecimal forceClosingPrice, String symbol, int operateNo, Long memberId) {
        OrderModel model = null;
        // 开多
        if (ContractHoldOrderEntity.OPENING_TYPE_MORE == type) {
            model = new OrderModel(id, RabbitPriceTypeEnum.CLOSE_MORE_BOMB.getValue(), forceClosingPrice.setScale(8, RoundingMode.HALF_UP).toPlainString(), symbol, operateNo, memberId);
            // 开空
        } else {
            model = new OrderModel(id, RabbitPriceTypeEnum.CLOSE_LESS_BOMB.getValue(), forceClosingPrice.setScale(8, RoundingMode.HALF_UP).toPlainString(), symbol, operateNo, memberId);
        }
        SpringContextHolder.getBean(OrderProducer.class).sendPriceOperate(JSONObject.toJSONString(model));
    }
 
 
    /**
     * 计算开仓价
     *
     * @param orderType 订单类型
     * @param newPrice  当前价
     * @param spread    划点
     * @return
     */
    public static BigDecimal getOpeningPrice(int orderType, BigDecimal newPrice, BigDecimal spread) {
        BigDecimal openingPrice = BigDecimal.ZERO;
        if (orderType == ContractHoldOrderEntity.OPENING_TYPE_MORE) {
            // 市场价*(1 + (点差/10000))
            openingPrice = newPrice.multiply(BigDecimal.ONE.add(spread.divide(new BigDecimal(10000)))).setScale(8, BigDecimal.ROUND_DOWN);
 
            // 开空
        } else if (orderType == ContractHoldOrderEntity.OPENING_TYPE_LESS) {
            // 市场价*(1 - (点差/10000))
            openingPrice = newPrice.multiply(BigDecimal.ONE.subtract(spread.divide(new BigDecimal(10000)))).setScale(8, BigDecimal.ROUND_DOWN);
        } else {
            throw new GlobalException(MessageSourceUtils.getString("unknown_type"));
        }
        return openingPrice;
    }
 
    /**
     * 计算开仓手续费
     *
     * @param openingPrice 开仓价
     * @param lotNumber    规格
     * @param count        张数
     * @param feeRatio     手续费率
     * @return
     */
    public static BigDecimal getOpenFeePrice(BigDecimal openingPrice, BigDecimal lotNumber, int count, BigDecimal feeRatio) {
        return openingPrice.multiply(lotNumber)
                .multiply(new BigDecimal(count))
                .multiply(feeRatio.divide(new BigDecimal(100)))
                .setScale(8, BigDecimal.ROUND_DOWN);
    }
 
    public static BigDecimal calOrderProfitOrLess(int type, BigDecimal newPrice, BigDecimal openPrice, BigDecimal lotNumber, int symbolCnt, int isProfit) {
        CacheSettingUtils cacheSettingUtils = SpringContextHolder.getBean(CacheSettingUtils.class);
 
        PlatformTradeSettingEntity tradeSetting = cacheSettingUtils.getTradeSetting();
        // 单个订单盈利
        BigDecimal profitOrLess = BigDecimal.ZERO;
        // 开多
        if (ContractHoldOrderEntity.OPENING_TYPE_MORE == type) {
            profitOrLess = newPrice.subtract(openPrice).multiply(new BigDecimal(symbolCnt)).multiply(lotNumber);
            // 开空
        } else {
            profitOrLess = openPrice.subtract(newPrice).multiply(new BigDecimal(symbolCnt)).multiply(lotNumber);
        }
 
        if (MemberEntity.IS_PROFIT_Y == isProfit) {
            if (profitOrLess.compareTo(BigDecimal.ZERO) > 0) {
                profitOrLess = profitOrLess.multiply(BigDecimal.ONE.subtract(tradeSetting.getForceParam()));
            } else {
                profitOrLess = profitOrLess.multiply(BigDecimal.ONE.add(tradeSetting.getForceParam()));
            }
        }
 
        return profitOrLess;
    }
 
 
    /**
     * 全仓模式下,维持保证金
     * 维持保证金 = 持仓价值*维持保证金率= 面值*张数*开仓价格*维持保证金率
     * @param contractHoldOrder
     * @return
     */
    public static BigDecimal calMemberHoldBond(ContractHoldOrderEntity contractHoldOrder) {
        TradeSettingDao tradeSettingDao = SpringContextHolder.getBean(TradeSettingDao.class);
        RedisUtils redisUtils = SpringContextHolder.getBean(RedisUtils.class);
        PlatformTradeSettingEntity tradeSetting = tradeSettingDao.findTradeSetting();
        BigDecimal holdBondRatio = (BigDecimal) redisUtils.get(AppContants.HOLD_BOND_RATIO);
        if (holdBondRatio == null) {
            holdBondRatio = tradeSetting.getHoldBondRatio();
            redisUtils.set(AppContants.HOLD_BOND_RATIO, tradeSetting.getHoldBondRatio());
        }
 
        return contractHoldOrder.getOpeningPrice().multiply(new BigDecimal(contractHoldOrder.getSymbolCntSale())).multiply(holdBondRatio).multiply(contractHoldOrder.getSymbolSku());
    }
}