897 lines
36 KiB
C#
897 lines
36 KiB
C#
using Org.BouncyCastle.Ocsp;
|
||
using System.Data;
|
||
using YLErp.BLL;
|
||
using YLErp.Commons;
|
||
using YLErp.Configuration.Enums;
|
||
using YLErp.DBModels;
|
||
using YLErp.DBModels.Enums;
|
||
using YLErp.DBModels.Helpers;
|
||
using YLErp.Helpers;
|
||
using YLErp.Model;
|
||
using YLErp.Model.Enum;
|
||
using YLErp.Modules.TradeModule.DealModule;
|
||
using YLErp.Modules.TradeModule.OrderModule;
|
||
using YLErp.QdpModule;
|
||
|
||
namespace YLErp.Modules.TradeModule.ForwardModule
|
||
{
|
||
public class ForwardTradeImportService : TradeCashService
|
||
{
|
||
|
||
public ForwardTradeImportService(OptUserInfo userInfo) : base(userInfo)
|
||
{
|
||
}
|
||
|
||
public void ImportFromExcel(Stream streamIn, string TypeName, out int totalNum, out int successNum)
|
||
{
|
||
totalNum = 0;
|
||
successNum = 0;
|
||
|
||
var rowIndex = 0;
|
||
try
|
||
{
|
||
var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0);
|
||
|
||
if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 2)
|
||
{
|
||
throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" };
|
||
}
|
||
|
||
var table = ds.Tables[0];
|
||
var reader = new DataRowReader(table, 0);
|
||
|
||
rowIndex = 1;
|
||
totalNum = table.Rows.Count - rowIndex;
|
||
|
||
foreach (var row in table.Rows.Cast<DataRow>().Skip(1))
|
||
{
|
||
using (var trans = BeginTransaction())
|
||
{
|
||
rowIndex++;
|
||
if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString())))
|
||
{
|
||
totalNum--;
|
||
continue;
|
||
}
|
||
reader.SetDataRow(row);
|
||
|
||
|
||
switch (TypeName)
|
||
{
|
||
case "远期交易":
|
||
HandleForwardTrade(reader, ForwardTradePriceModel.STANDARD);//标准模式导入
|
||
break;
|
||
case "远期交易挂钩标的":
|
||
HandleForwardTrade(reader, ForwardTradePriceModel.HOOK_PRICE);//挂钩标的模式导入
|
||
break;
|
||
default:
|
||
HandleFinishForwardTrade(reader);//批量了结导入
|
||
break;
|
||
|
||
}
|
||
successNum++;
|
||
|
||
trans.Commit();
|
||
}
|
||
}
|
||
}
|
||
catch (ServiceException se)
|
||
{
|
||
if (se.Tag != null)
|
||
{
|
||
throw;
|
||
}
|
||
|
||
throw new ServiceException($"已成功导入{successNum}条;\n第{rowIndex}行,{se.Message}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogFactory.GetLogger(TypeName).Error(ex);
|
||
throw new ServiceException($"已成功导入{successNum}条,\n第{rowIndex}行,发生错误:{ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
#region 远期交易导入
|
||
|
||
/// <summary>
|
||
/// 处理每行数据
|
||
/// </summary>
|
||
/// <param name="reader"></param>
|
||
public void HandleForwardTrade(DataRowReader reader, ForwardTradePriceModel priceModel)
|
||
{
|
||
OtcTradeForward td = new OtcTradeForward();
|
||
td = MapForwardTrade(reader, priceModel);
|
||
|
||
var otcOptionTradeFullEx = new OtcOptionTradeFullEx { ClientName = td.ClientName };
|
||
new OtcOptionSaveChecker(this).CheckAssetBook(td).CheckTrader(td).CheckClient(otcOptionTradeFullEx);
|
||
td.ClientId = otcOptionTradeFullEx.ClientId;
|
||
|
||
CalculatedForwardFieldValue(td, reader);
|
||
|
||
td.TradeType = "远期";
|
||
|
||
var mapTd = TradeConverter.ConvertForward(td);
|
||
new TradeSaveService(OptUser).SaveTrade(mapTd, TradeSourceEnum.导入交易);
|
||
}
|
||
|
||
public OtcTradeForward MapForwardTrade(DataRowReader reader, ForwardTradePriceModel priceModel)
|
||
{
|
||
var td = new OtcTradeForward
|
||
{
|
||
TradeNumber = reader.GetString("交易编号", false),
|
||
AssetBookName = reader.GetString("簿记账户名称", true),
|
||
TraderName = reader.GetString("交易员名称", true),
|
||
ClientName = reader.GetString("交易对手方名称", true),
|
||
StructureType = reader.GetString("结构类型", true),
|
||
//UnderlyingCode = reader.GetString("标的代码", true),
|
||
//BasisUnderlyingCode = reader.GetString("挂钩标的代码", false),
|
||
//BasisGap = reader.GetDouble("基差", false),
|
||
BuySell = reader.GetString("交易方向", true),
|
||
OptionType = reader.GetString("多空方向", false),
|
||
TradeAmount = reader.GetDouble("成交数量", false) ?? 0,
|
||
Lots = reader.GetDouble("手数", false),
|
||
Strike = reader.GetDouble("交割价格", true),
|
||
//SpotPrice = reader.GetDouble("期初标的价格", true) ?? 0,
|
||
AnnualMarginRate = reader.GetPercent("年化预付金成本", false) ?? 0,
|
||
AnnualStoragePrice = reader.GetDouble("年化仓储成本", false) ?? 0,
|
||
NoRiskRate = reader.GetPercent("无风险利率", false) ?? 0,
|
||
MarginTemplateName = reader.GetString("预付金模板", false),
|
||
TradeDate = reader.GetDate("成交日期", true),
|
||
ExerciseDate = reader.GetDate("到期日期", true),
|
||
|
||
SettlementDate = reader.GetDate("结算日期", true),
|
||
ObservationDates = reader.GetString("均价结算日", false),
|
||
Comments = reader.GetString("备注", false)
|
||
};
|
||
string IsSupplyForwardValue = reader.GetString("是否补偿远期价值", true);
|
||
|
||
if (IsSupplyForwardValue == "补偿" || IsSupplyForwardValue == "是")
|
||
{
|
||
td.MetaDic.Add("IsSupplyForwardValue", "1");
|
||
}
|
||
else if (IsSupplyForwardValue == "支付")
|
||
{
|
||
td.MetaDic.Add("IsSupplyForwardValue", "-1");
|
||
}
|
||
else if (IsSupplyForwardValue == "否")
|
||
{
|
||
td.MetaDic.Add("IsSupplyForwardValue", "0");
|
||
}
|
||
else
|
||
{
|
||
//添加拓展字段 是否补充远期价值,Type2:默认值 -1,其他默认值 0
|
||
if (PS.Config.ErpElement.ForwardValueIsSupplyOrPay == YLErp.Configuration.Enums.ForwardValueIsSupplyOrPay.Type2)
|
||
{
|
||
td.MetaDic.Add("IsSupplyForwardValue", "-1");
|
||
}
|
||
else
|
||
{
|
||
td.MetaDic.Add("IsSupplyForwardValue", "0");
|
||
}
|
||
}
|
||
|
||
if (priceModel == ForwardTradePriceModel.STANDARD)
|
||
{
|
||
td.UnderlyingCode = reader.GetString("标的1代码", true);
|
||
|
||
|
||
//判断是否组合标的
|
||
var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(td.UnderlyingCode);
|
||
if (synthetic != null)
|
||
{
|
||
|
||
var model = synthetic.GetSyntheticPriceModel();
|
||
|
||
var basisUnderlyingCode = reader.GetString("标的2代码", false);
|
||
var basisUnderlyingPrice = reader.GetString("标的2期初价格", false);
|
||
if (!string.IsNullOrEmpty(basisUnderlyingCode) || !string.IsNullOrEmpty(basisUnderlyingPrice))
|
||
{
|
||
throw new ServiceException("[组合标的]标的2代码必须为空");
|
||
}
|
||
|
||
var codeSet = synthetic.GetUnderlyingCodes().ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||
double SpotPrice = 0;
|
||
List<string> spList = new List<string>();
|
||
for (var i = 1; i <= 4; i++)
|
||
{
|
||
var code = reader.GetString("组合标的" + i + "代码", false);
|
||
|
||
if (string.IsNullOrWhiteSpace(code))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (!codeSet.Remove(code))
|
||
{
|
||
throw new ServiceException($"[组合标的]{i}_代码 填写错误,组合标的中不存在此标的:{code}");
|
||
}
|
||
|
||
var curSportprice = reader.GetDouble("组合标的" + i + "期初价格", true).Value;
|
||
|
||
var curUC = model.SuList.FirstOrDefault(n => n.UnderlyingCode == code);
|
||
|
||
if (curUC != null)
|
||
{
|
||
curUC.Price = curSportprice;
|
||
|
||
SpotPrice += curUC.Price * curUC.Coefficient;
|
||
}
|
||
|
||
}
|
||
if (codeSet.Any())
|
||
{
|
||
throw new ServiceException("[组合标的]未填写完整");
|
||
}
|
||
spList.Add(SpotPrice.OtcFormatUmPrice());
|
||
td.SpotPrice = SpotPrice;
|
||
td.MetaDic["期初信息"] = spList.ToJson();
|
||
td.MetaDic["组合标的"] = model.ToJson();
|
||
}
|
||
else
|
||
{
|
||
double SpotPrice = reader.GetDouble("标的1期初价格", true) ?? 0;
|
||
td.BasisUnderlyingCode = reader.GetString("标的2代码", false);
|
||
double? SpotPrice2 = reader.GetDouble("标的2期初价格", false);
|
||
//string[] sp = new string[] { SpotPrice.OtcFormatUmPrice(), SpotPrice2 };
|
||
List<string> spList = new List<string>();
|
||
spList.Add(SpotPrice.ToString("0.####"));
|
||
if (!string.IsNullOrEmpty(td.BasisUnderlyingCode))
|
||
{
|
||
|
||
if (SpotPrice2 == null)
|
||
{
|
||
throw new ServiceException("填写标的2代码,则必须填写标的2期初价格");
|
||
}
|
||
else
|
||
{
|
||
spList.Add((SpotPrice2 ?? 0).ToString("0.####"));
|
||
}
|
||
}
|
||
|
||
td.SpotPrice = (double)((decimal)SpotPrice - (decimal)(SpotPrice2 ?? 0));
|
||
td.MetaDic["期初信息"] = spList.ToJson();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
td.UnderlyingCode = reader.GetString("标的代码", true);
|
||
td.BasisUnderlyingCode = reader.GetString("挂钩标的代码", false);
|
||
td.BasisGap = reader.GetDouble("基差", false);
|
||
GetSpotPrice(td);
|
||
}
|
||
//var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
||
|
||
if (PS.Config.Is浙期)
|
||
{
|
||
|
||
var flag = false;
|
||
|
||
var codes = new List<string>();
|
||
|
||
codes.Add(td.UnderlyingCode);
|
||
codes.Add(td.BasisUnderlyingCode);
|
||
|
||
//判断标的是否为现货
|
||
flag = UndelyingHelper.IsCodesExistsCommoditySpot(codes, (um) =>
|
||
{
|
||
return um.UnderlyingInstrumentType == "CommoditySpot" ? true : false;
|
||
});
|
||
|
||
if (flag)
|
||
{
|
||
var dateStr = reader.GetDate("实际到期日期", false) ?? td.ExerciseDate;
|
||
|
||
|
||
td.MetaDic["ActualExerciseDate"] = dateStr.Value.ToString("yyyy-MM-dd");
|
||
}
|
||
|
||
|
||
}
|
||
//DateTime.TryParse(dateStr, out var date);
|
||
|
||
|
||
switch (td.BuySell)
|
||
{
|
||
case "Buy":
|
||
td.BuySell = "买入";
|
||
break;
|
||
case "Sell":
|
||
td.BuySell = "卖出";
|
||
break;
|
||
case "买入":
|
||
case "卖出":
|
||
break;
|
||
default:
|
||
throw new ServiceException("交易方向 填写错误:" + td.BuySell);
|
||
}
|
||
|
||
td.OptionType = GetOptionType(td.OptionType, td.BuySell);
|
||
|
||
if (string.IsNullOrEmpty(td.MarginTemplateName))
|
||
{
|
||
td.MarginTemplateName = "系统默认";
|
||
td.MarginRate = 0;
|
||
td.PositionMarginRate = 0;
|
||
}
|
||
else
|
||
{
|
||
var marginTemplateList = GetMarginTemplateItems();
|
||
if (marginTemplateList.Any(a => a.Name.Equals(td.MarginTemplateName)))
|
||
{
|
||
switch (td.MarginTemplateName)
|
||
{
|
||
case "系统默认":
|
||
td.MarginType = MarginTypeEnum.DEFAULT;
|
||
td.MarginRate = 0;
|
||
td.PositionMarginRate = 0;
|
||
break;
|
||
case "无预付金":
|
||
td.MarginType = MarginTypeEnum.NONE;
|
||
td.MarginRate = 0;
|
||
td.PositionMarginRate = 0;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
var MarginRate = reader.GetDouble("初始预付金率", false);
|
||
var PositionMarginRate = reader.GetDouble("持仓预付金率", false);
|
||
|
||
td.MarginRate = MarginRate ?? 0;
|
||
td.PositionMarginRate = PositionMarginRate ?? 0;
|
||
|
||
var marginTemplate = marginTemplateList.Where(a => a.Name.Equals(td.MarginTemplateName)).FirstOrDefault();
|
||
td.MarginType = (MarginTypeEnum)marginTemplate.MarginType;
|
||
if (MarginRate == null)
|
||
{
|
||
td.MarginRate = marginTemplate.InitialMarginRatio ?? 0;
|
||
}
|
||
if (PositionMarginRate == null)
|
||
{
|
||
td.PositionMarginRate = marginTemplate.PositionMarginRatio ?? 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
throw new ServiceException("预付金模板填写错误:" + td.MarginTemplateName);
|
||
}
|
||
}
|
||
|
||
if (QdpCalendarHelper.IsHoliday((DateTime)td.TradeDate))
|
||
{
|
||
throw new ServiceException("成交日期:" + td.TradeDate + ",不能为节假日");
|
||
}
|
||
if (QdpCalendarHelper.IsHoliday((DateTime)td.ExerciseDate))
|
||
{
|
||
throw new ServiceException("到期日期:" + td.ExerciseDate + ",不能为节假日");
|
||
}
|
||
if (QdpCalendarHelper.IsHoliday((DateTime)td.SettlementDate))
|
||
{
|
||
throw new ServiceException("结算日期:" + td.SettlementDate + ",不能为节假日");
|
||
}
|
||
if (td.TradeDate > td.ExerciseDate)
|
||
{
|
||
throw new ServiceException("交易日应该在到期日之前");
|
||
}
|
||
if (td.SettlementDate < td.ExerciseDate)
|
||
{
|
||
throw new ServiceException("结算日期不能小于到期日期");
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(td.ObservationDates))
|
||
{
|
||
DateTime dt = new DateTime();
|
||
if (td.ObservationDates.Contains(','))
|
||
{
|
||
string[] arrayDate = td.ObservationDates.Split(',');
|
||
if (!arrayDate.Any(a => DateTime.TryParse(a, out dt)))
|
||
throw new ServiceException("均价结算日日期格式填写错误" + td.ObservationDates);
|
||
}
|
||
else
|
||
{
|
||
|
||
if (!DateTime.TryParse(td.ObservationDates, out dt))
|
||
throw new ServiceException("均价结算日日期格式填写错误" + td.ObservationDates);
|
||
}
|
||
}
|
||
|
||
#region 观察频率写入metadic表中
|
||
string termStr = null;
|
||
var ruleStr = reader.GetString("观察周期", false);
|
||
if (!string.IsNullOrWhiteSpace(ruleStr))
|
||
{
|
||
|
||
switch (ruleStr)
|
||
{
|
||
case "每日":
|
||
termStr = "1D";
|
||
break;
|
||
case "每周":
|
||
termStr = "1W";
|
||
break;
|
||
case "每月":
|
||
termStr = "1M";
|
||
break;
|
||
case "每年":
|
||
termStr = "1Y";
|
||
break;
|
||
}
|
||
if (termStr == null)
|
||
{
|
||
var strs = ruleStr.Split(new[] { ',' });
|
||
termStr = strs[0].Trim();
|
||
}
|
||
//敲入观察周期写入metadic中
|
||
td.MetaDic["敲入观察周期"] = termStr;
|
||
}
|
||
#endregion
|
||
|
||
return td;
|
||
}
|
||
|
||
public string GetOptionType(string OptionType, string BuySell = "")
|
||
{
|
||
switch (OptionType)
|
||
{
|
||
case "多头":
|
||
case "Call":
|
||
return "看涨";
|
||
case "空头":
|
||
case "Put":
|
||
return "看跌";
|
||
case "看涨":
|
||
case "看跌":
|
||
return OptionType;
|
||
case "":
|
||
case null:
|
||
return BuySell == "买入" ? "看涨" : "看跌";
|
||
default:
|
||
throw new ServiceException("多空方向 填写错误:" + OptionType);
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 计算远期字段值
|
||
/// </summary>
|
||
/// <param name="td"></param>
|
||
/// <returns></returns>
|
||
public void CalculatedForwardFieldValue(OtcTradeForward td, DataRowReader reader)
|
||
{
|
||
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
||
|
||
if (um == null)
|
||
{
|
||
throw new ServiceException("系统中不存在,导入的标的1代码");
|
||
}
|
||
else if (!string.IsNullOrEmpty(td.BasisUnderlyingCode))
|
||
{
|
||
if (DataCacheProvider.GetUnderlyingDataSource().GetData(td.BasisUnderlyingCode) == null)
|
||
{
|
||
throw new ServiceException("系统中不存在,导入的标的2代码");
|
||
}
|
||
|
||
}
|
||
//GetSpotPrice(td);
|
||
|
||
GetCountRatio(td);
|
||
|
||
GetNotional_TradeAmount_Lots(td, um);
|
||
|
||
GetForwardValue(td);
|
||
|
||
GetOpenCommission_TradePrice(td, reader, null, null);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取期初价格
|
||
/// </summary>
|
||
/// <param name="td"></param>
|
||
public void GetSpotPrice(OtcTradeForward td)
|
||
{
|
||
//计算期初价格 期初价格 = 挂钩标的价格 + 基差
|
||
if (!string.IsNullOrWhiteSpace(td.BasisUnderlyingCode))
|
||
{
|
||
var umBasis = DataCacheProvider.GetUnderlyingDataSource().GetData(td.BasisUnderlyingCode);
|
||
td.SpotPrice = umBasis.Price + (td.BasisGap ?? 0);
|
||
}
|
||
else
|
||
{
|
||
var umBasis = DataCacheProvider.GetUnderlyingDataSource().GetData(td.BasisUnderlyingCode);
|
||
td.SpotPrice = umBasis.Price;
|
||
}
|
||
}
|
||
|
||
public void GetCountRatio(OtcTradeForward td)
|
||
{
|
||
var CountRatio = td.CountRatio ?? 0;
|
||
if (CountRatio < 1)
|
||
{
|
||
CountRatio = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode)?.CountRatio ?? 1;
|
||
}
|
||
td.CountRatio = CountRatio;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取份额,成交数量,手数
|
||
/// </summary>
|
||
/// <param name="td"></param>
|
||
/// <param name="um"></param>
|
||
public void GetNotional_TradeAmount_Lots(OtcTradeForward td, underlying_manager um)
|
||
{
|
||
if (td.Lots < 0)
|
||
throw new ServiceException("手数不能为负数");
|
||
if (td.TradeAmount < 0)
|
||
throw new ServiceException("成交数量不能为负数");
|
||
if (td.TradeAmount == 0 && td.Lots == 0)
|
||
throw new ServiceException("成交数量与手数,两者必须填写一个");
|
||
if (td.Lots > 0)
|
||
{
|
||
td.Notional = (double)td.Lots * um.ContractSize;
|
||
td.TradeAmount = td.Notional / (double)td.CountRatio;
|
||
}
|
||
else if (td.TradeAmount > 0)
|
||
{
|
||
td.Notional = td.TradeAmount * (double)td.CountRatio;
|
||
td.Lots = td.Notional / um.ContractSize;
|
||
}
|
||
td.OriginalNotional = td.Notional;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取远期价值
|
||
/// (交割价格- 期初价格) * 成交数量 * CountRatio *(多-1,空1)]*(买入1,卖出 - 1)
|
||
/// </summary>
|
||
/// <param name="td"></param>
|
||
public void GetForwardValue(OtcTradeForward td)
|
||
{
|
||
var OptionType = GetOptionType(td.OptionType, td.BuySell);
|
||
var ForwardValue = (td.Strike - td.SpotPrice) * td.TradeAmount * td.CountRatio * (OptionType == "看涨" ? -1 : 1) * (td.BuySell == "买入" ? 1 : -1);
|
||
td.ForwardValue = ForwardValue ?? 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取远期价值
|
||
/// </summary>
|
||
/// <param name="Strike"></param>
|
||
/// <param name="SpotPrice"></param>
|
||
/// <param name="TradeAmount"></param>
|
||
/// <param name="CountRatio"></param>
|
||
/// <param name="OptionType"></param>
|
||
/// <param name="BuySell"></param>
|
||
/// <returns></returns>
|
||
public double GetForwardValue(double Strike, double SpotPrice, double TradeAmount, int CountRatio, string OptionType, string BuySell)
|
||
{
|
||
OptionType = GetOptionType(OptionType, BuySell);
|
||
return (Strike - SpotPrice) * TradeAmount * CountRatio * (OptionType == "看涨" ? -1 : 1) * (BuySell == "买入" ? 1 : -1);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取远期价值
|
||
/// </summary>
|
||
/// <param name="Strike"></param>
|
||
/// <param name="SpotPrice"></param>
|
||
/// <param name="TradeAmount"></param>
|
||
/// <param name="CountRatio"></param>
|
||
/// <param name="OptionType"></param>
|
||
/// <param name="BuySell"></param>
|
||
/// <returns></returns>
|
||
public double GetForwardValue(double Strike, double SpotPrice, double OriginalNotional, string OptionType, string BuySell)
|
||
{
|
||
OptionType = GetOptionType(OptionType, BuySell);
|
||
return (Strike - SpotPrice) * OriginalNotional * (OptionType == "看涨" ? -1 : 1) * (BuySell == "买入" ? 1 : -1);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取开仓费用,开仓总费用
|
||
/// </summary>
|
||
/// <param name="td"></param>
|
||
public void GetOpenCommission_TradePrice(OtcTradeForward td, DataRowReader reader, double? OpenCommission, double? TradePrice)
|
||
{
|
||
if (reader != null)
|
||
{
|
||
OpenCommission = reader.GetDouble("开仓费用", false);
|
||
TradePrice = reader.GetDouble("开仓总费用", false);
|
||
}
|
||
if (OpenCommission == null && TradePrice == null)
|
||
{
|
||
throw new ServiceException("开仓费用与开仓总费用,两者必须填写一个");
|
||
}
|
||
|
||
var supplyPrice = td.MetaDic["IsSupplyForwardValue"] == "1" ? td.ForwardValue : td.MetaDic["IsSupplyForwardValue"] == "-1" ? -td.ForwardValue : 0;
|
||
//补偿远期价值+开仓费用
|
||
if (OpenCommission != null && TradePrice == null)
|
||
{
|
||
td.TradePrice = supplyPrice + OpenCommission * td.Lots;
|
||
td.OpenCommission = OpenCommission.Value;
|
||
}
|
||
//开仓费用 = (开仓总费用 -(补充远期价值))/ 手数
|
||
else if ((OpenCommission != null && TradePrice != null) || (OpenCommission == null && TradePrice != null))
|
||
{
|
||
td.TradePrice = TradePrice;
|
||
td.OpenCommission = ((td.TradePrice - supplyPrice) / td.Lots) ?? 0;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 远期交易批量了结导入
|
||
public void HandleFinishForwardTrade(DataRowReader reader)
|
||
{
|
||
TradeCashImportReq tcReq = MapFinishForward(reader);
|
||
var td = DbContext.trade.FirstOrDefault(t => t.TradeNumber == tcReq.TradeNumber && t.ValidState != "InValid");
|
||
if (td == null)
|
||
{
|
||
throw new ServiceException("交易数据 不存在,交易编号:" + tcReq.TradeNumber);
|
||
}
|
||
if (td.TradeStatus != ConsTrade.确认成交)
|
||
{
|
||
throw new ServiceException("只有交易状态为‘确认成交’,才能进行批量了结导入");
|
||
}
|
||
|
||
td.trade_cash = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == td.id && t.Action == "系统操作-期权费" && t.ValidState != "InValid" && t.IsDeleted == false);
|
||
|
||
var otcTradeForward = new TradeForwardService(OptUser).GetDetail(td.id);
|
||
|
||
if ((tcReq.UnwindTradeAmount ?? 0) == 0 && tcReq.UnwindType == "部分平仓")
|
||
{
|
||
throw new ServiceException("平仓数量不能为0或空值");
|
||
}
|
||
if (tcReq.UnwindTradeAmount > td.TradeAmount)
|
||
{
|
||
throw new ServiceException("平仓数量不能大于持仓数量");
|
||
}
|
||
else if (tcReq.UnwindTradeAmount < td.TradeAmount && tcReq.UnwindType == "全部平仓")
|
||
{
|
||
throw new ServiceException("全部平仓时,平仓数量等于持仓数量");
|
||
}
|
||
|
||
|
||
if (tcReq.UnwindPriceCheck == null && tcReq.UnwindFee == null)
|
||
{
|
||
throw new ServiceException("当每手平仓费用未填,则平仓总费用必填");
|
||
}
|
||
|
||
CheckValueDate(td, tcReq.ValueDate);
|
||
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
||
var CountRatio = td.CountRatio ?? 0;
|
||
if (CountRatio < 1)
|
||
{
|
||
CountRatio = um?.CountRatio ?? 1;
|
||
}
|
||
td.CountRatio = CountRatio;
|
||
|
||
var eodpriceProvider = new YLErp.Modules.DataProviderModule.EodPriceProvider((DateTime)tcReq.ValueDate);
|
||
|
||
if (tcReq.FinalPrice == null)
|
||
{
|
||
tcReq.FinalPrice = eodpriceProvider.GetPrice(td.UnderlyingCode, SettlementTypeEnum.ClosePrice);//平仓当天标的价格;
|
||
}
|
||
|
||
if (tcReq.UnwindTradeAmount == td.TradeAmount && tcReq.UnwindType == "部分平仓")
|
||
{
|
||
tcReq.UnwindType = "全部平仓";
|
||
}
|
||
|
||
if (tcReq.UnwindType == "全部平仓")
|
||
{
|
||
tcReq.UnwindTradeAmount = td.TradeAmount;
|
||
}
|
||
|
||
var unwindNotional = tcReq.UnwindTradeAmount * td.CountRatio;
|
||
|
||
double extraAmount = ComputeExtraAmount(td, tcReq) ?? 0;
|
||
|
||
tcReq.UnwindForwardValue = GetForwardValue(otcTradeForward.Strike ?? 0, tcReq.FinalPrice ?? 0, tcReq.UnwindTradeAmount ?? 0, td.CountRatio ?? 1, td.OptionType, td.BuySell);
|
||
tcReq.UnwindRatio = (unwindNotional ?? 0) / (td.OriginalNotional ?? 0);
|
||
|
||
var supplyPrice = otcTradeForward.MetaDic["IsSupplyForwardValue"] == "1" ? otcTradeForward.ForwardValue : otcTradeForward.MetaDic["IsSupplyForwardValue"] == "-1" ? -otcTradeForward.ForwardValue : 0;
|
||
if (tcReq.UnwindFee == null)
|
||
{
|
||
tcReq.UnwindPriceCheck = tcReq.UnwindPriceCheck ?? 0;
|
||
//平仓的总费用 =远期收益+每手平仓费用×平仓手数-(开仓时补偿的远期价值)×平仓数量/成交数量+预付金成本
|
||
//其中开仓时补偿的远期价值 = 持仓价值(期初)*Flag
|
||
var unwindPrice = (tcReq.UnwindPriceCheck * td.Lots * tcReq.UnwindRatio) ?? 0;
|
||
tcReq.UnwindFee = tcReq.UnwindForwardValue + unwindPrice - supplyPrice * tcReq.UnwindRatio + extraAmount;
|
||
}
|
||
else
|
||
{
|
||
tcReq.UnwindFee = tcReq.UnwindFee ?? 0;
|
||
//平仓费用 = (平仓的总费用 +(开仓时补偿的远期价值* 平仓比例)- 远期收益 - 预付金成本)/ (平仓比例 *手数)
|
||
tcReq.UnwindPriceCheck = (tcReq.UnwindFee + supplyPrice * tcReq.UnwindRatio - tcReq.UnwindForwardValue - extraAmount) / (otcTradeForward.Lots * tcReq.UnwindRatio);
|
||
}
|
||
|
||
TradeCashReq req = new TradeCashReq();
|
||
req.ValidState = "Valid";
|
||
req.UnwindFee = tcReq.UnwindFee ?? 0;
|
||
req.Notional = td.Notional;
|
||
req.TradeAmount = td.TradeAmount;
|
||
req.UnwindPrice = tcReq.UnwindPriceCheck;
|
||
req.FinalPrice = tcReq.FinalPrice;
|
||
req.UnwindType = tcReq.UnwindType;
|
||
req.UnwindNotional = (tcReq.UnwindTradeAmount * td.CountRatio) ?? 0;
|
||
req.UnwindPercentRate = td.OriginalNotional > 0 ? (req.UnwindNotional / td.OriginalNotional) ?? 0 : 0;
|
||
req.UnwindPricePercentRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice);
|
||
req.ExtraAmount = extraAmount;
|
||
req.ValueDate = tcReq.ValueDate;
|
||
//增加现金交割交易记录
|
||
var tc = CloseTrade_TradeCashSave(td, req, false, !false, !false);
|
||
new TradeCashService(this).SaveTradeCashDetail(tc);
|
||
|
||
td.TradeSource = TradeSourceEnum.导入交易.ToString();
|
||
td.OptId = UserId;
|
||
td.OptName = UserName;
|
||
td.OptDate = DateTime.Now;
|
||
|
||
td.StockEqvNotional -= (double)(td.OriginalStockEqvNotional * tc.UnwindPercentRate);
|
||
td.Notional -= (double)(td.OriginalNotional * tc.NotionalPercentRate);
|
||
td.TradeAmount = td.Notional / um.CountRatio;
|
||
td.UnWindNotional = tc.UnwindNotional;
|
||
td.UnWindDate = tc.ValueDate;
|
||
|
||
if (tc.UnwindType == "全部平仓" || (((decimal)(td.OriginalNotional ?? 0) - (decimal)req.UnwindNotional) == 0 && tc.UnwindType == "部分平仓"))
|
||
{
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
tc.IsLastAction = true;
|
||
}
|
||
else
|
||
{
|
||
td.TradeStatus = ConsTrade.确认成交;
|
||
td.HasPartialUnWind = 1;
|
||
tc.IsLastAction = false;
|
||
}
|
||
DbContext.SaveChanges();
|
||
|
||
//增加出入金记录
|
||
var ee = new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate);
|
||
|
||
//删除E/Bod数据
|
||
RemoveEodTradeAndFutureInfo(false, td.id, tc.ValueDate);
|
||
|
||
AddTradeAuditLog(td.id);
|
||
}
|
||
|
||
|
||
public TradeCashImportReq MapFinishForward(DataRowReader reader)
|
||
{
|
||
var tc = new TradeCashImportReq
|
||
{
|
||
TradeNumber = reader.GetString("交易编号", true),
|
||
UnwindType = reader.GetString("平仓类型", true),
|
||
UnwindTradeAmount = reader.GetDouble("平仓数量", false),
|
||
ValueDate = (DateTime)reader.GetDate("平仓日期", true),
|
||
FinalPrice = reader.GetDouble("标的价格", false),
|
||
UnwindPriceCheck = reader.GetDouble("每手平仓费用", false),
|
||
UnwindFee = reader.GetDouble("平仓总费用", false)
|
||
};
|
||
|
||
|
||
if (QdpCalendarHelper.IsHoliday((DateTime)tc.ValueDate))
|
||
{
|
||
throw new ServiceException("平仓日期:" + tc.ValueDate + ",不能为节假日");
|
||
}
|
||
|
||
return tc;
|
||
}
|
||
/// <summary>
|
||
/// 验证平仓日期
|
||
/// </summary>
|
||
/// <param name="td"></param>
|
||
/// <param name="ValueDate"></param>
|
||
/// <exception cref="ServiceException"></exception>
|
||
public void CheckValueDate(trade td, DateTime ValueDate)
|
||
{
|
||
|
||
if (td.TradeDate > ValueDate)
|
||
{
|
||
throw new ServiceException("平仓日期必须要大于或等于成交日期");
|
||
}
|
||
if (ValueDate > td.ExerciseDate)
|
||
{
|
||
throw new ServiceException("平仓日期必须要小于或等于到期日期");
|
||
}
|
||
if (ValueDate > valuedateBLL.ValueDate)
|
||
{
|
||
throw new ServiceException("平仓日期必须要小于或等于系统日期");
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 计算预付金成本
|
||
/// </summary>
|
||
/// <param name="td"></param>
|
||
/// <param name="tcReq"></param>
|
||
/// <returns></returns>
|
||
public double? ComputeExtraAmount(trade td, TradeCashImportReq tcReq)
|
||
{
|
||
var valueDate = valuedateBLL.ValueDate;
|
||
|
||
if (valueDate > td.ExerciseDate)
|
||
{
|
||
valueDate = td.ExerciseDate.Value;
|
||
}
|
||
|
||
var lastMarginRecord = DbContext.eod_forward_margin.Where(f => f.TradeId == td.id && f.ValueDate < SystemValueDate).OrderByDescending(x => x.ValueDate).FirstOrDefault();
|
||
|
||
if (lastMarginRecord != null)
|
||
{
|
||
var totaldays = (valuedateBLL.ValueDate.Date - lastMarginRecord.ValueDate.Date).TotalDays;
|
||
double HolidayMargin = lastMarginRecord.SettlePrice * lastMarginRecord.MarginRate * lastMarginRecord.AnnualRate * totaldays / 365;
|
||
var unwindAmount = tcReq.UnwindTradeAmount;
|
||
var holidayMarginTotal = unwindAmount * HolidayMargin;
|
||
var lastMargin = (lastMarginRecord.MarginSum - lastMarginRecord.CloseMarginSum) * ((unwindAmount) / lastMarginRecord.Notional) + lastMarginRecord.CloseMarginSum;
|
||
return lastMargin + holidayMarginTotal;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算 交易员角度的远期价值
|
||
/// </summary>
|
||
/// <param name="buySell"></param>
|
||
/// <param name="pptionType"></param>
|
||
/// <param name="strike"></param>
|
||
/// <param name="FinalPrice"></param>
|
||
/// <param name="unwindNotional"></param>
|
||
/// <returns></returns>
|
||
public double ComputForwardValue(string buySell, string optionType, double strike, double FinalPrice, double unwindNotional)
|
||
{
|
||
//(行权价-标的价格)* 平仓份额
|
||
decimal ForwardValue = 0;
|
||
if ((buySell == "卖出" && optionType == "看跌") || (buySell == "买入" && optionType == "看涨"))
|
||
{
|
||
ForwardValue = ((decimal)strike - (decimal)FinalPrice) * (decimal)unwindNotional;
|
||
}
|
||
else if ((buySell == "卖出" && optionType == "看涨") || (buySell == "买入" && optionType == "看跌"))
|
||
{
|
||
ForwardValue = -(((decimal)strike - (decimal)FinalPrice) * (decimal)unwindNotional);
|
||
}
|
||
return (double)ForwardValue;
|
||
}
|
||
|
||
public void AddTradeAuditLog(int TradeId)
|
||
{
|
||
var auditLog = new TradeAuditLog
|
||
{
|
||
TradeId = TradeId,
|
||
Changes = null,
|
||
DataType = "00",
|
||
OptType = "批量了结-平仓",
|
||
OptId = UserId,
|
||
OptName = UserName,
|
||
OptDate = OptDate,
|
||
AuditFlag = TradeAuditFlag.operation
|
||
};
|
||
//记录审核日志
|
||
DbContext.TradeAuditLog.Add(auditLog);
|
||
DbContext.SaveChanges();
|
||
}
|
||
|
||
public List<margin_template> GetMarginTemplateItems()
|
||
{
|
||
var marginTemplates = DbContext.margin_template.ToList();
|
||
List<margin_template> strList = new List<margin_template>();
|
||
strList = marginTemplates;
|
||
strList.Add(new margin_template
|
||
{
|
||
Name = "系统默认",
|
||
MarginType = (int)MarginTypeEnum.DEFAULT,
|
||
InitialMarginRatio = 0,
|
||
PositionMarginRatio = 0
|
||
|
||
});
|
||
strList.Add(new margin_template
|
||
{
|
||
Name = "无预付金",
|
||
MarginType = (int)MarginTypeEnum.NONE,
|
||
InitialMarginRatio = 0,
|
||
PositionMarginRatio = 0
|
||
|
||
});
|
||
return strList;
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|