1251 lines
72 KiB
C#
1251 lines
72 KiB
C#
using YLErp.BLL;
|
|
using YLErp.CustomizedBizLogic;
|
|
using YLErp.DBModels.Consts;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.Enums;
|
|
using YLErp.Model;
|
|
using YLErp.Model.Enum;
|
|
using YLErp.Models;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.Modules.TradeModule;
|
|
using YLErp.Modules.TradeModule.DealModule;
|
|
using YLErp.Modules.TradeModule.OrderModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.EodModule
|
|
{
|
|
/// <summary>
|
|
/// 日终结算服务
|
|
/// </summary>
|
|
public class EodHandleSwapFlowService : TradeServiceBase
|
|
{
|
|
public EodHandleSwapFlowService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
public void ComposeGroupTrade(string tradeNumber)
|
|
{
|
|
using (var trans = BeginTransaction())
|
|
{
|
|
var tradeNumbers = DbContext.trade_swap_flow.Where(x => x.TradeDate == valuedateBLL.ValueDate && x.TradeNumber != null && x.TradeNumber != "").Select(x => x.TradeNumber).Distinct().ToList();
|
|
if (!string.IsNullOrWhiteSpace(tradeNumber))
|
|
{
|
|
tradeNumbers = tradeNumbers.Where(x => x == tradeNumber).ToList();
|
|
}
|
|
if (tradeNumbers.Any())
|
|
{
|
|
var flows = DbContext.trade_swap_flow.Where(x => x.TradeDate == valuedateBLL.ValueDate && tradeNumbers.Contains(x.TradeNumber)).ToList();
|
|
tradeNumbers.ForEach(x =>
|
|
{
|
|
var hasNoPosition = true;
|
|
|
|
var originalTrade = DbContext.trade.FirstOrDefault(y => y.TradeNumber == x);
|
|
if (originalTrade == null)
|
|
{
|
|
throw new ServiceException($"未找到该交易编号:{x}");
|
|
}
|
|
else
|
|
{
|
|
var client = DataCacheProvider.GetClientDataSource().GetData(originalTrade.ClientId);
|
|
if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + ""))
|
|
{
|
|
throw new ServiceException($"客户:{client.Name}未设置交易种类“场外互换”,无法生成互换交易!");
|
|
}
|
|
|
|
var tradeFlows = flows.Where(y => y.TradeNumber == x).ToList();
|
|
|
|
#region 还原之前收盘产生的历史数据
|
|
|
|
var originalTradeId = originalTrade.id;
|
|
//获取收盘日当天已经生成的交易,需要先做删除,再重新生成
|
|
var todayTradeIds = (from td in DbContext.trade.Where(y => y.TradeDate == valuedateBLL.ValueDate && y.ValidState != "InValid")
|
|
join swap in DbContext.trade_swap.Where(y => y.OriginalTradeId == originalTradeId && y.SwapType == "普通")
|
|
on td.id equals swap.TradeId
|
|
select td.id).ToList();
|
|
if (todayTradeIds.Any())
|
|
{
|
|
todayTradeIds.ForEach(y =>
|
|
{
|
|
new TradeInvalidService(this).InvalidTrade(y, false);
|
|
});
|
|
|
|
var tradeSwapDetails = DbContext.trade_swap_detail.Where(y => todayTradeIds.Contains(y.ChildTradeId));
|
|
DbContext.trade_swap_detail.RemoveRange(tradeSwapDetails);
|
|
DbContext.SaveChanges();
|
|
}
|
|
|
|
//对当天主交易做初始化处理
|
|
var todayParentTradesIds = (from td in DbContext.trade.Where(y => y.TradeDate == valuedateBLL.ValueDate && y.ParentTradeId == 0 && y.ValidState != "InValid")
|
|
join swap in DbContext.trade_swap.Where(y => y.SwapType == "多空组合")
|
|
on td.id equals swap.TradeId
|
|
where td.id == originalTradeId || swap.OriginalTradeId == originalTradeId
|
|
select td.id).ToList();
|
|
if (todayParentTradesIds.Any())
|
|
{
|
|
var todayParentTrades = DbContext.trade.Where(y => todayParentTradesIds.Contains(y.id)).ToList();
|
|
todayParentTrades.ForEach(y =>
|
|
{
|
|
y.TradeStatus = "确认成交";
|
|
y.StockEqvNotional = 0;
|
|
y.OriginalStockEqvNotional = 0;
|
|
y.StockEqvNotionalReal = 0;
|
|
y.TradePrice = 0;
|
|
});
|
|
DbContext.SaveChanges();
|
|
}
|
|
|
|
var preTradeDate = (from td in DbContext.trade.Where(y => y.TradeDate < valuedateBLL.ValueDate && y.ParentTradeId > 0 && y.ValidState != "InValid")
|
|
join swap in DbContext.trade_swap.Where(y => y.OriginalTradeId == originalTradeId)
|
|
on td.id equals swap.TradeId
|
|
select td.TradeDate).ToList().Max();
|
|
var preTradeDateTradeIds = (from td in DbContext.trade.Where(y => y.TradeDate == preTradeDate && y.ParentTradeId > 0 && y.ValidState != "InValid")
|
|
join swap in DbContext.trade_swap.Where(y => y.OriginalTradeId == originalTradeId)
|
|
on td.id equals swap.TradeId
|
|
select td.id).ToList();
|
|
if (preTradeDateTradeIds.Any())
|
|
{
|
|
preTradeDateTradeIds.ForEach(y =>
|
|
{
|
|
new TradeBackService(this).Execute(y, true, TradeBackActionEnum.backTrade);
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 生成新的开平仓记录
|
|
|
|
var preTradeDateTrades = (from td in DbContext.trade.Where(y => y.TradeDate == preTradeDate && y.ParentTradeId > 0 && y.ValidState != "InValid")
|
|
join swap in DbContext.trade_swap.Where(y => y.OriginalTradeId == originalTradeId)
|
|
on td.id equals swap.TradeId
|
|
select new { td, swap }).ToList();
|
|
|
|
//前一天存在持仓的场景
|
|
//先平仓再开仓的情景时,只有完全平仓的子交易:合成的流水手续费记为平仓手续费,存在持仓的子交易:合成的流水手续费记为开仓手续费。
|
|
if (preTradeDateTrades.Any())
|
|
{
|
|
var newParentTradeId = 0;
|
|
//对前一天的持仓标的做先平仓后开仓处理
|
|
preTradeDateTrades.ForEach(y =>
|
|
{
|
|
var notional = (y.swap.IsPayFloatingProfit ? y.swap.PayNotional : y.swap.GetNotional) ?? 0;
|
|
var longShort = y.swap.IsPayFloatingProfit ? y.swap.PayLongShort : y.swap.GetLongShort;
|
|
var spotPrice = (y.swap.IsPayFloatingProfit ? y.swap.PaySpotPrice : y.swap.GetSpotPrice) ?? 0;
|
|
y.td.trade_swap = y.swap;
|
|
var totalNotional = notional * (longShort == "多头" ? 1 : -1);
|
|
var totalSpotPrice = totalNotional * spotPrice;
|
|
|
|
var underlyingTradeFlows = tradeFlows.Where(z => z.UnderlyingCode == y.td.UnderlyingCode);
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(y.td.UnderlyingCode);
|
|
var sumBuyLots = underlyingTradeFlows.Sum(z => z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0));
|
|
if (sumBuyLots > 0)
|
|
{
|
|
var sumBuyNotional = sumBuyLots * underlying.ContractSize;
|
|
totalNotional += sumBuyNotional;
|
|
var totalBuySpotPrice = underlyingTradeFlows.Sum(z => z.Lots * underlying.ContractSize * (z.SpotPrice ?? 0) * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0));
|
|
totalSpotPrice += totalBuySpotPrice;
|
|
}
|
|
|
|
var sumSellLots = underlyingTradeFlows.Sum(z => z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0));
|
|
if (sumSellLots > 0)
|
|
{
|
|
var sumSellNotional = sumSellLots * underlying.ContractSize;
|
|
totalNotional -= sumSellNotional;
|
|
var totalSellSpotPrice = underlyingTradeFlows.Sum(z => z.Lots * underlying.ContractSize * (z.SpotPrice ?? 0) * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0));
|
|
totalSpotPrice -= totalSellSpotPrice;
|
|
}
|
|
|
|
var totalFee = underlyingTradeFlows.Sum(z => z.NeedCostFee ? (z.SingleFee != null ? (z.SingleFee.Value * z.Lots) : (z.UnAnnualRate * z.Lots * z.SpotPrice * underlying.ContractSize ?? 0)) : 0);
|
|
|
|
//当天该标的完全平仓
|
|
if (totalNotional == 0)
|
|
{
|
|
y.td.UnWindDate = valuedateBLL.ValueDate;
|
|
y.td.FinalPrice = spotPrice + totalSpotPrice * (longShort == "多头" ? -1 : 1) / notional;
|
|
UnwindSwapTradeCashHandle(y.td, "平仓", null, totalFee);
|
|
}
|
|
//当天还存在持仓,需要先平仓,再开仓
|
|
else
|
|
{
|
|
#region 平仓
|
|
|
|
y.td.UnWindDate = valuedateBLL.ValueDate;
|
|
|
|
//假设先用收盘价将当前的剩余份额平仓掉,再用收盘价重新开仓剩余份额
|
|
var fillNotional = -totalNotional;
|
|
EodPrice eodPrice = null;
|
|
new EodPriceProvider(valuedateBLL.ValueDate, false).TryGetEodPrice(y.td.UnderlyingCode, out eodPrice);
|
|
var closePrice = eodPrice?.ClosePrice ?? underlying.Price ?? 0;
|
|
totalSpotPrice += fillNotional * closePrice;
|
|
|
|
y.td.FinalPrice = spotPrice + totalSpotPrice * (longShort == "多头" ? -1 : 1) / notional;
|
|
UnwindSwapTradeCashHandle(y.td, "平仓", null, 0);
|
|
|
|
#endregion
|
|
|
|
#region 重新开仓
|
|
|
|
var flow = new trade_swap_flow()
|
|
{
|
|
UnderlyingCode = y.td.UnderlyingCode,
|
|
TradeDate = valuedateBLL.ValueDate,
|
|
StartDate = valuedateBLL.ValueDate,
|
|
ExerciseDate = y.td.ExerciseDate,
|
|
QuoteCurrency = y.td.QuoteCurrency,
|
|
SettlementDate = y.td.SettlementDate,
|
|
SingleFee = y.swap.IsPayFloatingProfit ? y.swap.GetSingleFee : y.swap.PaySingleFee,
|
|
UnAnnualRate = y.swap.IsPayFloatingProfit ? y.swap.GetUnAnnualRate : y.swap.PayUnAnnualRate,
|
|
TradeNumber = x
|
|
};
|
|
flow.Lots = Math.Abs(totalNotional / underlying.ContractSize);
|
|
flow.BuySell = totalNotional > 0 ? "买入" : "卖出";
|
|
flow.SpotPrice = closePrice;
|
|
var trade = MapSwapTradeHandle(flow, false);
|
|
InnerSaveSwapTrade(trade, totalFee);
|
|
|
|
if (newParentTradeId == 0 && trade.ParentTradeId > 0)
|
|
{
|
|
newParentTradeId = trade.ParentTradeId;
|
|
}
|
|
|
|
hasNoPosition = false;
|
|
|
|
#endregion
|
|
}
|
|
});
|
|
|
|
//对当天新的标的流水处理
|
|
var underlyingCodes = preTradeDateTrades.Select(y => y.td.UnderlyingCode).Distinct().ToList();
|
|
var tradeFlowsUnderlyingGroupNew = GetTradeFlowsUnderlyingGroup(tradeFlows.Where(y => !underlyingCodes.Contains(y.UnderlyingCode)));
|
|
tradeFlowsUnderlyingGroupNew.ForEach(y =>
|
|
{
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(y.UnderlyingCode);
|
|
var underlyingTradeFlows = tradeFlows.Where(z => z.UnderlyingCode == y.UnderlyingCode).ToList();
|
|
double buyTotalFee = 0d;
|
|
double sellTotalFee = 0d;
|
|
double totalFee = 0d;
|
|
|
|
underlyingTradeFlows.ForEach(z =>
|
|
{
|
|
if (z.NeedCostFee)
|
|
{
|
|
if (z.SingleFee == null && z.UnAnnualRate == null)
|
|
{
|
|
var varitey = DataCacheProvider.GetVariety(y.UnderlyingCode);
|
|
var clientVarietyConfig = DbContext.client_variety_config.Where(o => o.ClientId == client.id && o.VarietyId == varitey.id && o.ValueDate <= y.flow.TradeDate).OrderByDescending(o => o.ValueDate).FirstOrDefault();
|
|
if (clientVarietyConfig == null)
|
|
{
|
|
throw new ServiceException($"该客户[{client.Name}]需要维护品种[{varitey.VarietyCode}]在[{y.flow.TradeDate}]相关的收费参数配置");
|
|
}
|
|
|
|
buyTotalFee += clientVarietyConfig.SingleFee > 0 ? (clientVarietyConfig.SingleFee * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0)) : (clientVarietyConfig.UnAnnualRate * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
sellTotalFee += clientVarietyConfig.SingleFee > 0 ? (clientVarietyConfig.SingleFee * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0)) : (clientVarietyConfig.UnAnnualRate * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
totalFee += clientVarietyConfig.SingleFee > 0 ? (z.SingleFee.Value * z.Lots) : (z.UnAnnualRate * z.Lots * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
}
|
|
else
|
|
{
|
|
buyTotalFee += z.SingleFee != null ? (z.SingleFee.Value * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0)) : (z.UnAnnualRate * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
sellTotalFee += z.SingleFee != null ? (z.SingleFee.Value * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0)) : (z.UnAnnualRate * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
totalFee += z.SingleFee != null ? (z.SingleFee.Value * z.Lots) : (z.UnAnnualRate * z.Lots * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
}
|
|
}
|
|
});
|
|
|
|
//当天新增的标的,买卖手数合成为0,即持仓数为0,新增一笔普通互换,然后当天平仓平掉
|
|
if (y.Lots == 0)
|
|
{
|
|
buyTotalFee = underlyingTradeFlows.Sum(z => z.NeedCostFee ? (z.SingleFee != null ? (z.SingleFee.Value * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0)) : (z.UnAnnualRate * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0)) : 0);
|
|
sellTotalFee = underlyingTradeFlows.Sum(z => z.NeedCostFee ? (z.SingleFee != null ? (z.SingleFee.Value * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0)) : (z.UnAnnualRate * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0)) : 0);
|
|
SetOpenUnwindSingleSwapTrade(y, buyTotalFee, sellTotalFee);
|
|
}
|
|
//当天新增的标的,存在持仓的,用均价作为开仓价
|
|
else
|
|
{
|
|
y.flow.Lots = Math.Abs(y.Lots);
|
|
y.flow.BuySell = y.Lots > 0 ? "买入" : "卖出";
|
|
y.flow.SpotPrice = Math.Abs(y.SumSpotPrice / y.Lots);
|
|
var trade = MapSwapTradeHandle(y.flow, false);
|
|
totalFee = underlyingTradeFlows.Sum(z => z.NeedCostFee ? (z.SingleFee != null ? (z.SingleFee.Value * z.Lots) : (z.UnAnnualRate * z.Lots * z.SpotPrice * underlying.ContractSize ?? 0)) : 0);
|
|
InnerSaveSwapTrade(trade, totalFee);
|
|
|
|
if (newParentTradeId == 0 && trade.ParentTradeId > 0)
|
|
{
|
|
newParentTradeId = trade.ParentTradeId;
|
|
}
|
|
|
|
hasNoPosition = false;
|
|
}
|
|
});
|
|
|
|
var oldParentTradeId = preTradeDateTrades.FirstOrDefault().td.ParentTradeId;
|
|
var oldParentTrade = DbContext.trade.Find(oldParentTradeId);
|
|
AddTradeOperationHistoryAndSetParentTradeInfo(true, oldParentTrade, "合成多空组合流水平仓操作");
|
|
|
|
var newParentTrade = DbContext.trade.Find(newParentTradeId);
|
|
if (newParentTrade != null)
|
|
{
|
|
AddTradeOperationHistoryAndSetParentTradeInfo(true, newParentTrade, "合成多空组合流水开仓操作");
|
|
}
|
|
}
|
|
//当天开仓的场景
|
|
//第一天开仓的交易,将流水费用合成变为对应子交易的交易费用;
|
|
else
|
|
{
|
|
var tradeFlowsUnderlyingGroup = GetTradeFlowsUnderlyingGroup(tradeFlows);
|
|
|
|
tradeFlowsUnderlyingGroup.ForEach(y =>
|
|
{
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(y.UnderlyingCode);
|
|
var underlyingTradeFlows = tradeFlows.Where(z => z.UnderlyingCode == y.UnderlyingCode).ToList();
|
|
|
|
|
|
double buyTotalFee = 0d;
|
|
double sellTotalFee = 0d;
|
|
double totalFee = 0d;
|
|
|
|
underlyingTradeFlows.ForEach(z =>
|
|
{
|
|
if (z.NeedCostFee)
|
|
{
|
|
if (z.SingleFee == null && z.UnAnnualRate == null)
|
|
{
|
|
var varitey = DataCacheProvider.GetVariety(y.UnderlyingCode);
|
|
var clientVarietyConfig = DbContext.client_variety_config.Where(o => o.ClientId == client.id && o.VarietyId == varitey.id && o.ValueDate <= y.flow.TradeDate).OrderByDescending(o => o.ValueDate).FirstOrDefault();
|
|
if (clientVarietyConfig == null)
|
|
{
|
|
throw new ServiceException($"该客户[{client.Name}]需要维护品种[{varitey.VarietyCode}]在[{y.flow.TradeDate}]相关的收费参数配置");
|
|
}
|
|
|
|
buyTotalFee += clientVarietyConfig.SingleFee > 0 ? (clientVarietyConfig.SingleFee * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0)) : (clientVarietyConfig.UnAnnualRate * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
sellTotalFee += clientVarietyConfig.SingleFee > 0 ? (clientVarietyConfig.SingleFee * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0)) : (clientVarietyConfig.UnAnnualRate * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
totalFee += clientVarietyConfig.SingleFee > 0 ? (clientVarietyConfig.SingleFee * z.Lots) : (clientVarietyConfig.UnAnnualRate * z.Lots * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
}
|
|
else
|
|
{
|
|
buyTotalFee += z.SingleFee != null ? (z.SingleFee.Value * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0)) : (z.UnAnnualRate * z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
sellTotalFee += z.SingleFee != null ? (z.SingleFee.Value * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0)) : (z.UnAnnualRate * z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0) * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
totalFee += z.SingleFee != null ? (z.SingleFee.Value * z.Lots) : (z.UnAnnualRate * z.Lots * z.SpotPrice * underlying.ContractSize ?? 0);
|
|
}
|
|
}
|
|
});
|
|
|
|
//持仓数为0,新增一笔普通互换,然后当天平仓平掉
|
|
if (y.Lots == 0)
|
|
{
|
|
SetOpenUnwindSingleSwapTrade(y, buyTotalFee, sellTotalFee);
|
|
}
|
|
//存在持仓的,用均价作为开仓价
|
|
else
|
|
{
|
|
y.flow.Lots = Math.Abs(y.Lots);
|
|
y.flow.BuySell = y.Lots > 0 ? "买入" : "卖出";
|
|
y.flow.SpotPrice = Math.Abs(y.SumSpotPrice / y.Lots);
|
|
var trade = MapSwapTradeHandle(y.flow, false);
|
|
InnerSaveSwapTrade(trade, totalFee);
|
|
}
|
|
});
|
|
|
|
AddTradeOperationHistoryAndSetParentTradeInfo(true, originalTrade, "合成多空组合流水开仓");
|
|
}
|
|
|
|
tradeFlows.ForEach(y => y.Status = "已完成");
|
|
DbContext.SaveChanges();
|
|
|
|
if (hasNoPosition)
|
|
{
|
|
var tradeDateTradeIds = (from td in DbContext.trade.Where(z => z.TradeDate == valuedateBLL.ValueDate && z.ValidState != "InValid")
|
|
join swap in DbContext.trade_swap.Where(z => z.OriginalTradeId == originalTradeId && z.SwapType == "多空组合")
|
|
on td.id equals swap.TradeId
|
|
select td.id).ToList();
|
|
if (tradeDateTradeIds.Any())
|
|
{
|
|
tradeDateTradeIds.ForEach(z =>
|
|
{
|
|
new TradeInvalidService(this).InvalidTrade(z, false);
|
|
});
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
});
|
|
}
|
|
|
|
trans.Commit();
|
|
}
|
|
}
|
|
|
|
private List<trade_swap_flow_group> GetTradeFlowsUnderlyingGroup(IEnumerable<trade_swap_flow> tradeFlows)
|
|
{
|
|
var tradeFlowsUnderlyingGroup = tradeFlows.GroupBy(y => y.UnderlyingCode)
|
|
.Select(y =>
|
|
new trade_swap_flow_group()
|
|
{
|
|
UnderlyingCode = y.Key,
|
|
BuyLots = y.Sum(z => z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0)),
|
|
SellLots = y.Sum(z => z.Lots * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0)),
|
|
Lots = y.Sum(z => z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : -1)),
|
|
SumBuySpotPrice = y.Sum(z => z.Lots * (z.SpotPrice ?? 0) * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : 0)),
|
|
SumSellSpotPrice = y.Sum(z => z.Lots * (z.SpotPrice ?? 0) * (z.BuySell == "卖出" || z.BuySell == "S" ? 1 : 0)),
|
|
SumSpotPrice = y.Sum(z => z.Lots * (z.SpotPrice ?? 0) * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : -1)),
|
|
flow = new trade_swap_flow()
|
|
{
|
|
UnderlyingCode = y.Key,
|
|
BuySell = y.Sum(z => z.Lots * (z.BuySell == "买入" || z.BuySell == "B" ? 1 : -1)) >= 0 ? "买入" : "卖出",
|
|
ClearingAgency = y.First().ClearingAgency,
|
|
TradeNumber = y.First().TradeNumber,
|
|
TradeDate = y.First().TradeDate,
|
|
StartDate = y.First().StartDate,
|
|
ExerciseDate = y.First().ExerciseDate,
|
|
QuoteCurrency = y.First().QuoteCurrency,
|
|
SettlementDate = y.First().SettlementDate,
|
|
NeedCostFee = y.First().NeedCostFee,
|
|
SingleFee = y.First().SingleFee,
|
|
UnAnnualRate = y.First().UnAnnualRate
|
|
}
|
|
}
|
|
).ToList();
|
|
|
|
return tradeFlowsUnderlyingGroup;
|
|
}
|
|
|
|
private void SetOpenUnwindSingleSwapTrade(trade_swap_flow_group flowGroup, double openCostFee, double closeCostFee)
|
|
{
|
|
flowGroup.flow.Lots = flowGroup.BuyLots;
|
|
flowGroup.flow.SpotPrice = flowGroup.SumBuySpotPrice / flowGroup.BuyLots;
|
|
var trade = MapSwapTradeHandle(flowGroup.flow, true);
|
|
InnerSaveSwapTrade(trade, openCostFee);
|
|
trade.UnWindDate = valuedateBLL.ValueDate;
|
|
trade.FinalPrice = flowGroup.SumSellSpotPrice / flowGroup.SellLots;
|
|
UnwindSwapTradeCashHandle(trade, "平仓", null, closeCostFee);
|
|
|
|
AddTradeOperationHistoryAndSetParentTradeInfo(true, trade, "合成多空组合流水时生成的单笔交易开平仓记录");
|
|
}
|
|
|
|
private trade MapSwapTradeHandle(trade_swap_flow swapFlow, bool isSingleTrade)
|
|
{
|
|
var originalTrades = DbContext.trade.Where(x => x.TradeNumber == swapFlow.TradeNumber);
|
|
new TradeExtendService(UserInfo).SetTradeExtend(originalTrades, true);
|
|
var originalTrade = originalTrades.FirstOrDefault();
|
|
var lastParentTrade = originalTrade;
|
|
if (originalTrade == null)
|
|
{
|
|
throw new ServiceException($"该交易编号[{swapFlow.TradeNumber}]在系统中不存在");
|
|
}
|
|
else if (originalTrade.TradeType != "收益互换" || originalTrade.trade_swap.SwapType != "多空组合")
|
|
{
|
|
throw new ServiceException($"该交易编号[{swapFlow.TradeNumber}]的交易类型非多空组合的收益互换");
|
|
}
|
|
|
|
var defaultExerciseDate = originalTrade.ExerciseDate;
|
|
|
|
var td = new trade
|
|
{
|
|
TradeDate = swapFlow.TradeDate,
|
|
StartDate = swapFlow.StartDate,
|
|
ExerciseDate = swapFlow.ExerciseDate,
|
|
TradeType = "收益互换",
|
|
StructureType = "收益互换",
|
|
ClientId = originalTrade.ClientId,
|
|
ClientName = originalTrade.ClientName,
|
|
QuoteCurrency = swapFlow.QuoteCurrency,
|
|
SettlementCurrency = originalTrade.SettlementCurrency,
|
|
UnderlyingCode = swapFlow.UnderlyingCode,
|
|
SpotPrice = swapFlow.SpotPrice,
|
|
Lots = swapFlow.Lots,
|
|
IsNight = swapFlow.IsNight,
|
|
OpponentRole = "甲方",
|
|
MarginTemplateName = null,
|
|
MarginType = MarginTypeEnum.DEFAULT,
|
|
IsGroup = isSingleTrade ? 0 : 2
|
|
};
|
|
|
|
if (!isSingleTrade)
|
|
{
|
|
if (originalTrade.TradeDate == swapFlow.TradeDate)
|
|
{
|
|
td.ParentTradeId = originalTrade.id;
|
|
}
|
|
else if (originalTrade.TradeDate < swapFlow.TradeDate)
|
|
{
|
|
var todayParentTrade = (from trade in DbContext.trade.Where(x => x.ValidState != "InValid" && x.TradeDate == swapFlow.TradeDate && x.ClientId == originalTrade.ClientId)
|
|
join swap in DbContext.trade_swap.Where(x => x.OriginalTradeId == originalTrade.id)
|
|
on trade.id equals swap.TradeId
|
|
select trade).FirstOrDefault();
|
|
if (todayParentTrade == null)
|
|
{
|
|
var lastParentTrades = (from trade in DbContext.trade.Where(x => x.ValidState != "InValid" && x.TradeDate < swapFlow.TradeDate && x.ClientId == originalTrade.ClientId && x.IsGroup == 1)
|
|
join swap in DbContext.trade_swap.Where(x => x.OriginalTradeId == originalTrade.id)
|
|
on trade.id equals swap.TradeId
|
|
select trade).OrderByDescending(x => x.TradeDate);
|
|
|
|
if (lastParentTrades.Any())
|
|
{
|
|
new TradeExtendService(UserInfo).SetTradeExtend(lastParentTrades, true);
|
|
|
|
lastParentTrade = lastParentTrades.First();
|
|
defaultExerciseDate = lastParentTrade.ExerciseDate;
|
|
}
|
|
|
|
var newParentTrade = new trade
|
|
{
|
|
TradeDate = swapFlow.TradeDate,
|
|
StartDate = swapFlow.StartDate,
|
|
ExerciseDate = defaultExerciseDate,
|
|
TradeType = "收益互换",
|
|
StructureType = "收益互换",
|
|
ClientId = lastParentTrade.ClientId,
|
|
ClientName = lastParentTrade.ClientName,
|
|
QuoteCurrency = lastParentTrade.QuoteCurrency,
|
|
SettlementCurrency = lastParentTrade.SettlementCurrency,
|
|
UnderlyingCode = lastParentTrade.UnderlyingCode,
|
|
UnderlyingId = lastParentTrade.UnderlyingId,
|
|
UnderlyingAssetClass = lastParentTrade.UnderlyingAssetClass,
|
|
MaturityDate = lastParentTrade.MaturityDate,
|
|
UnderlyingInstrumentType = lastParentTrade.UnderlyingInstrumentType,
|
|
UnderlyingAssetName = lastParentTrade.UnderlyingName,
|
|
SpotPrice = lastParentTrade.SpotPrice,
|
|
IsNight = lastParentTrade.IsNight,
|
|
OpponentRole = lastParentTrade.OpponentRole,
|
|
MarginTemplateName = lastParentTrade.MarginTemplateName,
|
|
MarginType = lastParentTrade.MarginType,
|
|
IsUsePremiumRate = lastParentTrade.IsUsePremiumRate,
|
|
PrincipalRate = lastParentTrade.PrincipalRate,
|
|
ParticipationRate = lastParentTrade.ParticipationRate,
|
|
TraderId = lastParentTrade.TraderId,
|
|
TraderName = lastParentTrade.TraderName,
|
|
AssetId = lastParentTrade.AssetId,
|
|
AssetBookName = lastParentTrade.AssetBookName,
|
|
IsGroup = lastParentTrade.IsGroup
|
|
};
|
|
|
|
newParentTrade.MetaDic = lastParentTrade.MetaDic;
|
|
|
|
newParentTrade.trade_swap = new trade_swap()
|
|
{
|
|
SwapType = lastParentTrade.trade_swap.SwapType,
|
|
OriginalTradeId = originalTrade.id,
|
|
IsGetFloatingProfit = lastParentTrade.trade_swap.IsGetFloatingProfit,
|
|
GetSingleFee = lastParentTrade.trade_swap.GetSingleFee,
|
|
GetUnAnnualRate = lastParentTrade.trade_swap.GetUnAnnualRate,
|
|
GetSwapTimeAndRate = lastParentTrade.trade_swap.GetSwapTimeAndRate,
|
|
GetMarginRate = lastParentTrade.trade_swap.GetMarginRate,
|
|
GetUnderlyingId = lastParentTrade.trade_swap.GetUnderlyingId,
|
|
GetUnderlyingCode = lastParentTrade.trade_swap.GetUnderlyingCode,
|
|
GetSpotPrice = lastParentTrade.trade_swap.GetSpotPrice,
|
|
IsPayFloatingProfit = lastParentTrade.trade_swap.IsPayFloatingProfit,
|
|
PaySingleFee = lastParentTrade.trade_swap.PaySingleFee,
|
|
PayUnAnnualRate = lastParentTrade.trade_swap.PayUnAnnualRate,
|
|
PayMarginRate = lastParentTrade.trade_swap.PayMarginRate,
|
|
PayUnderlyingId = lastParentTrade.trade_swap.PayUnderlyingId,
|
|
PayUnderlyingCode = lastParentTrade.trade_swap.PayUnderlyingCode,
|
|
PaySpotPrice = lastParentTrade.trade_swap.PaySpotPrice,
|
|
PaySwapTimeAndRate = lastParentTrade.trade_swap.PaySwapTimeAndRate,
|
|
AnnualDays = lastParentTrade.trade_swap.AnnualDays,
|
|
AnnualVarIncome = lastParentTrade.trade_swap.AnnualVarIncome,
|
|
RateCalcMode = lastParentTrade.trade_swap.RateCalcMode,
|
|
IsTradePriceWhenOpen = lastParentTrade.trade_swap.IsTradePriceWhenOpen,
|
|
};
|
|
|
|
InnerSaveSwapTrade(newParentTrade, 0);
|
|
td.ParentTradeId = newParentTrade.id;
|
|
}
|
|
else
|
|
{
|
|
td.ParentTradeId = todayParentTrade.id;
|
|
defaultExerciseDate = todayParentTrade.ExerciseDate;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new ServiceException($"交易编号[{swapFlow.TradeNumber}]对应的交易流水的交易日期[{swapFlow.TradeDate?.ToString("yyyy-MM-dd")}]不该在多空组合交易的交易日[{originalTrade.TradeDate?.ToString("yyyy-MM-dd")}]之前");
|
|
}
|
|
}
|
|
|
|
td.MetaDic = lastParentTrade.MetaDic;
|
|
|
|
//标的代码(必需)
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
|
if (underlying == null)
|
|
{
|
|
throw new ServiceException($"该标的代码[{td.UnderlyingCode}]在系统中不存在");
|
|
}
|
|
else
|
|
{
|
|
td.UnderlyingId = underlying.id;
|
|
td.UnderlyingAssetClass = underlying.UnderlyingType;
|
|
td.MaturityDate = underlying.MaturityDate;
|
|
|
|
if (td.ExerciseDate == null)
|
|
{
|
|
td.ExerciseDate = underlying.MaturityDate;
|
|
swapFlow.ExerciseDate = underlying.MaturityDate;
|
|
}
|
|
td.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType;
|
|
td.UnderlyingAssetName = underlying.UnderlyingName;
|
|
}
|
|
|
|
if (td.ExerciseDate == null)
|
|
{
|
|
td.ExerciseDate = defaultExerciseDate;
|
|
}
|
|
|
|
var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId);
|
|
//交易份额
|
|
td.Notional = (td.Lots ?? 0) * underlying.ContractSize;
|
|
td.TradeAmount = td.Notional / variety.CountRatio;
|
|
td.OriginalNotional = td.Notional;
|
|
td.StockEqvNotional = (td.SpotPrice ?? 0) * td.Notional;
|
|
td.StockEqvNotionalReal = td.StockEqvNotional;
|
|
td.OriginalStockEqvNotional = td.StockEqvNotional;
|
|
td.PrincipalRate = 0;
|
|
td.ParticipationRate = 1;
|
|
td.AssetId = originalTrade.AssetId;
|
|
td.AssetBookName = originalTrade.AssetBookName;
|
|
td.TraderId = originalTrade.TraderId;
|
|
td.TraderName = originalTrade.TraderName;
|
|
//td.InitialMargin = td.StockEqvNotional * (lastParentTrade.trade_swap.GetMarginRate - lastParentTrade.trade_swap.PayMarginRate);
|
|
|
|
td.trade_swap = new trade_swap()
|
|
{
|
|
TradeId = td.id,
|
|
OriginalTradeId = originalTrade.id
|
|
};
|
|
|
|
td.trade_swap.SwapType = "普通";
|
|
td.trade_swap.AnnualDays = lastParentTrade.trade_swap.AnnualDays;
|
|
td.trade_swap.AnnualVarIncome = lastParentTrade.trade_swap.AnnualVarIncome;
|
|
td.trade_swap.RateCalcMode = lastParentTrade.trade_swap.RateCalcMode;
|
|
td.trade_swap.IsTradePriceWhenOpen = true;
|
|
td.trade_swap.IsPayFloatingProfit = lastParentTrade.trade_swap.IsPayFloatingProfit;
|
|
td.trade_swap.IsGetFloatingProfit = lastParentTrade.trade_swap.IsGetFloatingProfit;
|
|
td.trade_swap.GetSwapTimeAndRate = lastParentTrade.trade_swap.GetSwapTimeAndRate;
|
|
td.trade_swap.PaySwapTimeAndRate = lastParentTrade.trade_swap.PaySwapTimeAndRate;
|
|
|
|
var isAnnualSet = swapFlow.SingleFee != null || swapFlow.UnAnnualRate != null;
|
|
if (isAnnualSet)
|
|
{
|
|
if (td.trade_swap.IsPayFloatingProfit)
|
|
{
|
|
td.trade_swap.GetSingleFee = swapFlow.SingleFee ?? 0;
|
|
td.trade_swap.GetUnAnnualRate = swapFlow.UnAnnualRate ?? 0;
|
|
}
|
|
else
|
|
{
|
|
td.trade_swap.PaySingleFee = swapFlow.SingleFee ?? 0;
|
|
td.trade_swap.PayUnAnnualRate = swapFlow.UnAnnualRate ?? 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var varitey = DataCacheProvider.GetVariety(td.UnderlyingCode);
|
|
var clientVarietyConfig = DbContext.client_variety_config.Where(x => x.ClientId == td.ClientId && x.VarietyId == varitey.id && x.ValueDate <= td.TradeDate).OrderByDescending(x => x.ValueDate).FirstOrDefault();
|
|
if (clientVarietyConfig == null)
|
|
{
|
|
throw new ServiceException($"该客户[{td.ClientName}]需要维护品种[{varitey.VarietyCode}]在[{td.TradeDate}]相关的收费参数配置");
|
|
}
|
|
|
|
if (td.trade_swap.IsPayFloatingProfit)
|
|
{
|
|
td.trade_swap.GetSingleFee = clientVarietyConfig.SingleFee;
|
|
td.trade_swap.GetUnAnnualRate = clientVarietyConfig.UnAnnualRate;
|
|
}
|
|
else
|
|
{
|
|
td.trade_swap.PaySingleFee = clientVarietyConfig.SingleFee;
|
|
td.trade_swap.PayUnAnnualRate = clientVarietyConfig.UnAnnualRate;
|
|
}
|
|
}
|
|
|
|
if (td.trade_swap.IsPayFloatingProfit)
|
|
{
|
|
td.trade_swap.GetTradePrice = (td.trade_swap.GetSingleFee ?? 0) * (td.Lots ?? 0) + (td.trade_swap.GetUnAnnualRate ?? 0) * td.StockEqvNotional;
|
|
td.trade_swap.GetMarginRate = lastParentTrade.trade_swap.GetMarginRate;
|
|
td.trade_swap.PaySpotPrice = td.SpotPrice;
|
|
td.trade_swap.PayUnderlyingId = td.UnderlyingId;
|
|
td.trade_swap.PayUnderlyingCode = td.UnderlyingCode;
|
|
td.trade_swap.PayNotional = td.Notional;
|
|
td.trade_swap.PayTradeAmount = td.TradeAmount;
|
|
}
|
|
else
|
|
{
|
|
td.trade_swap.PayTradePrice = (td.trade_swap.PaySingleFee ?? 0) * (td.Lots ?? 0) + (td.trade_swap.PayUnAnnualRate ?? 0) * td.StockEqvNotional;
|
|
td.trade_swap.PayMarginRate = lastParentTrade.trade_swap.PayMarginRate;
|
|
td.trade_swap.GetSpotPrice = td.SpotPrice;
|
|
td.trade_swap.GetUnderlyingId = td.UnderlyingId;
|
|
td.trade_swap.GetUnderlyingCode = td.UnderlyingCode;
|
|
td.trade_swap.GetNotional = td.Notional;
|
|
td.trade_swap.GetTradeAmount = td.TradeAmount;
|
|
}
|
|
|
|
//交易方向
|
|
var longshort = swapFlow.BuySell;
|
|
switch (longshort)
|
|
{
|
|
case "买入":
|
|
case "B":
|
|
if (td.trade_swap.IsPayFloatingProfit)
|
|
{
|
|
td.trade_swap.PayLongShort = "多头";
|
|
}
|
|
else
|
|
{
|
|
td.trade_swap.GetLongShort = "多头";
|
|
}
|
|
break;
|
|
case "卖出":
|
|
case "S":
|
|
if (td.trade_swap.IsPayFloatingProfit)
|
|
{
|
|
td.trade_swap.PayLongShort = "空头";
|
|
}
|
|
else
|
|
{
|
|
td.trade_swap.GetLongShort = "空头";
|
|
}
|
|
break;
|
|
default:
|
|
throw new ServiceException("买卖方向 填写错误:" + longshort);
|
|
}
|
|
|
|
return td;
|
|
}
|
|
|
|
private void InnerSaveSwapTrade(trade td, double tradePriceQuote)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(td.TradeNumber))
|
|
{
|
|
if (DbContext.trade.Any(x => x.TradeNumber == td.TradeNumber))
|
|
{
|
|
throw new ServiceException($"存在重复的交易编号[{td.TradeNumber}]");
|
|
}
|
|
}
|
|
|
|
td.TradeStatus = ConsTrade.确认成交;
|
|
|
|
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.TradeDate.Value, seekPreday: true);
|
|
|
|
if (td.trade_swap.IsPayFloatingProfit)
|
|
{
|
|
td.trade_swap.GetTradePrice = tradePriceQuote;
|
|
td.TradePrice = tradePriceQuote * currencyRate;
|
|
|
|
td.BuySell = "卖出";
|
|
}
|
|
else
|
|
{
|
|
td.trade_swap.PayTradePrice = tradePriceQuote;
|
|
td.TradePrice = tradePriceQuote * currencyRate;
|
|
|
|
td.BuySell = "买入";
|
|
}
|
|
|
|
td.OriginalNotional = td.Notional;
|
|
td.OriginalStockEqvNotional = td.StockEqvNotional;
|
|
td.StockEqvNotionalReal = td.StockEqvNotionalReal;
|
|
td.MarginTemplateName = null;
|
|
td.MarginType = MarginTypeEnum.DEFAULT;
|
|
td.IsTradePricePayType = true;
|
|
td.TradeSource = TradeSourceEnum.导入交易.ToString();
|
|
td.OptId = UserId;
|
|
td.OptName = UserName;
|
|
td.OptDate = DateTime.Now;
|
|
td.CreatorId = UserId;
|
|
td.CreatorName = UserName;
|
|
td.CreateDate = DateTime.Now;
|
|
DbContext.trade.Add(td);
|
|
DbContext.SaveChanges();
|
|
|
|
td.trade_swap.TradeId = td.id;
|
|
td.trade_swap.OptId = UserId;
|
|
td.trade_swap.OptName = UserName;
|
|
td.trade_swap.OptDate = DateTime.Now;
|
|
DbContext.trade_swap.Add(td.trade_swap);
|
|
|
|
SaveTradeMeta(td);
|
|
|
|
var tc = new trade_cash
|
|
{
|
|
ValidState = "Valid"
|
|
};
|
|
DbContext.trade_cash.Add(tc);
|
|
tc.OptId = UserId;
|
|
tc.OptName = UserName;
|
|
tc.OptDate = DateTime.Now;
|
|
tc.Action = ClientCashInCashOut.系统操作_期权费;
|
|
tc.Amount = (td.TradePrice ?? 0) * (td.BuySell == "买入" ? -1 : 1);
|
|
tc.QuoteAmount = tradePriceQuote * (td.BuySell == "买入" ? -1 : 1);
|
|
tc.CurrencyRate = currencyRate;
|
|
tc.ExceciseType = "现金";
|
|
tc.TradeId = td.id;
|
|
tc.ValueDate = td.TradeDate.Value;
|
|
tc.Notional = td.Notional;
|
|
tc.TradeAmount = td.TradeAmount;
|
|
tc.Status = TradeCashStatusEnum.已执行;
|
|
tc.TradeType = td.BuySell;
|
|
DbContext.SaveChanges();
|
|
|
|
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate);
|
|
|
|
var tcdGet = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = tc.Amount,
|
|
QuoteAmount = tc.QuoteAmount,
|
|
TradeCashType = TradeCashTypeEnum.开仓手续费.ToString(),
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = true,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdGet);
|
|
|
|
if (td.ParentTradeId > 0)
|
|
{
|
|
var parentTrade = DbContext.trade.Find(td.ParentTradeId);
|
|
if (parentTrade != null)
|
|
{
|
|
parentTrade.OriginalStockEqvNotional += td.StockEqvNotional;
|
|
parentTrade.StockEqvNotional += td.StockEqvNotional;
|
|
parentTrade.StockEqvNotionalReal += td.StockEqvNotional;
|
|
var tradePrice = (parentTrade.TradePrice ?? 0) * (parentTrade.BuySell == "买入" ? -1 : 1) + (td.TradePrice ?? 0) * (td.BuySell == "买入" ? -1 : 1);
|
|
parentTrade.TradePrice = Math.Abs(tradePrice);
|
|
parentTrade.BuySell = tradePrice >= 0 ? "卖出" : "买入";
|
|
|
|
var swapTrade = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == parentTrade.id);
|
|
if (swapTrade != null)
|
|
{
|
|
if (swapTrade.IsPayFloatingProfit)
|
|
{
|
|
swapTrade.GetTradePrice = tradePrice;
|
|
}
|
|
else
|
|
{
|
|
swapTrade.PayTradePrice = -tradePrice;
|
|
}
|
|
}
|
|
|
|
td.trade_swap.AnnualDays = swapTrade.AnnualDays;
|
|
|
|
trade_swap_detail detail = new trade_swap_detail()
|
|
{
|
|
IsForGet = td.trade_swap.IsGetFloatingProfit,
|
|
TradeId = td.ParentTradeId,
|
|
ChildTradeId = td.id,
|
|
LongShort = td.trade_swap.IsGetFloatingProfit ? td.trade_swap.GetLongShort : td.trade_swap.PayLongShort,
|
|
UnderlyingCode = td.UnderlyingCode,
|
|
SpotPrice = td.SpotPrice,
|
|
OriginalNotional = td.OriginalNotional,
|
|
Notional = td.Notional,
|
|
TradePrice = td.trade_swap.IsGetFloatingProfit ? (td.trade_swap.PayTradePrice ?? 0) : (td.trade_swap.GetTradePrice ?? 0)
|
|
};
|
|
DbContext.trade_swap_detail.Add(detail);
|
|
|
|
if (string.IsNullOrWhiteSpace(td.TradeNumber))
|
|
{
|
|
var childCount = DbContext.trade.Where(x => x.ParentTradeId == parentTrade.id && x.ValidState != "InValid").Count();
|
|
td.TradeNumber = BizLogicSingleton.Instance.GenerateSubTradeNumberBeforeConfirm(td, parentTrade, childCount, DbContext);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrWhiteSpace(td.TradeNumber))
|
|
{
|
|
td.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(td, DbContext);
|
|
}
|
|
}
|
|
|
|
DbContext.SaveChanges();
|
|
}
|
|
|
|
private void SaveTradeMeta(trade t)
|
|
{
|
|
if (t != null && t.MetaDic != null && t.MetaDic.Count() > 0)
|
|
{
|
|
foreach (var kv in t.MetaDic)
|
|
{
|
|
if (!string.IsNullOrEmpty(kv.Value))
|
|
{
|
|
new TradeServiceBase(UserInfo).AddTradeMeta(false, t.id, kv.Key, kv.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UnwindSwapTradeCashHandle(trade td, string action, double? annualFee, double? costFee)
|
|
{
|
|
var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId);
|
|
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
|
|
|
//增加现金交割交易记录
|
|
var tc = new trade_cash();
|
|
DbContext.trade_cash.Add(tc);
|
|
|
|
tc.OptId = UserId;
|
|
tc.OptName = UserName;
|
|
tc.OptDate = OptDate;
|
|
tc.ExceciseType = "现金";
|
|
tc.TradeType = td.BuySell;
|
|
tc.CallPut = td.CallPut;
|
|
tc.Notional = td.Notional;
|
|
tc.TradeAmount = td.TradeAmount;
|
|
tc.IsLastAction = true;
|
|
tc.TradeId = td.id;
|
|
tc.FinalPrice = td.FinalPrice;
|
|
tc.UnwindType = "全部平仓";
|
|
tc.UnwindNotional = td.Notional;
|
|
tc.UnwindTradeAmount = td.TradeAmount;
|
|
tc.UnwindPercentRate = 1;
|
|
tc.NotionalPercentRate = tc.UnwindPercentRate;
|
|
|
|
var trade_cash_swap = new trade_cash_swap();
|
|
if (td.trade_swap.IsPayFloatingProfit)
|
|
{
|
|
var initialAmountPayQuote = PayoffSwapCalcService.GetInitialAmountSwapPay(td, td.trade_swap, td.SpotPrice ?? 0
|
|
, td.FinalPrice ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0), td.UnWindDate.Value, null);
|
|
|
|
var preSwapDate = td.trade_swap.IncludeFirstDay ? td.StartDate.Value.AddDays(-1) : td.StartDate.Value;
|
|
var extraAmountGetQuote = annualFee ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, td.UnWindDate.Value, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0));
|
|
var extraAmountPayQuote = annualFee ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, td.UnWindDate.Value, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0));
|
|
|
|
var costFeeGetQuote = costFee ?? 0;
|
|
|
|
var costTradePriceGetQuote = 0.0;
|
|
if (!td.trade_swap.IsTradePriceWhenOpen)
|
|
{
|
|
costTradePriceGetQuote = PayoffSwapCalcService.GetCostFee(td, td, tc, true, true);
|
|
}
|
|
|
|
var quoteAmount = extraAmountGetQuote + costFeeGetQuote + costTradePriceGetQuote - initialAmountPayQuote - extraAmountPayQuote;
|
|
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: quoteAmount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
|
|
|
|
var initialAmountPay = initialAmountPayQuote * currencyRate;
|
|
var extraAmountGet = extraAmountGetQuote * currencyRate;
|
|
var extraAmountPay = extraAmountPayQuote * currencyRate;
|
|
var costFeeGet = costFeeGetQuote * currencyRate;
|
|
var costTradePriceGet = costTradePriceGetQuote * currencyRate;
|
|
|
|
td.TradeStatus = tc.ValueDate == td.ExerciseDate ? "已到期" : "已平仓";
|
|
td.StockEqvNotional = 0;
|
|
td.Notional = 0;
|
|
td.TradeAmount = 0;
|
|
td.UnWindNotional = tc.UnwindNotional;
|
|
|
|
tc.Amount = extraAmountGet + costFeeGet + costTradePriceGet - initialAmountPay - extraAmountPay;
|
|
tc.QuoteAmount = quoteAmount;
|
|
tc.CurrencyRate = currencyRate;
|
|
tc.Action = action == "平仓" ? ClientCashInCashOut.系统操作_平仓费 : ClientCashInCashOut.系统操作_互换;
|
|
tc.IsLastAction = true;
|
|
tc.Status = TradeCashStatusEnum.已执行;
|
|
tc.ValueDate = td.UnWindDate.Value;
|
|
tc.ValidState = "Valid";
|
|
tc.ExerciseWay = td.UnWindDate == td.ExerciseDate ? TradeCashExerciseWayEnum.到期行权 : TradeCashExerciseWayEnum.提前终止行权;
|
|
DbContext.SaveChanges();
|
|
|
|
|
|
//增加出入金记录
|
|
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate);
|
|
|
|
var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId);
|
|
|
|
trade_cash_swap.StartDate = td.StartDate.Value;
|
|
trade_cash_swap.PayStartPrice = trade_swap.PayFinalPrice ?? trade_swap.PaySpotPrice;
|
|
trade_cash_swap.PayFinalPrice = tc.FinalPrice;
|
|
trade_cash_swap.PayInitialAmount = initialAmountPay;
|
|
trade_cash_swap.PayExtraAmount = extraAmountPay;
|
|
trade_cash_swap.PayAmount = initialAmountPay + extraAmountPay;
|
|
var customizedResultsPay = QdpHelper.ParseAutocallCustomizedInfo(td.trade_swap.PaySwapTimeAndRate);
|
|
var paySwapRates = customizedResultsPay.Item2;
|
|
trade_cash_swap.PaySwapRate = paySwapRates.FirstOrDefault();
|
|
trade_cash_swap.GetExtraAmount = extraAmountGet;
|
|
trade_cash_swap.GetCostFee = costFeeGet + costTradePriceGet;
|
|
trade_cash_swap.GetAmount = extraAmountGet + costFeeGet + costTradePriceGet;
|
|
var customizedResultsGet = QdpHelper.ParseAutocallCustomizedInfo(td.trade_swap.GetSwapTimeAndRate);
|
|
var getSwapRates = customizedResultsGet.Item2;
|
|
trade_cash_swap.GetSwapRate = getSwapRates.FirstOrDefault();
|
|
trade_cash_swap.TradeId = tc.TradeId;
|
|
trade_cash_swap.TradeCashId = tc.id;
|
|
trade_cash_swap.OptId = tc.OptId;
|
|
trade_cash_swap.OptName = tc.OptName;
|
|
trade_cash_swap.OptDate = DateTime.Now;
|
|
DbContext.trade_cash_swap.Add(trade_cash_swap);
|
|
|
|
var tcdGet = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = extraAmountGet - extraAmountPay,
|
|
QuoteAmount = extraAmountGetQuote - extraAmountPayQuote,
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = true,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now,
|
|
TradeCashType = TradeCashTypeEnum.利息.ToString()
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdGet);
|
|
|
|
var tcdCostFeeGet = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = costFeeGet,
|
|
QuoteAmount = costFeeGetQuote,
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = true,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now,
|
|
TradeCashType = TradeCashTypeEnum.了结手续费.ToString()
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdCostFeeGet);
|
|
|
|
var tcdCostTradePriceGet = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = costTradePriceGet,
|
|
QuoteAmount = costTradePriceGetQuote,
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = true,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now,
|
|
TradeCashType = TradeCashTypeEnum.开仓手续费.ToString()
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdCostTradePriceGet);
|
|
|
|
var tcdPay = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = -initialAmountPay,
|
|
QuoteAmount = -initialAmountPayQuote,
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = false,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now,
|
|
TradeCashType = TradeCashTypeEnum.浮动收益.ToString()
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdPay);
|
|
}
|
|
else
|
|
{
|
|
var initialAmountGetQuote = PayoffSwapCalcService.GetInitialAmountSwapGet(td, td.trade_swap, td.SpotPrice ?? 0
|
|
, td.FinalPrice ?? 0, td.OriginalStockEqvNotional ?? 0, td.UnWindDate.Value, null);
|
|
|
|
var preSwapDate = td.trade_swap.IncludeFirstDay ? td.StartDate.Value.AddDays(-1) : td.StartDate.Value;
|
|
var extraAmountPayQuote = annualFee ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, td.UnWindDate.Value, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0));
|
|
var extraAmountGetQuote = annualFee ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, td.UnWindDate.Value, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0));
|
|
|
|
var costFeePayQuote = costFee ?? 0;
|
|
|
|
var costTradePricePayQuote = 0.0;
|
|
if (!td.trade_swap.IsTradePriceWhenOpen)
|
|
{
|
|
costTradePricePayQuote = PayoffSwapCalcService.GetCostFee(td, td, tc, false, true);
|
|
|
|
}
|
|
|
|
var quoteAmount = initialAmountGetQuote + extraAmountGetQuote - extraAmountPayQuote - costFeePayQuote - costTradePricePayQuote;
|
|
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: quoteAmount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
|
|
|
|
var initialAmountGet = initialAmountGetQuote * currencyRate;
|
|
var extraAmountPay = extraAmountPayQuote * currencyRate;
|
|
var extraAmountGet = extraAmountGetQuote * currencyRate;
|
|
var costFeePay = costFeePayQuote * currencyRate;
|
|
var costTradePricePay = costTradePricePayQuote * currencyRate;
|
|
|
|
tc.Amount = initialAmountGet + extraAmountGet - extraAmountPay - costFeePay - costTradePricePay;
|
|
tc.QuoteAmount = quoteAmount;
|
|
tc.CurrencyRate = currencyRate;
|
|
tc.Action = action == "平仓" ? ClientCashInCashOut.系统操作_平仓费 : ClientCashInCashOut.系统操作_互换;
|
|
tc.IsLastAction = true;
|
|
tc.Status = TradeCashStatusEnum.已执行;
|
|
tc.ValueDate = td.UnWindDate.Value;
|
|
tc.ValidState = "Valid";
|
|
tc.ExerciseWay = td.UnWindDate == td.ExerciseDate ? TradeCashExerciseWayEnum.到期行权 : TradeCashExerciseWayEnum.提前终止行权;
|
|
DbContext.SaveChanges();
|
|
|
|
td.TradeStatus = tc.ValueDate == td.ExerciseDate ? "已到期" : "已平仓";
|
|
td.StockEqvNotional = 0;
|
|
td.Notional = 0;
|
|
td.TradeAmount = 0;
|
|
td.UnWindNotional = tc.UnwindNotional;
|
|
//增加出入金记录
|
|
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate);
|
|
|
|
var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId);
|
|
|
|
trade_cash_swap.StartDate = td.StartDate.Value;
|
|
trade_cash_swap.GetStartPrice = trade_swap.GetFinalPrice ?? trade_swap.GetSpotPrice;
|
|
trade_cash_swap.GetFinalPrice = tc.FinalPrice;
|
|
trade_cash_swap.GetInitialAmount = initialAmountGet;
|
|
trade_cash_swap.GetExtraAmount = extraAmountGet;
|
|
trade_cash_swap.GetAmount = initialAmountGet + extraAmountGet;
|
|
var customizedResultsGet = QdpHelper.ParseAutocallCustomizedInfo(td.trade_swap.GetSwapTimeAndRate);
|
|
var getSwapRates = customizedResultsGet.Item2;
|
|
trade_cash_swap.GetSwapRate = getSwapRates.FirstOrDefault();
|
|
trade_cash_swap.PayExtraAmount = extraAmountPay;
|
|
trade_cash_swap.PayCostFee = costFeePay + costTradePricePay;
|
|
trade_cash_swap.PayAmount = extraAmountPay + costFeePay + costTradePricePay;
|
|
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(td.trade_swap.PaySwapTimeAndRate);
|
|
var paySwapRates = customizedResults.Item2;
|
|
trade_cash_swap.PaySwapRate = paySwapRates.FirstOrDefault();
|
|
trade_cash_swap.TradeId = tc.TradeId;
|
|
trade_cash_swap.TradeCashId = tc.id;
|
|
trade_cash_swap.OptId = tc.OptId;
|
|
trade_cash_swap.OptName = tc.OptName;
|
|
trade_cash_swap.OptDate = DateTime.Now;
|
|
DbContext.trade_cash_swap.Add(trade_cash_swap);
|
|
|
|
var tcdGet = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = extraAmountGet - extraAmountPay,
|
|
QuoteAmount = extraAmountGetQuote - extraAmountPayQuote,
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = true,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now,
|
|
TradeCashType = TradeCashTypeEnum.利息.ToString()
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdGet);
|
|
|
|
var tcdCostFeeGet = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = -costFeePay,
|
|
QuoteAmount = -costFeePayQuote,
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = true,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now,
|
|
TradeCashType = TradeCashTypeEnum.了结手续费.ToString()
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdCostFeeGet);
|
|
|
|
var tcdCostTradePriceGet = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = -costTradePricePay,
|
|
QuoteAmount = -costTradePricePayQuote,
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = true,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now,
|
|
TradeCashType = TradeCashTypeEnum.开仓手续费.ToString()
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdCostTradePriceGet);
|
|
|
|
var tcdPay = new trade_cash_detail
|
|
{
|
|
TradeId = tc.TradeId,
|
|
TradeCashId = tc.id,
|
|
Action = tc.Action,
|
|
Amount = initialAmountGet,
|
|
QuoteAmount = initialAmountGetQuote,
|
|
ValueDate = tc.ValueDate,
|
|
IsForGet = false,
|
|
OptId = tc.OptId,
|
|
OptName = tc.OptName,
|
|
OptDate = DateTime.Now,
|
|
TradeCashType = TradeCashTypeEnum.浮动收益.ToString()
|
|
};
|
|
DbContext.trade_cash_detail.Add(tcdPay);
|
|
}
|
|
|
|
if (td.ParentTradeId > 0)
|
|
{
|
|
var parentTrade = DbContext.trade.Find(td.ParentTradeId);
|
|
if (parentTrade != null)
|
|
{
|
|
parentTrade.UnWindDate = tc.ValueDate;
|
|
parentTrade.StockEqvNotional = 0;
|
|
parentTrade.TradeStatus = "已平仓";
|
|
|
|
var tradeSwapDetail = DbContext.trade_swap_detail.FirstOrDefault(x => x.TradeId == parentTrade.id && x.ChildTradeId == td.id);
|
|
if (tradeSwapDetail != null)
|
|
{
|
|
tradeSwapDetail.Notional = td.Notional;
|
|
}
|
|
|
|
var tradeCash = DbContext.trade_cash.FirstOrDefault(x => x.TradeId == td.ParentTradeId && x.Action == ClientCashInCashOut.系统操作_平仓费 && x.ValidState != "InValid");
|
|
if (tradeCash == null)
|
|
{
|
|
tradeCash = new trade_cash()
|
|
{
|
|
TradeId = td.ParentTradeId,
|
|
Notional = parentTrade.OriginalNotional ?? 0,
|
|
ValueDate = tc.ValueDate,
|
|
Action = tc.Action,
|
|
Amount = 0,
|
|
ExerciseWay = tc.ExerciseWay,
|
|
IsLastAction = true,
|
|
UnwindNotional = parentTrade.OriginalNotional ?? 0,
|
|
UnwindPercentRate = 1,
|
|
OptId = UserId,
|
|
OptName = UserName,
|
|
OptDate = DateTime.Now
|
|
};
|
|
DbContext.trade_cash.Add(tradeCash);
|
|
}
|
|
DbContext.SaveChanges();
|
|
|
|
var tradeCashSwap = DbContext.trade_cash_swap.FirstOrDefault(x => x.TradeCashId == tradeCash.id && x.TradeId == parentTrade.id);
|
|
if (tradeCashSwap == null)
|
|
{
|
|
tradeCashSwap = new trade_cash_swap()
|
|
{
|
|
TradeId = parentTrade.id,
|
|
TradeCashId = tradeCash.id,
|
|
StartDate = parentTrade.StartDate,
|
|
OptId = UserId,
|
|
OptName = UserName,
|
|
OptDate = DateTime.Now
|
|
};
|
|
DbContext.trade_cash_swap.Add(tradeCashSwap);
|
|
}
|
|
tradeCashSwap.GetAmount = (tradeCashSwap.GetAmount ?? 0) + (trade_cash_swap.GetAmount ?? 0);
|
|
tradeCashSwap.GetInitialAmount = (tradeCashSwap.GetInitialAmount ?? 0) + (trade_cash_swap.GetInitialAmount ?? 0);
|
|
tradeCashSwap.GetExtraAmount = (tradeCashSwap.GetExtraAmount ?? 0) + (trade_cash_swap.GetExtraAmount ?? 0);
|
|
tradeCashSwap.GetCostFee = (tradeCashSwap.GetCostFee ?? 0) + (trade_cash_swap.GetCostFee ?? 0);
|
|
tradeCashSwap.PayAmount = (tradeCashSwap.PayAmount ?? 0) + (trade_cash_swap.PayAmount ?? 0);
|
|
tradeCashSwap.PayInitialAmount = (tradeCashSwap.PayInitialAmount ?? 0) + (trade_cash_swap.PayInitialAmount ?? 0);
|
|
tradeCashSwap.PayExtraAmount = (tradeCashSwap.PayExtraAmount ?? 0) + (trade_cash_swap.PayExtraAmount ?? 0);
|
|
tradeCashSwap.PayCostFee = (tradeCashSwap.PayCostFee ?? 0) + (trade_cash_swap.PayCostFee ?? 0);
|
|
|
|
new TradeCashService(this).SaveTradeCashDetail(tradeCash);
|
|
|
|
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(parentTrade, tradeCash, tradeCash.ValueDate, true);
|
|
|
|
tradeCash.Amount += tc.Amount;
|
|
tc.ParentTradeId = td.ParentTradeId;
|
|
tc.ParentTradeCashId = tradeCash.id;
|
|
DbContext.SaveChanges();
|
|
}
|
|
}
|
|
|
|
DbContext.SaveChanges();
|
|
}
|
|
}
|
|
}
|