可用资金 从山证搬迁

This commit is contained in:
吴方海
2024-07-16 14:43:18 +08:00
parent 94f6bb6361
commit 8f377a4ca3
9 changed files with 598 additions and 6 deletions
+257
View File
@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
namespace YLErp.DBModels
{
[Table("client_order")]
public class ClientOrder
{
/// <summary>
/// 主键
/// </summary>
[Key]
public long id { get; set; }
/// <summary>
/// 委托编号
/// </summary>
public string order_sno { get; set; }
/// <summary>
/// 序号
/// </summary>
public string num { get; set; }
/// <summary>
/// STC订单编号
/// </summary>
public string stc_order_id { get; set; }
/// <summary>
/// 第三方订单编号
/// </summary>
public string third_party_order_id { get; set; }
/// <summary>
/// 客户机构编码
/// </summary>
public int? client_id { get; set; }
/// <summary>
/// 客户机构名称
/// </summary>
public string client_name { get; set; }
/// <summary>
/// 客户编号
/// </summary>
public long? client_user_id { get; set; }
/// <summary>
/// 客户名称
/// </summary>
public string client_user_name { get; set; }
/// <summary>
/// 债券代码
/// </summary>
public string security_id { get; set; }
/// <summary>
/// 债券简称
/// </summary>
public string symbol { get; set; }
/// <summary>
/// 债券规模
/// </summary>
public string bond_size { get; set; }
/// <summary>
/// 债券发行价格
/// </summary>
public decimal? issue_price { get; set; }
/// <summary>
/// 合约期限(对应债券期限,例如:10Y)
/// </summary>
public string contract_period { get; set; }
/// <summary>
/// 结构类型(1:收益互换)
/// </summary>
public int? struct_type { get; set; }
/// <summary>
/// 当前操作(0:无操作,1:委托拒绝,2:合约成交,3:客户撤单,4:对冲撤单,5:确认对冲,6:自动撤单)
/// </summary>
public int? current_operation { get; set; }
/// <summary>
/// 订单状态(-2:意向成交,-1:待意向确认,0:已报,1:部分成交,2:全部成交,4:已撤单,6:撤单中,8:已拒绝,9:自动确认,10:下单失败)
/// </summary>
public int? status { get; set; }
/// <summary>
/// 操作前订单状态(-2:意向成交,-1:待意向确认,0:已报,1:部分成交,2:全部成交,4:已撤单,6:撤单中,8:已拒绝,9:自动确认,10:下单失败)
/// </summary>
public int? before_status { get; set; }
/// <summary>
/// 是否创建对冲(0:未创建,1:已创建)
/// </summary>
public int? is_create_hedge_order { get; set; }
/// <summary>
/// 对冲状态(0:已报,1:全部对冲,2:未完全对冲)
/// </summary>
public int? hedge_status { get; set; }
/// <summary>
/// 对冲单撤单状态(-1:未创建对冲单,1:未撤单,2:已撤单,3:已拒绝,4:下单失败)
/// </summary>
public int? hedge_cancel_status { get; set; }
/// <summary>
/// 交易方向(0:买入,1:卖出)
/// </summary>
public int? side { get; set; }
/// <summary>
/// 确认类型(0:自动确认,1:交易员手动确认)
/// </summary>
public int? confirm_type { get; set; }
/// <summary>
/// 委托收益率
/// </summary>
public decimal? entrust_yield { get; set; }
/// <summary>
/// 委托净价
/// </summary>
public decimal? price { get; set; }
/// <summary>
/// 委托净价(含费)
/// </summary>
public decimal? price_include_fee { get; set; }
/// <summary>
/// 委托全价
/// </summary>
public decimal? full_price { get; set; }
/// <summary>
/// 委托全价(含费)
/// </summary>
public decimal? full_price_include_fee { get; set; }
/// <summary>
/// 委托数量类型:1:整量:,2:非整量
/// </summary>
public int? order_qty_type { get; set; }
/// <summary>
/// 委托数量(万)
/// </summary>
public decimal? order_qty { get; set; }
/// <summary>
/// 成交数量(万)
/// </summary>
public decimal? last_shares { get; set; }
/// <summary>
/// 对冲成交数量(万)
/// </summary>
public decimal? hedge_deal_number { get; set; }
/// <summary>
/// 累计成交
/// </summary>
public decimal? total_traded { get; set; }
/// <summary>
/// 成交收益率
/// </summary>
public decimal? ytm { get; set; }
/// <summary>
/// 成交净价
/// </summary>
public decimal? deal_price { get; set; }
/// <summary>
/// 成交净价(含费)
/// </summary>
public decimal? deal_price_include_fee { get; set; }
/// <summary>
/// 成交全价
/// </summary>
public decimal? deal_full_price { get; set; }
/// <summary>
/// 成交全价(含费)
/// </summary>
public decimal? deal_full_price_include_fee { get; set; }
/// <summary>
/// 撤单数量(万)
/// </summary>
public decimal? cancel_qty { get; set; }
/// <summary>
/// 清算时间(0T+0,1:T+1,2:T+2,远期:具体年月日)
/// </summary>
public string settl_type { get; set; }
/// <summary>
/// 清算日期
/// </summary>
public string settl_date { get; set; }
/// <summary>
/// 剩余期限
/// </summary>
public string remaining_term { get; set; }
/// <summary>
/// 债券票面利率类型(501001000:浮动利率,501002000:固定利率,501003000:累进利率)
/// </summary>
public string coupon_type { get; set; }
/// <summary>
/// 票面利率
/// </summary>
public decimal? coupon_rate { get; set; }
/// <summary>
/// 开平标志
/// </summary>
public int? open_close { get; set; }
/// <summary>
/// 币种
/// </summary>
public string currency { get; set; }
/// <summary>
/// 交易费率类型(0:百分比,1:单价固定交易费)
/// </summary>
public int? commission_rate_type { get; set; }
/// <summary>
/// 交易费率
/// </summary>
public decimal? commission_rate { get; set; }
/// <summary>
/// 交易费用
/// </summary>
public decimal? commission { get; set; }
/// <summary>
/// 远期
/// </summary>
public string future_date { get; set; }
/// <summary>
/// STC下单响应结果
/// </summary>
public string stc_order_result { get; set; }
/// <summary>
/// 订单来源(0:TRS客户端,1:TRS交易端,2:第三方平台,3:成交补录)
/// </summary>
public byte? order_source { get; set; }
/// <summary>
/// 交易员ID
/// </summary>
public long? trader_id { get; set; }
/// <summary>
/// 委托时间
/// </summary>
public DateTime? create_time { get; set; }
/// <summary>
/// 创建人
/// </summary>
public long? create_user { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? update_time { get; set; }
/// <summary>
/// 更新人
/// </summary>
public long? update_user { get; set; }
/// <summary>
/// 限额告警成交备注
/// </summary>
public string limit_alert_remark { get; set; }
/// <summary>
/// QT信息主键ID
/// </summary>
public long? qt_msg_primary_id { get; set; }
/// <summary>
/// 客户机构简称(客户简称)
/// </summary>
public string abbreviation { get; set; }
}
}
@@ -59,7 +59,10 @@ namespace YLErp.BLL.Eod
using var clientDb = new ClientDBContext();
var dmaClients = clientDb.client.ToList();
var clientIds = dmaClients.Select(s => s.id);
return GetBanlances(clientIds, valuedateBLL.ValueDate, calcDate: valuedateBLL.ValueDate);
var valuedate = valuedateBLL.ValueDate;
//获取根据系统时间
var lastBalanceDate = EodOperationBase.GetLastSettlementDate(valuedate);
return GetBanlances(clientIds, lastBalanceDate.AddDays(1), calcDate: valuedateBLL.ValueDate);
}
public IEnumerable<ClientSettleBalance> GetBanlances(IEnumerable<int> clientIds, DateTime startDate, DateTime? endDate = null, DateTime? calcDate = null)
{
@@ -199,6 +202,7 @@ namespace YLErp.BLL.Eod
//获取当日所有执行的交易 交易日为当前交易日或者行权日为当前交易日
ProcessClientPosition();
ProcessClientSwap(lastSettletDate, calcDate.Value);
ProcessClientFrozen(calcDate.Value, new List<swap_flow>(), _clientBalanceDic);
//if (PS.Config.IsGuoJun)//获取当日国君互换持仓
//{
// endDate = endDate.HasValue ? endDate.Value : startDate;
@@ -1430,7 +1434,148 @@ namespace YLErp.BLL.Eod
}
}
class ClientBalanceEx : ClientSettleBalance
/// <summary>
/// 计算客户的冻结资金
/// </summary>
/// <param name="startDate"></param>
public void ProcessClientFrozen(DateTime startDate, List<swap_flow> bondFlows, Dictionary<int, ClientBalanceEx> clientBalanceDic)
{
var clientIds = clientBalanceDic.Keys.ToList();
// step1 获取当天未簿记流水
var flows = DbContext.swap_flow.Where(x => x.OccurTime == startDate && x.DataState == (int)SwapFlowDateStateEnum. && clientIds.Contains(x.ClientId ?? 0)).AsNoTracking().ToList();
// step2 获取客户端当天已报,意向待确认,部分成交委托订单
using var bondDb = new BondOmsDBContext();
var nextDate = startDate.AddDays(1);
List<int> calcStatus = new List<int>() { -1, 0, 1 };
var clientOrder = bondDb.client_order.Where(x => x.create_time > startDate && x.create_time < nextDate && calcStatus.Contains(x.status ?? 0) && clientIds.Contains(x.client_id ?? 0)).AsNoTracking().ToList();
// 将clientOrder转换为swap_flow,方便合并
clientOrder.ForEach(item =>
{
swap_flow flow = new swap_flow()
{
BsType = (item.side ?? 0) + 1,
UnderlyingCode = item.security_id,
ClientId = item.client_id ?? 0,
ClientName = item.client_name,
TradingQty = (item.order_qty ?? 0) - (item.last_shares ?? 0)
};
// clientOrder中数量单位为万
flow.TradingQty *= 10000;
bondFlows.Add(flow);
});
flows.AddRange(bondFlows);
// step3 从swap_position获取已簿记原持仓数据
var positionQuery = from t in DbContext.trade.Where(x => clientIds.Contains(x.ClientId) && x.TradeType == "收益互换" && x.ValidState != ConsGlobal.InValid && ConsTrade.PositionTradeStatusList.Contains(x.TradeStatus))
join p in DbContext.swap_position on t.id equals p.SwapTradeId
where !p.IsInitial && !p.Invalid
select new
{
t.ClientId,
p.PosiDirection,
p.PosiQuantity,
p.UnderlyingCode,
p.PositionType
};
var positions = positionQuery.ToList();
// step4 将未簿记持仓与已有持仓分别 按客户,标的分组计算各自轧差名义本金
// 取设置的预付金比例
var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.ValueDate <= startDate).OrderByDescending(o => o.ValueDate).AsNoTracking().ToList();
var flowGroup = flows.GroupBy(s => new { s.ClientId, s.UnderlyingCode });
foreach (var itemGroup in flowGroup)
{
var balance = clientBalanceDic[itemGroup.Key.ClientId ?? 0];
string marginType = balance.ClientType == 1 ? "多空组合" : "品种";
// 获取客户预付金比例设置
var clientMarginTemplate = clientMarginTemplates.FirstOrDefault(x => x.ClientId == itemGroup.Key.ClientId && x.Type == marginType);
if (clientMarginTemplate == null)
{
clientMarginTemplate = clientMarginTemplates.FirstOrDefault(x => x.ClientId == 0 && x.Type == marginType);
}
// 没有设置预付金比例则跳过
if (clientMarginTemplate == null)
{
continue;
}
// 获取当前客户当前标的持仓数据,合并后的名义本金数量
var positionLsit = positions.Where(x => x.UnderlyingCode == itemGroup.Key.UnderlyingCode && x.ClientId == itemGroup.Key.ClientId).ToList();
var positionNotional = positionLsit.Sum(s => s.PosiQuantity * (s.PositionType == (int)PositionTypeFlag.Long ? 1 : -1));
//获取虚拟持仓合并后的名义本金数量
var virtualNotional = itemGroup.Sum(s => s.TradingQty * (s.BsType == (int)PositionTypeFlag.Long ? 1 : -1));
decimal money = CalcFrozanMarginMoney(positionNotional, virtualNotional, balance.ClientType == 1, clientMarginTemplate);
balance.FrozenMarginMoney += Convert.ToDouble(money);
}
}
/// <summary>
/// 计算冻结的预付金变化
/// </summary>
/// <param name="positionNotional">持仓名义本金</param>
/// <param name="virtualNotional">虚拟持仓名义本金</param>
/// <param name="dma">是否定义文件类型客户</param>
/// <param name="clientMarginTemplate">预付金设置信息</param>
/// <returns></returns>
private decimal CalcFrozanMarginMoney(decimal positionNotional, decimal virtualNotional, bool dma, client_marginrate clientMarginTemplate)
{
decimal money = 0;
var virtualPosi = positionNotional + virtualNotional;
var posiType = positionNotional >= 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
var virtualPosiType = virtualPosi >= 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
var positionNotionalAbs = Math.Abs(positionNotional);
var virtualNotionalAbs = Math.Abs(virtualNotional);
decimal longMarginRate = 0;
decimal shortMarginRate = 0;
//普通客户
if (!dma)
{
longMarginRate = Convert.ToDecimal(clientMarginTemplate.InitMarginRate);
shortMarginRate = longMarginRate;
}
else //定义文件类型客户
{
longMarginRate = Convert.ToDecimal(clientMarginTemplate.LongInitMarginRate);
shortMarginRate = Convert.ToDecimal(clientMarginTemplate.ShortInitMarginRate);
}
// 当前持仓多头
if (posiType == (int)PositionTypeFlag.Long)
{
if (virtualPosiType == (int)PositionTypeFlag.Long)
{
money = virtualNotional * longMarginRate;
}
else //虚拟持仓空头
{
if (virtualNotionalAbs <= positionNotionalAbs)
{
money = virtualNotional * longMarginRate;
}
else
{
money = (-virtualNotional * shortMarginRate) - positionNotional * (longMarginRate + shortMarginRate);
}
}
}
// 当前持仓空头
else
{
if (virtualPosiType == (int)PositionTypeFlag.Short)
{
money = (-virtualNotional) * shortMarginRate;
}
else //虚拟持仓多头
{
if (virtualNotionalAbs <= positionNotionalAbs)
{
money = (-virtualNotional) * shortMarginRate;
}
else
{
money = (virtualNotional + positionNotional) * longMarginRate + positionNotional * shortMarginRate;
}
}
}
return money;
}
public class ClientBalanceEx : ClientSettleBalance
{
public double InFundOther { get; set; }
+1
View File
@@ -14,5 +14,6 @@ namespace YLErp.DataBase
}
public DbSet<ClientDeal> client_deal { get; set; }
public DbSet<ClientPosition> client_position { get; set; }
public DbSet<ClientOrder> client_order { get; set; }
}
}
@@ -38,5 +38,66 @@ namespace YLErp.Model
/// 授信额度
/// </summary>
public double Credit { get; set; }
/// <summary>
/// 期初结存
/// </summary>
public double LastDayRemainFund { get; set; }
/// <summary>
/// 出金入金
/// </summary>
public double NetFund { get; set; }
/// <summary>
/// 互换成交收支
/// </summary>
public double OptionPremiumSwap { get; set; }
/// <summary>
/// 互换了结收支
/// </summary>
public double SwapBalance { get; set; }
/// <summary>
/// 其他收支
/// </summary>
public double OtherFund { get; set; }
/// <summary>
/// 期末结存
/// </summary>
public double AmountFund { get; set; }
/// <summary>
/// 质押市值
/// </summary>
public double GuaranteesTotalAmount { get; set; }
/// <summary>
/// 预付金占用
/// </summary>
public double MinusPayableMarginTotal { get; set; }
/// <summary>
/// 应付了结交易款
/// </summary>
public double ClosedTradePayableFundTotal { get; set; }
/// <summary>
/// 应付存续交易款
/// </summary>
public double PositionTradePayableFundTotal { get; set; }
/// <summary>
/// 追保金额
/// </summary>
public double MarginByPayableMarginTotal { get; set; }
/// <summary>
/// 应付资金总额
/// </summary>
public double PayableFundTotal { get; set; }
/// <summary>
/// 可取资金
/// </summary>
public double DesirableFundTotal { get; set; }
/// <summary>
/// 实现盈亏
/// </summary>
public double WinLoss { get; set; }
/// <summary>
/// 实际可用资金
/// </summary>
public double AvailableMoney { get; set; }
}
}
+31
View File
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace YLErp.Model
{
/// <summary>
/// trs下单假成交交易计算可用资金请求
/// </summary>
public class TradeClientCashCalcReq
{
/// <summary>
/// 标的代码
/// </summary>
public string underlyingCode { get; set; }
/// <summary>
/// 客户id
/// </summary>
public int clientId { get; set; }
/// <summary>
/// 交易数量
/// </summary>
public decimal tradingQty { get; set; }
/// <summary>
/// 买卖方向
/// </summary>
public int side { get; set; }
}
}
+18
View File
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace YLErp.Model
{
/// <summary>
/// trs下单假成交交易计算可用资金返回
/// </summary>
public class TradeClientCashCalcResp
{
public int clientId { get; set; }
public double availableAmount { get;set; }
}
}
@@ -35,7 +35,7 @@
}
//用户所有未确认出金信息
ProcessClientCashInCashOut(valueDate);
// ProcessClientCashInCashOut(valueDate);
//用户所有赎回申请抵押品
ProcessClientCashInOutProduct(valueDate);
+11 -3
View File
@@ -12,6 +12,7 @@ namespace YLErp.Web.App
private readonly IYcLogger _logger;
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private KafkaConsumerHelper _kafkaConsumer;
private IYLCache _yLCache;
private IKafkaProduce kafkaProduceHelper;
private string onRspAccountCapitalTopicTopic = string.Empty;
public ClientBalanceTask(IKafkaProduce kafkaProduce, IYLCache yLCache)
@@ -68,8 +69,10 @@ namespace YLErp.Web.App
Result result = new Result();
try
{
var cb = ClientAssetDataService.GetClientLatestBalance(null, lastBalanceDate, req.clientId, false, false, false);
cb.AvailableAmount = cb.AmountFund + cb.TotalCredit + cb.PayableMargin + cb.GuaranteesTotalAmount;
var cbs = new RealTimeClientBanlanceService(new OptUserInfo(0, "实时客户资金服务", OptUserFrom.Service)).GetBanlances(new List<int>() { req.clientId }, lastBalanceDate.AddDays(1), calcDate: valuedateBLL.ValueDate);
var cb = cbs.FirstOrDefault();
var AvailableMoney = cb.AmountFund + cb.TotalCredit + cb.PayableMargin + cb.GuaranteesTotalAmount;
cb.AvailableAmount = AvailableMoney - cb.FrozenMarginMoney;
var obj = new ClientBalanceForTrsResponse
{
TotalAmountTotal = cb.RoundedTotalAmountTotal,
@@ -79,10 +82,15 @@ namespace YLErp.Web.App
DaliyPnl = Math.Round(cb.DaliyPnl, 2),
ClientId = req.clientId,
ClientType = cb.ClientType,
Credit=cb.TotalCredit
Credit = cb.TotalCredit,
AvailableMoney = AvailableMoney
};
result.success = true;
result.obj = obj;
if (_yLCache != null)
{
_yLCache.StringSet<ClientBalanceForTrsResponse>("ClientBalance:" + cb.ClientId, obj);
}
}
catch (Exception ex)
{
@@ -2,6 +2,8 @@
using iTextSharp.text.pdf;
using Microsoft.AspNetCore.Authorization;
using System.Text;
using YLErp.BLL.Eod;
using YLErp.Cache;
using YLErp.DBModels;
using YLErp.DBModels.Consts;
using YLErp.Enums;
@@ -11,11 +13,17 @@ using YLErp.Modules.EodModule;
using YLErp.Modules.TradeModule.DocGenerateModule;
using YLErp.Modules.TradeModule.QueryModule;
using YLErp.Modules.TradeModule.SwapModule;
using static YLErp.BLL.Eod.RealTimeClientBanlanceService;
namespace YLErp.Web.Controllers
{
public class trade_spanController : BaseController
{
IYLCache _yLCache;
public trade_spanController(IYLCache yLCache)
{
_yLCache = yLCache;
}
public JsonResult GeneratePaymentDoc(List<int> tradeIds, DateTime? valueDate)
{
if (tradeIds == null || tradeIds.Count == 0)
@@ -139,6 +147,69 @@ namespace YLErp.Web.Controllers
};
return JsonSuccess("", obj);
}
/// <summary>
/// trs计算客户下单后可用资金
/// </summary>
/// <param name="calcReqs"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
public JsonResult CalcClientTradeAvailableAmount([FromBody] List<TradeClientCashCalcReq> calcReqs)
{
if (calcReqs.Count() == 0)
{
return JsonError("缺少参数");
}
var clientIds = calcReqs.Select(s => s.clientId).ToList();
Dictionary<int, ClientBalanceEx> clientBalanceDic = new Dictionary<int, ClientBalanceEx>();
foreach (var clientId in clientIds)
{
var client = DataCacheProvider.GetClientDataSource().GetData(clientId);
if (client == null)
{
continue;
}
clientBalanceDic[clientId] = new ClientBalanceEx
{
ClientId = clientId,
IsTradeCredit = client?.IsTradeCredit == 1,
MarginOptionType = client?.MarginOptionType,
CreditCanApplySwap = client.creditCanApplySwap,
TotalCreditStockEqvNotional = double.NaN,
SettlementCurrency = client?.SettlementCurrency,
ClientType = client.SwapTradeType ?? 0,
ClientName = client.Name
};
}
List<swap_flow> bondFlows = new List<swap_flow>();
foreach (var item in calcReqs)
{
swap_flow flow = new swap_flow()
{
BsType = item.side + 1,
UnderlyingCode = item.underlyingCode,
ClientId = item.clientId,
TradingQty = item.tradingQty
};
bondFlows.Add(flow);
}
new RealTimeClientBanlanceService(new OptUserInfo(0, "计算客户资金变化服务", OptUserFrom.Service)).ProcessClientFrozen(valuedateBLL.ValueDate, bondFlows, clientBalanceDic);
List<TradeClientCashCalcResp> list = new List<TradeClientCashCalcResp>();
foreach (var item in clientBalanceDic)
{
TradeClientCashCalcResp tradeClientCashCalc = new TradeClientCashCalcResp()
{
clientId = item.Key
};
var clientBalanceCache = _yLCache.StringGet<ClientBalanceForTrsResponse>("ClientBalance:" + item.Key);
tradeClientCashCalc.availableAmount = (clientBalanceCache?.AvailableMoney ?? 0) - item.Value.FrozenMarginMoney;
list.Add(tradeClientCashCalc);
}
return JsonSuccess("", list);
}
public Dictionary<string, object> clientBalanceQueryJson(TradeSpanReq req)
{
//按客户日期查询trade_span信息