1392 lines
63 KiB
C#
1392 lines
63 KiB
C#
using BaseOUDAL;
|
|
using Confluent.Kafka;
|
|
using DocumentFormat.OpenXml.Wordprocessing;
|
|
using Microsoft.Office.Interop.Excel;
|
|
using CsvHelper;
|
|
using Newtonsoft.Json;
|
|
using Qdp.Foundation.Utilities;
|
|
using YLErp.Abstract;
|
|
using YLErp.BLL;
|
|
using YLErp.BLL.Eod;
|
|
using YLErp.Commons;
|
|
using YLErp.DBModels;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.Enums;
|
|
using YLErp.Helpers;
|
|
using YLErp.Model;
|
|
using YLErp.Model.HengTaiModel;
|
|
using YLErp.Models;
|
|
using YLErp.Modules.AppModule;
|
|
using YLErp.Modules.ClientModule;
|
|
using YLErp.Modules.ExchangeTradeModule;
|
|
using YLErp.Modules.TradeModule;
|
|
using static YLErp.ConsGlobal;
|
|
|
|
namespace YLErp.Modules.SwapModule
|
|
{
|
|
public class SwapConsumerService : YLBaseService
|
|
{
|
|
|
|
static readonly IYcLogger logger = LogFactory.GetLogger<SwapConsumerService>();
|
|
|
|
private IKafkaProduce kafkaProduceHelper;
|
|
private string reqMarginInsert = string.Empty;
|
|
public SwapConsumerService(OptUserInfo optUser) : base(optUser)
|
|
{
|
|
reqMarginInsert = Environment.GetEnvironmentVariable("KafkaConfig_ReqMarginInsertTopic");
|
|
}
|
|
public void SetKafKaProduce(IKafkaProduce kafkaProduce)
|
|
{
|
|
kafkaProduceHelper = kafkaProduce;
|
|
}
|
|
/// <summary>
|
|
/// 4.8.1. 消费收益互换回执交易接口
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerTradeResp(KafkaConsumerHelper _kafkaConsumer)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<RequestBaseWithHead<SwapTradeReq>>(msg);
|
|
var pushLogService = new PushLogService(UserInfo);
|
|
var serialNo = result.HEADER.SERIAL_NO;
|
|
var extNo = result.HEADER.EXT_NO;
|
|
var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.收益互换交易回执);
|
|
var resultState = SwapPushDataStateEnum.接收处理成功;
|
|
if (pushLog != null && pushLog.data_state == (int)resultState)
|
|
{
|
|
return;
|
|
}
|
|
string dealResult = "";
|
|
if (result.RECORD == null)
|
|
{
|
|
resultState = SwapPushDataStateEnum.接收处理失败;
|
|
dealResult = "收益互换信息不能为空";
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(extNo) && result.RECORD.BASEINFO != null)
|
|
{
|
|
extNo = result.RECORD.BASEINFO.EXT_NO;
|
|
}
|
|
try
|
|
{
|
|
var trade = DbContext.trade.FirstOrDefault(x => x.TradeNumber == extNo);
|
|
if (trade != null)
|
|
{
|
|
var HTStatus = TryFormatInt(result.RECORD.BASEINFO.ORDSTATUS);
|
|
trade.HTStatus = HTStatus;
|
|
DbContext.SaveChanges();
|
|
if (HTStatus != 7)
|
|
{
|
|
return;
|
|
}
|
|
DealUpdateConsumerTrade(result.RECORD, trade);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception($"交易{extNo}不存在");
|
|
}
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
resultState = SwapPushDataStateEnum.接收处理失败;
|
|
dealResult = e.Message;
|
|
LogFactory.GetLogger("SwapConsumerService").Error("消费收益互换回执交易接口", e);
|
|
}
|
|
}
|
|
pushLogService.SaveConsumerLog(serialNo, extNo, SwapPushDataEnum.收益互换交易回执, resultState, msg, dealResult);
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 4.8.2. 消费互换资产交易回执接口
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerTradePosiResp(KafkaConsumerHelper _kafkaConsumer)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<RequestBaseWithHead<SwapPosiReq>>(msg);
|
|
var pushLogService = new PushLogService(UserInfo);
|
|
var serialNo = result.HEADER.SERIAL_NO;
|
|
var extNo = result.HEADER.EXT_NO;
|
|
var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.互换资产交易回执);
|
|
var resultState = SwapPushDataStateEnum.接收处理成功;
|
|
string dealResult = "";
|
|
if (pushLog != null && pushLog.data_state == (int)resultState)
|
|
{
|
|
return;
|
|
}
|
|
if (result.RECORD == null || result.RECORD.BASEINFO == null)
|
|
{
|
|
resultState = SwapPushDataStateEnum.接收处理失败;
|
|
dealResult = "找不到合约编号";
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
var account = result.RECORD.BASEINFO.SECU_INT;
|
|
var HTAccounts = DbContext.exchange_account.Where(x => !string.IsNullOrEmpty(x.AccountCode)).Select(s => s.AccountCode).Distinct().ToList();
|
|
if (HTAccounts.Contains(account))
|
|
{
|
|
PrepareExchangeTradeData(result.RECORD,"衡泰同步");
|
|
return;
|
|
}
|
|
if (OtcAppConfigHelper.HTUpdate != 1)
|
|
{
|
|
return;
|
|
}
|
|
var tradeNumber = result.RECORD.BASEINFO.TRD_RELATED_NO;
|
|
var HTStatus = TryFormatInt(result.RECORD.BASEINFO.ORDSTATUS);
|
|
var trade = CheckTradeExist(tradeNumber);
|
|
// 衡泰状态不是成交确认或者 当前交易有平仓,不给修改,如日内平仓,或者dma部分平仓
|
|
if (HTStatus != 7)
|
|
{
|
|
return;
|
|
}
|
|
if (pushLog == null && !CheckPositionNumber(extNo))
|
|
{
|
|
if (long.TryParse(extNo, out long dataId))//平仓
|
|
{
|
|
pushLog = pushLogService.GetLogByDataId(dataId);
|
|
if (pushLog != null)//我方平仓,不做操作
|
|
{
|
|
extNo = pushLog.data_number;
|
|
}
|
|
}
|
|
else //衡泰平仓
|
|
{
|
|
var posiGrossPrice = TryFormatDecimal(result.RECORD.TRADEINFO.ORDPRICE) / 100;
|
|
var orderDate = TryFormatDate(result.RECORD.BASEINFO.ORDDATE).Date;
|
|
dealResult = DealUnwindPosiEvent(posiGrossPrice, trade, orderDate);
|
|
}
|
|
}
|
|
else if (trade.UnWindDate == null)
|
|
{
|
|
PreparePosiData(result.RECORD, trade, extNo);
|
|
}
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
resultState = SwapPushDataStateEnum.接收处理失败;
|
|
dealResult = e.Message;
|
|
LogFactory.GetLogger("SwapConsumerService").Error("消费互换资产交易回执接口", e);
|
|
}
|
|
}
|
|
new PushLogService(UserInfo).SaveConsumerLog(serialNo, extNo, SwapPushDataEnum.互换资产交易回执, resultState, msg, dealResult);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 对冲交易回执接口
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerExchangeTradeResp(KafkaConsumerHelper _kafkaConsumer,string tradeSource)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
var hedgingSource = OtcAppConfigHelper.HedgingSource == 2 || OtcAppConfigHelper.HedgingSource == 0;
|
|
if (hedgingSource)
|
|
{
|
|
return;
|
|
}
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<SwapAsset>(msg);
|
|
if (result == null)
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
var account = result.SECU_INT;
|
|
var HTAccounts = DbContext.exchange_account.Where(x => !string.IsNullOrEmpty(x.AccountCode)).Select(s => s.AccountCode).Distinct().ToList();
|
|
if (HTAccounts.Contains(account))
|
|
{
|
|
PrepareExchangeTradeData(result, tradeSource);
|
|
return;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
LogFactory.GetLogger("SwapConsumerService").Error("对冲交易回执接口", e);
|
|
}
|
|
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// Trs对冲交易接口
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerTrsExchangeTradeResp(KafkaConsumerHelper _kafkaConsumer, string tradeSource)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
var hedgingSource = OtcAppConfigHelper.HedgingSource==1|| OtcAppConfigHelper.HedgingSource==0;
|
|
if (hedgingSource)
|
|
{
|
|
return;
|
|
}
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<SwapAsset>(msg);
|
|
if (result == null)
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
var account = result.SECU_INT;
|
|
var HTAccounts = DbContext.exchange_account.Where(x => !string.IsNullOrEmpty(x.AccountCode)).Select(s => s.AccountCode).Distinct().ToList();
|
|
if (HTAccounts.Contains(account))
|
|
{
|
|
PrepareExchangeTradeData(result, tradeSource);
|
|
Task.Run(() =>
|
|
{
|
|
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
LogFactory.GetLogger("SwapConsumerService").Error("TRS对冲交易回执接口", e);
|
|
}
|
|
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 4.8.3. 消费预付金交易回执接口
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerTradeMarginResp(KafkaConsumerHelper _kafkaConsumer)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<RequestBaseWithHead<SwapMarginReq>>(msg);
|
|
var pushLogService = new PushLogService(UserInfo);
|
|
var serialNo = result.HEADER.SERIAL_NO;
|
|
var extNo = result.HEADER.EXT_NO;
|
|
var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.预付金交易回执);
|
|
var resultState = SwapPushDataStateEnum.接收处理成功;
|
|
if (pushLog != null && pushLog.data_state == (int)resultState)
|
|
{
|
|
return;
|
|
}
|
|
string dealResult = "";
|
|
if (result.RECORD == null || result.RECORD.RESERVE == null)
|
|
{
|
|
resultState = SwapPushDataStateEnum.接收处理失败;
|
|
dealResult = "找不到合约编号";
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
if (OtcAppConfigHelper.HTUpdate != 1)
|
|
{
|
|
return;
|
|
}
|
|
//处理回执
|
|
var trade = CheckTradeExist(result.RECORD.RESERVE.CONTRACT_CODE);
|
|
var HTStatus = TryFormatInt(result.RECORD.BASEINFO.ORDSTATUS);
|
|
// 衡泰状态不是成交确认或者 当前交易有平仓,不给修改,如日内平仓,或者dma部分平仓
|
|
if (HTStatus != 7)
|
|
{
|
|
return;
|
|
}
|
|
if (pushLog == null && !CheckPositionNumber(extNo))//可能平仓
|
|
{
|
|
if (long.TryParse(extNo, out long dataId))//平仓
|
|
{
|
|
pushLog = pushLogService.GetLogByDataId(dataId);
|
|
if (pushLog != null)// 我方平仓
|
|
{
|
|
extNo = pushLog.data_number;
|
|
}
|
|
}
|
|
else //衡泰平仓
|
|
{
|
|
PrepareUnwindMarginData(result.RECORD, trade);
|
|
}
|
|
|
|
}
|
|
else if (trade.UnWindDate == null)
|
|
{
|
|
PrepareMarginData(result.RECORD, trade, extNo);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
resultState = SwapPushDataStateEnum.接收处理失败;
|
|
dealResult = e.Message;
|
|
LogFactory.GetLogger("SwapConsumerService").Error("消费预付金交易回执接口", e);
|
|
}
|
|
}
|
|
new PushLogService(UserInfo).SaveConsumerLog(serialNo, extNo, SwapPushDataEnum.预付金交易回执, resultState, msg, dealResult);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 4.8.4. 消费合约终止交易回执接口
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerTradeUnwindResp(KafkaConsumerHelper _kafkaConsumer)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<RequestBase<SwapUnwindReq>>(msg);
|
|
var pushLogService = new PushLogService(UserInfo);
|
|
var serialNo = result.SERIAL_NO;
|
|
var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.合约终止交易回执);
|
|
var resultState = SwapPushDataStateEnum.接收处理成功;
|
|
if (pushLog != null && pushLog.data_state == (int)resultState)
|
|
{
|
|
return;
|
|
}
|
|
string dealResult = "";
|
|
string extNo = result.EXT_NO;
|
|
try
|
|
{
|
|
//处理回执
|
|
if (result.RECORD.ACCTSWAP_TERMINATE==null)
|
|
{
|
|
return;
|
|
}
|
|
extNo = result.RECORD.ACCTSWAP_TERMINATE.CONTRACT_CODE;
|
|
var trade = CheckTradeExist(extNo);
|
|
var HTStatus = TryFormatInt(result.RECORD.ACCTSWAP_TERMINATE.ORDSTATUS);
|
|
if (HTStatus!=7)
|
|
{
|
|
return;
|
|
}
|
|
PrepareUnwindData(result.RECORD, trade);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
resultState = SwapPushDataStateEnum.接收处理失败;
|
|
dealResult = e.Message;
|
|
LogFactory.GetLogger("SwapConsumerService").Error("消费合约终止交易回执接口", e);
|
|
}
|
|
pushLogService.SaveConsumerLog(serialNo, result.EXT_NO, SwapPushDataEnum.合约终止交易回执, resultState, msg, dealResult);
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 4.8.6. 消费收益互换资金流水数据回执接口
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerTradeCashResp(KafkaConsumerHelper _kafkaConsumer)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<RequestBaseWithHead<SwapCashReq>>(msg);
|
|
var pushLogService = new PushLogService(UserInfo);
|
|
var serialNo = result.HEADER.SERIAL_NO;
|
|
var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.收益互换资金流水数据回执);
|
|
var resultState = SwapPushDataStateEnum.接收处理成功;
|
|
if (pushLog != null && pushLog.data_state == (int)resultState)
|
|
{
|
|
return;
|
|
}
|
|
string dealResult = "";
|
|
List<int> clientIds = new List<int>();
|
|
try
|
|
{
|
|
var extNos = result.RECORD.ACCTSWAP_CASH_LIST.Where(x => !string.IsNullOrEmpty(x.EXT_NO)).Select(s => s.EXT_NO).ToList();
|
|
var trades = CheckTradeExistByList(extNos);
|
|
//处理回执
|
|
|
|
clientIds = PrepareTradeCashData(result.RECORD, trades);
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
resultState = SwapPushDataStateEnum.接收处理失败;
|
|
dealResult = e.Message;
|
|
LogFactory.GetLogger("SwapConsumerService").Error("消费收益互换资金流水数据回执接口", e);
|
|
}
|
|
pushLogService.SaveConsumerLog(serialNo, "", SwapPushDataEnum.收益互换资金流水数据回执, resultState, msg, dealResult);
|
|
//if (clientIds.Count>0)
|
|
//{
|
|
// PushClientCash(clientIds, onRspAccountCapitalTopicTopic);
|
|
//}
|
|
}
|
|
});
|
|
}
|
|
|
|
public bool CheckPositionNumber(string extNo)
|
|
{
|
|
return DbContext.swap_position.Any(x => x.PosiNumber == extNo);
|
|
}
|
|
/// <summary>
|
|
/// 收盘 收益互换回执查衡泰库返回
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerIRSDb(KafkaConsumerHelper _kafkaConsumer)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<IRDbRespBase>(msg);
|
|
var respMappers = GetHTDbRespMapperData<ReqQryIRSDbResp>(result);
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 客户资金推送
|
|
/// </summary>
|
|
/// <param name="_kafkaConsumer"></param>
|
|
public void ConsumerCashNotice(KafkaConsumerHelper _kafkaConsumer)
|
|
{
|
|
_kafkaConsumer.Subscribe(msg =>
|
|
{
|
|
if (!string.IsNullOrEmpty(msg))
|
|
{
|
|
var result = JsonHelper.Deserialize<CashNoticeReq>(msg);
|
|
if (result!=null)
|
|
{
|
|
PushCashToHT(result.cashId, null);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// 对返回数据库数据进行映射
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="iRDbResp"></param>
|
|
/// <returns></returns>
|
|
private List<T> GetHTDbRespMapperData<T>(IRDbRespBase iRDbResp) where T : new()
|
|
{
|
|
List<T> values = new List<T>();
|
|
string[] fields = iRDbResp.Header.Split(',');
|
|
var records = iRDbResp.Records;
|
|
for (int j = 0; j < records.Count; j++)
|
|
{
|
|
string[] recordFields = records[j].Split(',');
|
|
T iRSDb = Activator.CreateInstance<T>();
|
|
for (int i = 0; i < fields.Length; i++)
|
|
{
|
|
string fieldName = fields[i];
|
|
string fieldValue = recordFields[i];
|
|
typeof(T).GetProperty(fieldName)?.SetValue(iRSDb, fieldValue);
|
|
values.Add(iRSDb);
|
|
}
|
|
}
|
|
return values;
|
|
}
|
|
#region 交易回执相关私有方法
|
|
/// <summary>
|
|
/// 修改交易
|
|
/// </summary>
|
|
/// <param name="swapTradeReq"></param>
|
|
private void DealUpdateConsumerTrade(SwapTradeReq swapTradeReq, trade trade)
|
|
{
|
|
var HTStatus = trade.HTStatus;
|
|
if (OtcAppConfigHelper.HTUpdate != 1)
|
|
{
|
|
return;
|
|
}
|
|
//0新建、-4交易执行中、-3风险预审中、7成交确认、9交易撤单、1审批中、5审批通过、4审批拒绝、10交易终止
|
|
if (HTStatus == 9)
|
|
{
|
|
new SwapTradeService(UserInfo).deleteTrade(trade.id, false);
|
|
return;
|
|
}
|
|
// 衡泰状态不是成交确认或者 当前交易有平仓,不给修改,如日内平仓,或者dma部分平仓
|
|
if (trade.UnWindDate!=null)
|
|
{
|
|
throw new Exception($"交易{trade.TradeNumber}已经产生平仓信息,不支持再修改");
|
|
}
|
|
ValidateTrade(swapTradeReq, trade);
|
|
trade.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == trade.id);
|
|
PrepareTradeData(swapTradeReq, trade, false);
|
|
if (trade.TradeStatus == ConsTrade.确认成交)
|
|
{
|
|
AfterUpdateDeal(trade, (int)SwapEventTypeEnum.修改交易, "接口修改交易");
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 互换交易数据准备
|
|
/// </summary>
|
|
/// <param name="swapTradeReq"></param>
|
|
/// <param name="trade"></param>
|
|
private void PrepareTradeData(SwapTradeReq swapTradeReq, trade trade, bool isAdd)
|
|
{
|
|
var tradeDirection = swapTradeReq.BASEINFO.TRDTYPE == "151" ? (int)SwapDirectionEnum.收取 : (int)SwapDirectionEnum.支付;
|
|
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == trade.id && x.IsInitial && !x.Invalid).ToList();
|
|
positions.ForEach(x =>
|
|
{
|
|
x.PosiStartDate = trade.StartDate.Value;
|
|
x.PosiMatuirityDate = trade.ExerciseDate.Value;
|
|
});
|
|
var interestList = positions.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode)).ToList();
|
|
var posiList = positions.Where(x => x.InterestDirection == 0).ToList();
|
|
interestList.ForEach(x =>
|
|
{
|
|
x.PosiStartDate = trade.StartDate.Value;
|
|
x.PosiMatuirityDate = trade.ExerciseDate.Value;
|
|
});
|
|
var floatRateInterest = positions.FirstOrDefault(x => !string.IsNullOrEmpty(x.FloatRateUnderlyingCode));
|
|
swap_position interest;
|
|
if (interestList.Any())
|
|
{
|
|
interest = interestList[0];
|
|
}
|
|
else
|
|
{
|
|
throw new Exception($"交易{trade.TradeNumber}找不到固定端信息");
|
|
}
|
|
var rate = swapTradeReq.ACCTSWAP.REGULAR_INFO.SPREAD;
|
|
|
|
if (!string.IsNullOrEmpty(swapTradeReq.ACCTSWAP.REGULAR_INFO.RATE))
|
|
{
|
|
var rated= TryFormatDecimal(rate);
|
|
if (rated!=0)
|
|
{
|
|
rate = swapTradeReq.ACCTSWAP.REGULAR_INFO.RATE;
|
|
}
|
|
}
|
|
interest.InterestRateDefault = TryFormatDecimal(rate);
|
|
interest.InterestDirection = tradeDirection;
|
|
if (!string.IsNullOrEmpty(swapTradeReq.ACCTSWAP.IR_I_CODE))
|
|
{
|
|
interest.FloatRateUnderlyingCode = swapTradeReq.ACCTSWAP.IR_I_CODE;
|
|
interest.IsAnnualized = true;
|
|
}
|
|
else
|
|
{
|
|
interest.IsAnnualized = interest.InterestRateDefault != 0;
|
|
}
|
|
interest.InterestType = swapTradeReq.ACCTSWAP.FLOAT_METHOD == "0" ? (int)InterestTypeEnum.单利 : (int)InterestTypeEnum.复利;
|
|
if (trade.trade_extend != null)
|
|
{
|
|
var calcMode = GetInterestRule(swapTradeReq.ACCTSWAP.FIXED_INTEREST_RULE);
|
|
TradeExtendJson tradeExtendJson = trade.trade_extend.ExtendObj;
|
|
tradeExtendJson.InterestCalcMode = calcMode;
|
|
trade.trade_extend.ExtendJson = JsonHelper.Serialize(tradeExtendJson);
|
|
if (isAdd)
|
|
{
|
|
DbContext.trade_extend.Add(trade.trade_extend);
|
|
}
|
|
}
|
|
|
|
var rateDate = TryFormatDate(swapTradeReq.ACCTSWAP.REGULAR_INFO.INTEREST_END_DAY);
|
|
|
|
IntervalModel intervalModel = new IntervalModel()
|
|
{
|
|
Date = rateDate,
|
|
Rate = interest.InterestRateDefault,
|
|
Settlement = 0
|
|
};
|
|
List<IntervalModel> intervalModels = new List<IntervalModel>
|
|
{
|
|
intervalModel
|
|
};
|
|
interest.InterestSwapInterval = JsonHelper.Serialize(intervalModels);
|
|
|
|
if (interest.id == 0)
|
|
{
|
|
DbContext.swap_position.Add(interest);
|
|
}
|
|
DbContext.SaveChanges();
|
|
var observation = DbContext.trade_obervation.FirstOrDefault(x => x.PositionId == interest.id);
|
|
if (observation == null)
|
|
{
|
|
observation = new TradeObervation()
|
|
{
|
|
PositionId = interest.id,
|
|
IsDeductPrincipal = true,
|
|
ObservationAlignEnd = true,
|
|
ObservationNum = 1,
|
|
ObservationUnit = "D",
|
|
ObservationHolidayType = "Following",
|
|
ObservationStart = trade.TradeDate
|
|
};
|
|
}
|
|
observation.ObservationInterval = interest.InterestSwapInterval;
|
|
if (observation.id == 0)
|
|
{
|
|
DbContext.trade_obervation.Add(observation);
|
|
}
|
|
DbContext.SaveChanges();
|
|
}
|
|
/// <summary>
|
|
/// 校验交易信息
|
|
/// </summary>
|
|
/// <param name="swapTradeReq"></param>
|
|
/// <param name="td"></param>
|
|
/// <exception cref="ServiceException"></exception>
|
|
private Client ValidateTrade(SwapTradeReq swapTradeReq, trade td)
|
|
{
|
|
//CheckTradeStatus(td);
|
|
if (string.IsNullOrEmpty(swapTradeReq.BASEINFO.PARTY_NAME))
|
|
{
|
|
throw new ServiceException($"参数PARTY_NAME为空");
|
|
}
|
|
Client client = null;
|
|
string partyNo = string.Empty;
|
|
client = DataCacheProvider.GetClientDataSource().AsQueryable().FirstOrDefault(x => x.Name == swapTradeReq.BASEINFO.PARTY_NAME);
|
|
partyNo = swapTradeReq.BASEINFO.PARTY_NAME;
|
|
if (client == null)
|
|
{
|
|
throw new ServiceException($"交易对手方{partyNo}信息不存在");
|
|
}
|
|
td.ClientId = client.id;
|
|
td.ClientName = client.Name;
|
|
td.ClientNumber = client.Number;
|
|
var orddDate = TryFormatDate(swapTradeReq.BASEINFO.ORDDATE);
|
|
var endDate = TryFormatDate(swapTradeReq.ACCTSWAP.END_DAY);
|
|
var stockNotional = TryFormatDouble(swapTradeReq.ACCTSWAP.PRINCIPAL);
|
|
td.TradeDate = orddDate.Date;
|
|
td.ExerciseDate = endDate.Date;
|
|
td.OriginalStockEqvNotional = stockNotional;
|
|
td.StockEqvNotional = td.OriginalStockEqvNotional.Value;
|
|
return client;
|
|
}
|
|
/// <summary>
|
|
/// 创建交易
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private trade CreateTrade(SwapTradeReq swapTradeReq)
|
|
{
|
|
TradeExtendJson tradeExtendJson = new TradeExtendJson()
|
|
{
|
|
FlowBookMode = (int)FlowBookModeEnum.重置,
|
|
FloatingPnlAnnualized = false,
|
|
NeedOpenFee = false,
|
|
OpenFeeType = 0,
|
|
Direction = 2,
|
|
};
|
|
var r = new trade()
|
|
{
|
|
TradeType = "收益互换",
|
|
BuySell = "卖出",
|
|
UnderlyingInstrumentType = "Stock",
|
|
StartDate = valuedateBLL.ValueDate,
|
|
TradeDate = valuedateBLL.ValueDate,
|
|
TraderId = UserId,
|
|
TraderName = UserName,
|
|
MarginTemplateName = "系统默认",
|
|
OpponentRole = "乙方",
|
|
OriginalStockEqvNotional = 0,
|
|
StructureType = "普通债券类收益互换",
|
|
InitialMargin = 0,
|
|
TradeStatus = "确认成交"
|
|
};
|
|
r.ExerciseDate = r.TradeDate.Value.AddDays(7);
|
|
r.trade_extend = new trade_extend()
|
|
{
|
|
ExtendJson = JsonHelper.Serialize(tradeExtendJson)
|
|
};
|
|
//山证衡泰互换簿记
|
|
|
|
var client = DataCacheProvider.GetClientDataSource().AsQueryable().FirstOrDefault(x => x.Name == swapTradeReq.BASEINFO.PARTY_NAME);
|
|
if (client == null)
|
|
{
|
|
throw new ServiceException($"找不到名称为{swapTradeReq.BASEINFO.PARTY_NAME}的交易对手方");
|
|
}
|
|
var etradeRule = new EtradingRuleService(UserInfo).GetEtradingRuleAccont(client.BoundSide, client.Number);
|
|
if (etradeRule == null || string.IsNullOrEmpty(etradeRule.AssetAccount_0))
|
|
{
|
|
throw new ServiceException($"{client.Number}未设置TRS对客簿记账户");
|
|
}
|
|
string clearingAgency = etradeRule.ClearingAgency_0;
|
|
var asset = DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == etradeRule.AssetAccount_0).FirstOrDefault();//取对客簿记账户
|
|
if (asset == null)
|
|
{
|
|
throw new ServiceException($"找不到名为{etradeRule.AssetAccount_0}的簿记账户信息");
|
|
}
|
|
if (asset.TraderIdsInt.Count == 0)
|
|
{
|
|
throw new ServiceException($"{etradeRule.AssetAccount_0}的簿记账户未设置交易员");
|
|
}
|
|
r.AssetBookName = asset.Name;
|
|
r.AssetId = asset.id;
|
|
r.TraderId = asset.TraderIdsInt.FirstOrDefault();
|
|
r.TraderName = asset.TraderNamesList.FirstOrDefault();
|
|
r.TradeOldStatus = ConsTrade.新增待确认;
|
|
r.OptId = UserId;
|
|
r.OptName = UserName;
|
|
r.OptDate = OptDate;
|
|
r.CreateDate = OptDate;
|
|
r.ValidState = ConsGlobal.Valid;
|
|
r.TradeSource = "系统交易";
|
|
r.TradeNumber = swapTradeReq.BASEINFO.EXT_NO;
|
|
return r;
|
|
}
|
|
private trade CheckTradeExist(string extNo)
|
|
{
|
|
var trade = DbContext.trade.FirstOrDefault(x => x.TradeNumber == extNo);
|
|
if (trade == null)
|
|
{
|
|
Thread.Sleep(5000);
|
|
trade = DbContext.trade.FirstOrDefault(x => x.TradeNumber == extNo);
|
|
if (trade == null)
|
|
{
|
|
throw new ServiceException($"不存在交易编号为{extNo}的交易,无法修改");
|
|
}
|
|
}
|
|
return trade;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 批量校验交易是否存在
|
|
/// </summary>
|
|
/// <param name="extNos"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="ServiceException"></exception>
|
|
private List<trade> CheckTradeExistByList(List<string> extNos)
|
|
{
|
|
List<trade> tradeList = new List<trade>();
|
|
var trades = DbContext.trade.Where(x => extNos.Contains(x.TradeNumber)).AsNoTracking();
|
|
foreach (var extNo in extNos)
|
|
{
|
|
var trade = trades.FirstOrDefault();
|
|
//if (trade==null)
|
|
//{
|
|
// throw new ServiceException($"交易编号为{extNo}的交易不存在");
|
|
//}
|
|
if (trade != null)
|
|
{
|
|
tradeList.Add(trade);
|
|
}
|
|
}
|
|
return tradeList;
|
|
}
|
|
/// <summary>
|
|
/// 校验交易状态
|
|
/// </summary>
|
|
/// <param name="trade"></param>
|
|
/// <exception cref="ServiceException"></exception>
|
|
private void CheckTradeStatus(trade trade)
|
|
{
|
|
if (trade.UnWindDate.HasValue)
|
|
{
|
|
throw new ServiceException($"交易已经产生平仓或收益结算,无法修改");
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 保存交易后需要做的处理
|
|
/// </summary>
|
|
/// <param name="td"></param>
|
|
/// <param name="eventType"></param>
|
|
/// <param name="msg"></param>
|
|
private void AfterUpdateDeal(trade td, int eventType, string msg)
|
|
{
|
|
SwapTradeService swapTradeService = new SwapTradeService(this);
|
|
swapTradeService.ClearSwapPositions(td);
|
|
DbContext.SaveChanges();
|
|
swapTradeService.InitialPosition(td, true);
|
|
swapTradeService.AddPositionEvent(td,"衡泰同步");
|
|
DbContext.SaveChanges();
|
|
//new SwapEodPositionService(this).InitSaveEodSwapPosition(td);
|
|
var swapEventService = new SwapEventService(this);
|
|
swapEventService.AddSwapEventDate(td.TradeDate.Value, td.id, eventType, "", 0, true, msg + "确认");
|
|
swapEventService.AddSwapEventDate(td.TradeDate.Value, td.id, (int)SwapEventTypeEnum.确认交易, "", 0, true, msg + "确认");
|
|
}
|
|
|
|
private void DeleteSwapEvent(int tradeId)
|
|
{
|
|
new SwapEventService(this).DeleteEvent(tradeId);
|
|
}
|
|
/// <summary>
|
|
/// 添加交易元数据
|
|
/// </summary>
|
|
private void AddTradeMeta(bool saveChanges, int tradeId, string metaKey, string metaValue)
|
|
{
|
|
DbContext.TradeMeta.Add(new TradeMeta
|
|
{
|
|
TradeId = tradeId,
|
|
MetaKey = metaKey,
|
|
MetaValue = metaValue,
|
|
CreateTime = DateTime.Now
|
|
});
|
|
if (saveChanges)
|
|
{
|
|
DbContext.SaveChanges();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 互换资产回执相关私有方法
|
|
private void PreparePosiData(SwapPosiReq swapPosiReq, trade td, string extNo)
|
|
{
|
|
var underlying = CheckUnderlying(swapPosiReq.TRADEINFO.I_CODE, swapPosiReq.TRADEINFO.MARKET);
|
|
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.PosiDirection > 0 && !x.Invalid).ToList();
|
|
var position = positions.Where(x => x.PosiNumber == extNo && x.IsInitial).FirstOrDefault();
|
|
var realPosition = positions.Where(x => x.PosiNumber == extNo && !x.IsInitial).FirstOrDefault();
|
|
if (position == null)
|
|
{
|
|
throw new Exception($"交易{td.TradeNumber}找不到持仓信息");
|
|
}
|
|
decimal price = Convert.ToDecimal(underlying.Price ?? 100);
|
|
bool init = swapPosiReq.TRADEINFO.OCFLAG == "0";
|
|
if (swapPosiReq.TRADEINFO.OCFLAG == "0")
|
|
{
|
|
position.UnderlyingCode= underlying.UnderlyingCode;
|
|
position.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType;
|
|
position.PositionType = swapPosiReq.BASEINFO.TRDTYPE == "10" ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
|
|
position.PosiQuantity = TryFormatDecimal(swapPosiReq.TRADEINFO.PARVALUE);
|
|
position.PosiGrossPrice = TryFormatDecimal(swapPosiReq.TRADEINFO.ORDPRICE) / price;
|
|
int tradeSide = position.PositionType == (int)EnumDirection.Long ? 1 : -1;
|
|
position.PosiNetPrice = position.PosiQuantity == 0 ? position.PosiGrossPrice : position.PosiGrossPrice + position.PosiTradingFee * tradeSide / position.PosiQuantity;
|
|
td.OriginalNotional = Convert.ToDouble(position.PosiQuantity);
|
|
td.Notional = td.OriginalNotional ?? 0;
|
|
td.UnderlyingCode = underlying.UnderlyingCode;
|
|
td.UnderlyingAssetName = underlying.UnderlyingInstrumentTypeCn;
|
|
td.UnderlyingName = underlying.UnderlyingName;
|
|
td.SpotPrice = Convert.ToDouble(position.PosiNetPrice);
|
|
td.TradePrice = Convert.ToDouble(position.PosiTradingFee);
|
|
td.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType;
|
|
position.PosiNotionalValue = position.PosiQuantity * position.ContractSize;
|
|
if (realPosition!=null)
|
|
{
|
|
realPosition.UnderlyingCode = underlying.UnderlyingCode;
|
|
realPosition.UnderlyingInstrumentType= underlying.UnderlyingInstrumentType;
|
|
realPosition.PositionType = position.PositionType;
|
|
realPosition.PosiQuantity= position.PosiQuantity;
|
|
realPosition.PosiGrossPrice = position.PosiGrossPrice;
|
|
realPosition.PosiNetPrice = position.PosiNetPrice;
|
|
realPosition.PosiNotionalValue = position.PosiNotionalValue;
|
|
}
|
|
|
|
var flowEvent = DbContext.swap_flow_event.FirstOrDefault(x => x.PositionId == position.id &&x.EventType==(int)SwapFlowEventTypeEnum.开仓 && x.DataState == (int)SwapFlowDateStateEnum.完成);
|
|
if (flowEvent!=null)
|
|
{
|
|
flowEvent.PositionQty = position.PosiQuantity;
|
|
flowEvent.UnderlyingCode = position.UnderlyingCode;
|
|
flowEvent.UnderlyingInstrumentType= position.UnderlyingInstrumentType;
|
|
flowEvent.PositionType= position.PositionType;
|
|
flowEvent.Quantity= position.PosiQuantity;
|
|
flowEvent.TradingAmount= position.PosiNotionalValue;
|
|
}
|
|
DbContext.SaveChanges();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 修改衡泰平仓明细价格
|
|
/// </summary>
|
|
/// <param name="price"></param>
|
|
/// <param name="td"></param>
|
|
/// <param name="valueDate"></param>
|
|
private string DealUnwindPosiEvent(decimal price,trade td,DateTime valueDate)
|
|
{
|
|
var posi = DbContext.swap_position.Where(x=>!x.Invalid&&x.IsInitial&&x.PosiDirection>0).FirstOrDefault();
|
|
if (posi==null)
|
|
{
|
|
throw new Exception("找不到资产端信息");
|
|
}
|
|
var flowEvent = GetSwapFlowEvent(posi.id,valueDate);
|
|
if (flowEvent==null)
|
|
{
|
|
throw new Exception("找不到资产端平仓事件明细信息");
|
|
}
|
|
flowEvent.TradingAmountAvg = price;
|
|
int tradeSide = posi.PositionType == (int)EnumDirection.Long ? 1 : -1;
|
|
flowEvent.TradingAmountFeeAvg = flowEvent.Quantity == 0 ? flowEvent.TradingAmountAvg : flowEvent.TradingAmountAvg + flowEvent.TradingFeePending * tradeSide / flowEvent.Quantity;
|
|
flowEvent.TradingAmountNetFeeAvg = flowEvent.Quantity == 0 ? flowEvent.TradingAmountNetAvg : flowEvent.TradingAmountNetAvg + flowEvent.TradingFeePending * tradeSide / flowEvent.Quantity;
|
|
DbContext.SaveChanges();
|
|
return "";
|
|
}
|
|
/// <summary>
|
|
/// 获取平仓明细
|
|
/// </summary>
|
|
/// <param name="posiId"></param>
|
|
/// <param name="valueDate"></param>
|
|
/// <param name="attempts"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="Exception"></exception>
|
|
private swap_flow_event GetSwapFlowEvent(long posiId,DateTime valueDate,int attempts=0)
|
|
{
|
|
var flowEvent = DbContext.swap_flow_event.FirstOrDefault(x=>x.PositionId== posiId&&x.EventDate==valueDate&&x.EventReason== "接口合约终止交易"&&x.DataState==(int)SwapFlowDateStateEnum.完成);
|
|
if (flowEvent==null)
|
|
{
|
|
attempts++;
|
|
if (attempts>10)
|
|
{
|
|
return null;
|
|
}
|
|
Thread.Sleep(1000* attempts);
|
|
return GetSwapFlowEvent(posiId, valueDate, attempts);
|
|
}
|
|
else
|
|
{
|
|
return flowEvent;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 接入对冲标的
|
|
/// </summary>
|
|
/// <param name="swapPosiReq"></param>
|
|
private void PrepareExchangeTradeData(SwapPosiReq swapPosiReq,string tradeSource)
|
|
{
|
|
var underlying = CheckUnderlying(swapPosiReq.TRADEINFO.I_CODE, swapPosiReq.TRADEINFO.MARKET);
|
|
var orddDate = TryFormatDate(swapPosiReq.BASEINFO.ORDDATE);
|
|
var longType = swapPosiReq.BASEINFO.TRDTYPE == "10" ? "多头" : "空头";
|
|
var openType = swapPosiReq.TRADEINFO.OCFLAG == "0" ? "开仓" : "平仓";
|
|
|
|
if (string.IsNullOrEmpty(swapPosiReq.BASEINFO.TRD_RELATED_NO) || !double.TryParse(swapPosiReq.TRADEINFO.PARVALUE, out _) || !double.TryParse(swapPosiReq.TRADEINFO.ORDPRICE, out _))
|
|
{
|
|
logger.Error("同步场内交易失败,数据格式不正确:" + JsonHelper.Serialize(swapPosiReq));
|
|
return;
|
|
}
|
|
|
|
ExchangeTradeSaveApiReq req = new ExchangeTradeSaveApiReq
|
|
{
|
|
ExchangeAccountCode = swapPosiReq.BASEINFO.SECU_INT,
|
|
BuySell = longType + openType,
|
|
Commission = 0,
|
|
IsHistory = orddDate.Date < valuedateBLL.ValueDate ? true : false,
|
|
Lots = double.Parse(swapPosiReq.TRADEINFO.PARVALUE) / underlying.ContractSize,
|
|
TradeDate = orddDate.Date,
|
|
TradeNumber = swapPosiReq.BASEINFO.TRD_RELATED_NO,
|
|
TradePrice = double.Parse(swapPosiReq.TRADEINFO.ORDPRICE) / (underlying.Price ?? 100),
|
|
TradeType = underlying.UnderlyingInstrumentTypeCn,
|
|
UnderlyingCode = underlying.UnderlyingCode
|
|
};
|
|
var service = new ExchangeTradeSyncApiService(UserInfo);
|
|
service.Save(req, tradeSource);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 接入对冲标的
|
|
/// </summary>
|
|
/// <param name="swapPosiReq"></param>
|
|
private void PrepareExchangeTradeData(SwapAsset swapPosiReq,string tradeSource)
|
|
{
|
|
var underlying = CheckUnderlying(swapPosiReq.I_CODE, swapPosiReq.MARKET);
|
|
var orddDate = TryFormatDate(swapPosiReq.ORD_DATE);
|
|
var longType = swapPosiReq.TRD_TYPE == "10" ? "多头" : "空头";
|
|
var openType = swapPosiReq.OC_FLAG == "0" ? "开仓" : "平仓";
|
|
|
|
if (string.IsNullOrEmpty(swapPosiReq.EXT_NO) || !double.TryParse(swapPosiReq.PAR_VALUE, out _) || !double.TryParse(swapPosiReq.ORD_PRICE, out _))
|
|
{
|
|
logger.Error("同步场内交易失败,数据格式不正确:" + JsonHelper.Serialize(swapPosiReq));
|
|
return;
|
|
}
|
|
|
|
ExchangeTradeSaveApiReq req = new ExchangeTradeSaveApiReq
|
|
{
|
|
ExchangeAccountCode = swapPosiReq.SECU_INT,
|
|
BuySell = longType + openType,
|
|
Commission = 0,
|
|
IsHistory = orddDate.Date < valuedateBLL.ValueDate ? true : false,
|
|
Lots = double.Parse(swapPosiReq.PAR_VALUE) / underlying.ContractSize,
|
|
TradeDate = orddDate.Date,
|
|
TradeNumber = swapPosiReq.EXT_NO,
|
|
TradePrice = double.Parse(swapPosiReq.ORD_PRICE) / (underlying.Price ?? 100),
|
|
TradeType = underlying.UnderlyingInstrumentTypeCn,
|
|
UnderlyingCode = underlying.UnderlyingCode
|
|
};
|
|
var service = new ExchangeTradeSyncApiService(UserInfo);
|
|
service.Save(req, tradeSource);
|
|
}
|
|
|
|
#endregion
|
|
#region 预付金交易回执相关私有方法
|
|
private void PrepareMarginData(SwapMarginReq swapMarginReq, trade td, string extNo)
|
|
{
|
|
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.PosiNumber == extNo).ToList();
|
|
var position = positions.FirstOrDefault(x => x.IsInitial);
|
|
var realPosition = positions.FirstOrDefault(x => !x.IsInitial);
|
|
if (position == null)
|
|
{
|
|
throw new Exception($"找不到持仓编码{extNo}的信息");
|
|
}
|
|
var marginDirection = swapMarginReq.BASEINFO.TRDTYPE == "128" ? (int)SwapDirectionEnum.收取 : (int)SwapDirectionEnum.支付;
|
|
position.InterestDirection = marginDirection;
|
|
position.InterestPrincipalFix = TryFormatDecimal(swapMarginReq.TRADEINFO.PARVALUE);
|
|
position.InterestRateDefault = TryFormatDecimal(swapMarginReq.RESERVE.RATE);
|
|
position.IsAnnualized = position.InterestRateDefault != 0;
|
|
position.Currency = swapMarginReq.RESERVE.CURRENCY;
|
|
IntervalModel intervalModel = new IntervalModel()
|
|
{
|
|
Date = td.ExerciseDate.Value,
|
|
Rate = position.InterestRateDefault,
|
|
Settlement = 0
|
|
};
|
|
List<IntervalModel> intervalModels = new List<IntervalModel>
|
|
{
|
|
intervalModel
|
|
};
|
|
var marginDateStr = swapMarginReq.BASEINFO.ORDDATE;
|
|
if (!string.IsNullOrEmpty(swapMarginReq.BASEINFO.SETDATE))
|
|
{
|
|
marginDateStr = swapMarginReq.BASEINFO.SETDATE;
|
|
}
|
|
var marginDate = TryFormatDate(marginDateStr);
|
|
position.HappenDate=marginDate;
|
|
position.InterestSwapInterval = JsonHelper.Serialize(intervalModels);
|
|
var observation = DbContext.trade_obervation.FirstOrDefault(x => x.PositionId == position.id);
|
|
if (observation == null)
|
|
{
|
|
observation = new TradeObervation()
|
|
{
|
|
PositionId = position.id,
|
|
IsDeductPrincipal = true,
|
|
ObservationAlignEnd = true,
|
|
ObservationNum = 1,
|
|
ObservationUnit = "D",
|
|
ObservationHolidayType = "Following",
|
|
ObservationStart = td.TradeDate
|
|
};
|
|
}
|
|
observation.ObservationInterval = position.InterestSwapInterval;
|
|
|
|
if (observation.id == 0)
|
|
{
|
|
DbContext.trade_obervation.Add(observation);
|
|
}
|
|
if (realPosition!=null)
|
|
{
|
|
realPosition.InterestDirection = position.InterestDirection;
|
|
realPosition.InterestPrincipalFix = position.InterestPrincipalFix;
|
|
realPosition.InterestRateDefault = position.InterestRateDefault;
|
|
realPosition.InterestSwapInterval = position.InterestSwapInterval;
|
|
realPosition.HappenDate = position.HappenDate;
|
|
realPosition.IsAnnualized = position.IsAnnualized;
|
|
realPosition.Currency = realPosition.Currency;
|
|
}
|
|
var flowEvent = DbContext.swap_flow_event.FirstOrDefault(x => x.PositionId == position.id && x.EventType == (int)SwapFlowEventTypeEnum.开仓&&x.DataState==(int)SwapFlowDateStateEnum.完成);
|
|
if (flowEvent != null)
|
|
{
|
|
flowEvent.InterestDirection = position.InterestDirection;
|
|
flowEvent.InterestPrincipal = position.InterestPrincipalFix;
|
|
flowEvent.InterestRate = position.InterestRateDefault;
|
|
flowEvent.InterestSwapInterval = position.InterestSwapInterval;
|
|
}
|
|
DbContext.SaveChanges();
|
|
// AfterUpdateDeal(td, (int)SwapEventTypeEnum.修改交易, $"接口预付金{msg}交易", true);
|
|
AddMarginCash(position, td);
|
|
}
|
|
private void PrepareUnwindMarginData(SwapMarginReq swapMarginReq, trade td)
|
|
{
|
|
var posi = DbContext.swap_position.Where(x => !x.Invalid && x.IsInitial && ConsTrade.InterestMarginModels.Contains(x.InterestMode)).FirstOrDefault();
|
|
if (posi == null)
|
|
{
|
|
throw new Exception($"{td.TradeNumber}找不到持仓信息");
|
|
}
|
|
var marginDate = TryFormatDate(swapMarginReq.BASEINFO.ORDDATE);
|
|
var flowEvent = GetSwapFlowEvent(posi.id, marginDate);
|
|
if (flowEvent == null)
|
|
{
|
|
throw new Exception($"{posi.PosiNumber}找不到平仓事件");
|
|
}
|
|
var swapEvent = DbContext.swap_event.Where(x=>x.id==flowEvent.EventId).FirstOrDefault();
|
|
if (swapEvent==null|| string.IsNullOrEmpty(swapEvent.EventData))
|
|
{
|
|
throw new Exception($"{td.TradeNumber}找不到平仓事件");
|
|
}
|
|
swapEvent.unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
|
|
decimal unwindMarginMoney = 0;
|
|
unwindMarginMoney = TryFormatDecimal(swapMarginReq.TRADEINFO.PARVALUE);
|
|
var interestAmount = TryFormatDecimal(swapMarginReq.TRADEINFO.INTEREST_AMOUNT);
|
|
var currentMarginMoney = posi.InterestPrincipalFix;
|
|
var origamount = currentMarginMoney * swapEvent.unwindData.ClosePercent;
|
|
var difMarginMoney = currentMarginMoney- (unwindMarginMoney> origamount? unwindMarginMoney: origamount);
|
|
posi.InterestPrincipalFix = difMarginMoney;
|
|
flowEvent.InterestPrincipal = unwindMarginMoney;
|
|
flowEvent.TdInterestAmount = interestAmount;
|
|
flowEvent.InterestAmount = interestAmount;
|
|
flowEvent.InterestClosePnL = interestAmount;
|
|
DbContext.SaveChanges();
|
|
UpdateUnwindMarginCash(posi, td, -unwindMarginMoney, marginDate, ClientCashInCashOut.系统操作_应付预付金);
|
|
new SwapTradeBaseService(this).AddClientCashInCashOut(td, Convert.ToDouble(-interestAmount), ClientCashInCashOut.系统操作_预付金返息, marginDate);
|
|
}
|
|
/// <summary>
|
|
/// 添加预付金资金记录
|
|
/// </summary>
|
|
/// <param name="marginPosition"></param>
|
|
/// <param name="td"></param>
|
|
private void AddMarginCash(swap_position marginPosition, trade td)
|
|
{
|
|
var cashService = new ClientCashInCashOutService(this);
|
|
var marginAmount = marginPosition.InterestPrincipalFix * (marginPosition.InterestDirection == 1 ? -1 : 1);
|
|
cashService.SaveSwapTradeClientCash(td, Convert.ToDouble(marginAmount), marginPosition.HappenDate.HasValue ? marginPosition.HappenDate.Value : td.TradeDate.Value, marginPosition.id, ClientCashInCashOut.系统操作_应付预付金);
|
|
}
|
|
/// <summary>
|
|
/// 修改预付金返息记录
|
|
/// </summary>
|
|
/// <param name="marginPosition"></param>
|
|
/// <param name="td"></param>
|
|
/// <param name="marginAmount"></param>
|
|
/// <param name="marginDate"></param>
|
|
private void UpdateUnwindMarginCash(swap_position marginPosition, trade td, decimal marginAmount, DateTime marginDate,string actionName)
|
|
{
|
|
var cashService = new ClientCashInCashOutService(this);
|
|
cashService.SaveSwapTradeClientCash(td, Convert.ToDouble(marginAmount), marginDate, marginPosition.id, actionName);
|
|
}
|
|
#endregion
|
|
#region 合约终止回执相关私有方法
|
|
private void PrepareUnwindData(SwapUnwindReq swapUnwindReq, trade td)
|
|
{
|
|
var eventIdStr = swapUnwindReq.ACCTSWAP_TERMINATE.EXT_NO;
|
|
bool allClose = swapUnwindReq.ACCTSWAP_TERMINATE.TERMINATE_TYPE == "1";
|
|
var closeQty = TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.TERMINATE_COUNT);
|
|
var unwindDate = TryFormatDate(swapUnwindReq.ACCTSWAP_TERMINATE.TERMINATE_DAY).Date;
|
|
var payDate = TryFormatDate(swapUnwindReq.ACCTSWAP_TERMINATE.PAY_DAY).Date;
|
|
var markClosePnl = TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.ZCD_AMOUNT);
|
|
var interestAmount= TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.GDD_AMOUNT);
|
|
var tradeinfFee = TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.ASSET_FEE);
|
|
var fixFee = TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.FIX_FEE);
|
|
interestAmount -= fixFee;
|
|
markClosePnl += tradeinfFee;
|
|
swap_event swapEvent;
|
|
if (!long.TryParse(eventIdStr, out long eventId))//不识别,为衡泰新增的平仓信息
|
|
{
|
|
swapEvent = null;
|
|
}
|
|
else
|
|
{
|
|
swapEvent = DbContext.swap_event.FirstOrDefault(x => x.id == eventId);
|
|
}
|
|
// 先不支持衡泰修改平仓信息
|
|
if (swapEvent == null&& OtcAppConfigHelper.HTUpdate == 1&&td.TradeStatus!= ConsTrade.已平仓)
|
|
{
|
|
new SwapDealService(this).AutoSwapUnwindFromConsumer(td, unwindDate, payDate, markClosePnl, tradeinfFee, interestAmount, fixFee, closeQty, allClose);
|
|
}
|
|
}
|
|
#endregion
|
|
#region 互换资金流水回执相关私有方法
|
|
private List<int> PrepareTradeCashData(SwapCashReq swapCashReq, List<trade> trades)
|
|
{
|
|
var tradenumbers = trades.Select(s => s.TradeNumber).ToList();
|
|
List<int> clientIds = new List<int>();
|
|
foreach (var cashItem in swapCashReq.ACCTSWAP_CASH_LIST)
|
|
{
|
|
var money = cashItem.CASH_AMOUNT;
|
|
var backCard = cashItem.BANK_ACC_CODE;
|
|
var bankCardInfo = new ClientBankCardService(UserInfo).GetBankCardByBankCode(backCard);
|
|
if (bankCardInfo == null)
|
|
{
|
|
throw new ServiceException("根据银行卡信息找不到对应客户");
|
|
}
|
|
var trade = trades.FirstOrDefault(x => x.TradeNumber == cashItem.EXT_NO);
|
|
var client = DataCacheProvider.GetClientDataSource().GetData(bankCardInfo.ClientId ?? 0);
|
|
var cash = new ClientCashInCashOut();
|
|
cash.ClientId = bankCardInfo.ClientId;
|
|
cash.ClientName = client?.Name;
|
|
cash.ClientNumber = client?.Number;
|
|
cash.Direction = money > 0 ? "入金" : "出金";
|
|
cash.Money = Math.Abs(money);
|
|
cash.TradeId = trade?.id;
|
|
cash.TradeNumber = trade?.TradeNumber;
|
|
cash.HappenDate = TryFormatDate(cashItem.SETDATE_REAL).Date;
|
|
cash.OptDate = TryFormatDate(cashItem.SETDATE);
|
|
cash.CurrencyCode = "CNY";
|
|
cash.Number = UniqueTimeId.GetStr();
|
|
cash.State = "未确认";
|
|
cash.SetOpt(UserInfo);
|
|
cash.Comments = "互换资金流水回执";
|
|
cash.OpenBankCard = backCard;
|
|
cash.OpenBankId = bankCardInfo?.id;
|
|
DbContext.ClientCashInCashOut.Add(cash);
|
|
if (client?.SwapTradeType == 1)
|
|
{
|
|
clientIds.Add(cash.ClientId ?? 0);
|
|
}
|
|
}
|
|
DbContext.SaveChanges();
|
|
return clientIds.Distinct().ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 推送客户资金信息到客户端
|
|
/// </summary>
|
|
/// <param name="clientIds"></param>
|
|
/// <param name="onRspAccountCapitalTopicTopic"></param>
|
|
private void PushClientCash(List<int> clientIds, string onRspAccountCapitalTopicTopic)
|
|
{
|
|
try
|
|
{
|
|
var clientSettles = new RealTimeClientBanlanceService(new OptUserInfo(0, "实时客户资金服务", OptUserFrom.Service)).GetBanlances(clientIds, valuedateBLL.ValueDate);
|
|
foreach (var cb in clientSettles)
|
|
{
|
|
Result result = new Result();
|
|
try
|
|
{
|
|
var obj = new ClientBalanceForTrsResponse
|
|
{
|
|
TotalAmountTotal = cb.RoundedTotalAmountTotal,
|
|
AvailableAmount = Math.Round(cb.AvailableAmount, 2),
|
|
PositionPv = cb.RoundedPositionPv,
|
|
PositionPnl = cb.RoundedPositionPnl,
|
|
DaliyPnl = Math.Round(cb.DaliyPnl, 2),
|
|
ClientId = cb.ClientId,
|
|
ClientType = cb.ClientType,
|
|
Credit = cb.TotalCredit
|
|
};
|
|
result.success = true;
|
|
result.obj = obj;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result.msg = ex.Message;
|
|
result.success = false;
|
|
}
|
|
kafkaProduceHelper.Produce(onRspAccountCapitalTopicTopic, JsonConvert.SerializeObject(result));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 出入金相关私有方法
|
|
public void PushCashToHT(int cashId, PushLog pushLog)
|
|
{
|
|
//var cash= DbContext.ClientCashInCashOut.FirstOrDefault(x=>x.id==cashId);
|
|
// if (cash==null)
|
|
// {
|
|
// return;
|
|
// }
|
|
// var req = new RequestBase<SwapMarginReq>();
|
|
// var pushService = new PushLogService(UserInfo);
|
|
// var enumType = cash.Direction=="出金"? SwapPushDataEnum.客户出金新增: SwapPushDataEnum.客户入金新增;
|
|
// if (pushLog==null)
|
|
// {
|
|
// pushLog = pushService.AddLog(cashId, enumType, cash.Number);
|
|
// }
|
|
// req.RECORD = PrepareTradeMarginReq(cash);
|
|
// if (req.RECORD == null)
|
|
// {
|
|
// return;
|
|
// }
|
|
// req.EXT_NO = cash.Number;
|
|
// req.SERIAL_NO = pushLog.id.ToString();
|
|
// req.ACTION = "2043";
|
|
// var data_req = JsonHelper.Serialize(req);
|
|
// pushService.UpdateLogReq(pushLog.id, data_req);
|
|
// kafkaProduceHelper.Produce(reqMarginInsert, data_req);
|
|
}
|
|
/// <summary>
|
|
/// 互换预付金请求数据组装
|
|
/// </summary>
|
|
/// <param name="model"></param>
|
|
/// <returns></returns>
|
|
private SwapMarginReq PrepareTradeMarginReq(ClientCashInCashOut model)
|
|
{
|
|
SwapMarginReq swapMarginReq = new SwapMarginReq();
|
|
var etradeAccount = DbContext.etrade_account.FirstOrDefault();
|
|
#region BASEINFO
|
|
swapMarginReq.BASEINFO.ORDDATE = model.HappenDate.Value.ToString("yyyy-MM-dd");
|
|
swapMarginReq.BASEINFO.SETDATE = swapMarginReq.BASEINFO.ORDDATE;
|
|
swapMarginReq.BASEINFO.PARTY_NAME = model.ClientName;
|
|
swapMarginReq.BASEINFO.ORDSTATUS = "1";
|
|
swapMarginReq.BASEINFO.TRDTYPE = model.Direction == "入金" ? "128" : "127";
|
|
swapMarginReq.BASEINFO.TRADER_ID = etradeAccount?.trader_account;
|
|
swapMarginReq.BASEINFO.SECU_INT = etradeAccount?.balance_account_trade;
|
|
#endregion
|
|
#region TRADEINFO
|
|
swapMarginReq.TRADEINFO.PARVALUE = model.Money.OtcFormatMoney(false, 4);
|
|
#endregion
|
|
return swapMarginReq;
|
|
}
|
|
#endregion
|
|
/// <summary>
|
|
/// 校验标的信息
|
|
/// </summary>
|
|
/// <param name="underlyingCode"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="ServiceException"></exception>
|
|
private underlying_manager CheckUnderlying(string underlyingCode, string market)
|
|
{
|
|
var underlying = DbContext.underlying_manager.FirstOrDefault(x => x.UnderlyingCode.StartsWith(underlyingCode + ".") && x.MarketCode == market);
|
|
if (underlying == null)
|
|
{
|
|
throw new ServiceException($"标的代码{underlyingCode}不存在");
|
|
}
|
|
return underlying;
|
|
}
|
|
private DateTime TryFormatDate(string val)
|
|
{
|
|
if (!DateTime.TryParse(val, out DateTime formatDate))
|
|
{
|
|
throw new ServiceException($"参数{val}不是正确的日期格式");
|
|
}
|
|
return formatDate;
|
|
}
|
|
private double TryFormatDouble(string val)
|
|
{
|
|
if (!double.TryParse(val, out double doubleVal))
|
|
{
|
|
throw new ServiceException($"参数{val}不是数字");
|
|
}
|
|
return doubleVal;
|
|
}
|
|
private decimal TryFormatDecimal(string val)
|
|
{
|
|
if (!decimal.TryParse(val, out decimal decimalVal))
|
|
{
|
|
throw new ServiceException($"参数{val}不是数字");
|
|
}
|
|
return decimalVal;
|
|
}
|
|
private int TryFormatInt(string val)
|
|
{
|
|
if (!int.TryParse(val, out int intVal))
|
|
{
|
|
throw new ServiceException($"参数{val}不是数字");
|
|
}
|
|
return intVal;
|
|
}
|
|
private string GetInterestRule(string interestRule)
|
|
{
|
|
switch (interestRule)
|
|
{
|
|
case "1":
|
|
return "01";
|
|
case "2":
|
|
return "11";
|
|
default:
|
|
return "10";
|
|
}
|
|
}
|
|
|
|
private string GetTradeStatus(string orderStatus)
|
|
{
|
|
switch (orderStatus)
|
|
{
|
|
//0新建、-4交易执行中、-3风险预审中、7成交确认、9交易撤单、1审批中、5审批通过、4审批拒绝、10交易终止
|
|
case "0":
|
|
return ConsTrade.新增待确认;
|
|
case "1":
|
|
return ConsTrade.审批中;
|
|
case "4":
|
|
case "9"://撤单
|
|
return "";
|
|
case "10":
|
|
return ConsTrade.已平仓;
|
|
default:
|
|
return ConsTrade.确认成交;
|
|
}
|
|
}
|
|
}
|
|
}
|