338 lines
12 KiB
C#
338 lines
12 KiB
C#
using BaseOUDAL;
|
|
using YLErp.BLL;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.DBModels.Helpers;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.ExchangeTradeModule
|
|
{
|
|
/// <summary>
|
|
/// 对冲交易同步API服务
|
|
/// </summary>
|
|
public class ExchangeTradeSyncApiService : ExchangeTradeSaveServiceBase
|
|
{
|
|
public ExchangeTradeSyncApiService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
|
|
}
|
|
|
|
public ExchangeTradeSyncApiService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
|
|
}
|
|
|
|
public int Save(ExchangeTradeSaveApiReq req, string tradeSource)
|
|
{
|
|
if (req.TradeDate == DateTime.MinValue)
|
|
{
|
|
throw new ServiceException("交易日期 必须填写");
|
|
}
|
|
|
|
if (!req.IsHistory && req.TradeDate < valuedateBLL.ValueDate)
|
|
{
|
|
throw new ServiceException("交易日期 不能小于系统日期:" + valuedateBLL.ValueDate.ToString("yyyy-MM-dd"));
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(req.UnderlyingCode))
|
|
{
|
|
throw new ServiceException("交易产品代码 必须填写");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(req.BuySell))
|
|
{
|
|
throw new ServiceException("交易方向 必须填写");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(req.ExchangeAccountCode))
|
|
{
|
|
throw new ServiceException("交易账户代码 必须填写");
|
|
}
|
|
|
|
switch (req.TradeType)
|
|
{
|
|
case "股票":
|
|
case "商品现货":
|
|
case "场内期权":
|
|
case "利率债":
|
|
case "信用债":
|
|
case "其它债券":
|
|
break;
|
|
case "商品期货":
|
|
if (req.UnderlyingCode.Length > 8)
|
|
{
|
|
req.TradeType = "场内期权";
|
|
}
|
|
break;
|
|
default:
|
|
throw new ServiceException("交易类型未能识别:" + req.TradeType);
|
|
}
|
|
|
|
//20220415:金仕达接口给的商品期货数据,如果是来源于场内期权行权产生的,则会和场内期权交易编号一致
|
|
ExchangeTrade dbTrade = null;
|
|
|
|
var canUpdate = false;
|
|
|
|
//两个交易日内交易编号相同的在数据库存在数据,就忽略不做处理;不存在,就插入一条交易日为上一日的交易。
|
|
if (req.TradeNumber.EndsWith("_dzrh_"))
|
|
{
|
|
var lastTradeDate = QdpCalendarHelper.GetNonHolidayDefore(req.TradeDate.AddDays(-1));
|
|
if (DbContext.ExchangeTrade.Any(x => (x.TradeDate == req.TradeDate || x.TradeDate == lastTradeDate) && x.TradeNumber == req.TradeNumber))
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
req.TradeDate = lastTradeDate;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dbTrade = DbContext.ExchangeTrade.FirstOrDefault(x => x.TradeDate == req.TradeDate && x.TradeType == req.TradeType && x.TradeNumber == req.TradeNumber);
|
|
canUpdate = dbTrade != null
|
|
//&& req.TradeType == dbTrade.TradeType
|
|
&& req.BuySell == dbTrade.TradeSide
|
|
&& req.ExchangeAccountCode.Equals(dbTrade.ExchangeAccountCode, StringComparison.OrdinalIgnoreCase)
|
|
&& req.UnderlyingCode.Equals(dbTrade.TradeType == "场内期权" ? dbTrade.OptionCode : dbTrade.UnderlyingCode, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
var reqTrade = PrepareReqTrad(req, tradeSource);
|
|
|
|
SaveExchangeTradePosition(reqTrade, canUpdate ? dbTrade : null);
|
|
|
|
if (canUpdate)
|
|
{
|
|
dbTrade.UnderlyingCode = reqTrade.UnderlyingCode;
|
|
dbTrade.TradeSinglePrice = reqTrade.TradeSinglePrice;
|
|
|
|
dbTrade.Notional = reqTrade.Notional;
|
|
dbTrade.TradeAmount = reqTrade.TradeAmount;
|
|
dbTrade.TradeLots = reqTrade.TradeLots;
|
|
|
|
dbTrade.TraderId = reqTrade.TraderId;
|
|
dbTrade.TraderName = reqTrade.TraderName;
|
|
|
|
dbTrade.UnderlyingId = reqTrade.UnderlyingId;
|
|
dbTrade.MaturityDate = reqTrade.MaturityDate;
|
|
|
|
dbTrade.OptionStrike = reqTrade.OptionStrike;
|
|
dbTrade.OptionType = reqTrade.OptionType;
|
|
dbTrade.ExerciseMode = reqTrade.ExerciseMode;
|
|
|
|
dbTrade.AssetBookId = reqTrade.AssetBookId;
|
|
dbTrade.ExchangeAccountId = reqTrade.ExchangeAccountId;
|
|
|
|
dbTrade.InstrumentType = reqTrade.InstrumentType;
|
|
|
|
dbTrade.OptDate = DateTime.Now;
|
|
}
|
|
else
|
|
{
|
|
DbContext.ExchangeTrade.Add(reqTrade);
|
|
}
|
|
|
|
return DbContext.SaveChanges();
|
|
}
|
|
|
|
//将req转换交易对象
|
|
private ExchangeTrade PrepareReqTrad(ExchangeTradeSaveApiReq req, string tradeSource = "下单同步")
|
|
{
|
|
var td = new ExchangeTrade
|
|
{
|
|
IsValid = true,
|
|
|
|
TradeType = req.TradeType,
|
|
TradeDate = req.TradeDate,
|
|
TradeNumber = req.TradeNumber,
|
|
TradeSide = req.BuySell,
|
|
TradeSinglePrice = req.TradePrice,
|
|
UnderlyingCode = req.UnderlyingCode,
|
|
ExchangeAccountCode = req.ExchangeAccountCode,
|
|
|
|
Commission = 0,
|
|
CommissionType = CommissionType.系统计算,
|
|
|
|
OptId = 0,
|
|
OptDate = DateTime.Now,
|
|
OptName = UserName,
|
|
CreateTime = DateTime.Now,
|
|
TradeSource = tradeSource,
|
|
|
|
Notional = 0,
|
|
TradeAmount = 0,
|
|
TradeLots = 0,
|
|
|
|
TraderId = 0,
|
|
TraderName = UserName,
|
|
|
|
UnderlyingId = 0,
|
|
MaturityDate = null,
|
|
|
|
OptionCode = null,
|
|
OptionStrike = null,
|
|
OptionType = null,
|
|
ExerciseMode = null,
|
|
|
|
AssetBookId = 0,
|
|
ExchangeAccountId = 0,
|
|
|
|
InstrumentType = null,
|
|
Comments = null,
|
|
};
|
|
|
|
var contractSize = 0d;
|
|
|
|
if (td.TradeType == "场内期权")
|
|
{
|
|
SetTradeOptionInfo(td, td.UnderlyingCode, out var option, false);
|
|
|
|
contractSize = option.ContractSize;
|
|
}
|
|
|
|
//标的信息
|
|
var um = DbContext.underlying_manager.Where(un => un.UnderlyingCode == td.UnderlyingCode)
|
|
.Select(n => new
|
|
{
|
|
n.id,
|
|
n.UnderlyingType,
|
|
n.MaturityDate,
|
|
n.UnderlyingTypeId,
|
|
n.UnderlyingInstrumentType,
|
|
n.ContractSize
|
|
}).FirstOrDefault();
|
|
|
|
if (um == null)
|
|
{
|
|
throw new ServiceException($"未找到标的信息:" + td.UnderlyingCode);
|
|
}
|
|
|
|
td.UnderlyingId = um.id;
|
|
td.InstrumentType = um.UnderlyingInstrumentType;
|
|
|
|
if (td.TradeType != "场内期权")
|
|
{
|
|
td.MaturityDate = um.MaturityDate;
|
|
}
|
|
|
|
//交易数量
|
|
if (td.TradeType == "股票")
|
|
{
|
|
td.TradeLots = req.Lots / 100;
|
|
td.Notional = td.TradeAmount = req.Lots;
|
|
}
|
|
else
|
|
{
|
|
if (contractSize < 1)
|
|
{
|
|
contractSize = um.ContractSize;
|
|
}
|
|
|
|
var variety = DbContext.variety.Where(n => n.id == um.UnderlyingTypeId).FirstOrDefault();
|
|
|
|
if (variety == null)
|
|
{
|
|
throw new ServiceException($"未找到{td.UnderlyingCode}的品种信息");
|
|
}
|
|
|
|
var CountRatio = VarietyHelper.GetCountRatio(variety.QuoteUnit);
|
|
|
|
if (contractSize > 1)
|
|
{
|
|
td.TradeLots = req.Lots;
|
|
td.Notional = req.Lots * contractSize;
|
|
td.TradeAmount = td.Notional / variety.CountRatio;
|
|
}
|
|
else
|
|
{
|
|
td.TradeLots = req.Lots;
|
|
td.Notional = req.Lots * (variety.TradeUnitValue ?? 1);
|
|
td.TradeAmount = td.Notional / variety.CountRatio;
|
|
}
|
|
}
|
|
|
|
//簿记等信息
|
|
PrepareBook(td, um.UnderlyingTypeId);
|
|
|
|
if (req.Commission.HasValue)
|
|
{
|
|
td.Commission = req.Commission.Value;
|
|
td.CommissionType = CommissionType.手动录入;
|
|
}
|
|
|
|
return td;
|
|
}
|
|
|
|
//为trade准备对冲账户/簿记/交易员信息
|
|
private void PrepareBook(ExchangeTrade td, int varietyId)
|
|
{
|
|
var query = from a in DbContext.exchange_account
|
|
join b in DbContext.assetunit on a.DefaultBookId equals b.id into bs
|
|
from b in bs.DefaultIfEmpty()
|
|
where a.AccountCode == td.ExchangeAccountCode && a.Status == 1
|
|
select new
|
|
{
|
|
AccountId = a.id,
|
|
a.AccountCode,
|
|
a.VarietyIds,
|
|
a.TraderIds,
|
|
BookId = b != null ? b.id : 0,
|
|
BookName = b != null ? b.Name : "",
|
|
a.Description
|
|
};
|
|
|
|
var accbooks = query.ToList();
|
|
|
|
if (accbooks.Count == 0)
|
|
{
|
|
throw new ServiceException($"对冲账户'{td.ExchangeAccountCode}'在系统中不存在");
|
|
}
|
|
|
|
var accbook = accbooks[0];
|
|
var commaVarietyId = $",{varietyId},";
|
|
|
|
if (accbooks.Count == 1)
|
|
{
|
|
if (varietyId > 0 && !(string.IsNullOrWhiteSpace(accbook.VarietyIds) || string.Concat(",", accbook.VarietyIds.Trim(), ",").Contains(commaVarietyId)))
|
|
{
|
|
throw new ServiceException($"对冲账户'{td.ExchangeAccountCode}'配置品种不包含此交易的品种");
|
|
}
|
|
}
|
|
else if (varietyId > 0)
|
|
{
|
|
accbook = accbooks.FirstOrDefault(n => string.Concat(",", n.VarietyIds.Trim(), ",").Contains(commaVarietyId))
|
|
?? accbooks.FirstOrDefault(n => string.IsNullOrWhiteSpace(n.VarietyIds));
|
|
|
|
if (accbook == null)
|
|
{
|
|
throw new ServiceException($"对冲账户'{td.ExchangeAccountCode}'配置品种不包含此交易的品种");
|
|
}
|
|
}
|
|
|
|
//对冲账户
|
|
td.ExchangeAccountId = accbook.AccountId;
|
|
td.ExchangeAccountCode = accbook.AccountCode;
|
|
|
|
//簿记帐户
|
|
if (accbook.BookId < 1)
|
|
{
|
|
throw new ServiceException($"对冲账户'{td.ExchangeAccountCode}'没有找到簿记账户");
|
|
}
|
|
|
|
td.AssetBookId = accbook.BookId;
|
|
|
|
//交易员信息
|
|
var traderId = td.TraderId;
|
|
var trader = traderId > 0 ? UserBLL.GetById(traderId) : null;
|
|
if (trader == null)
|
|
{
|
|
traderId = DataConvert.ConvertCommaValuesToInt32Array(accbook.TraderIds).FirstOrDefault();
|
|
if (traderId > 0)
|
|
{
|
|
trader = UserBLL.GetById(traderId);
|
|
}
|
|
}
|
|
td.TraderId = trader?.Id ?? UserId;
|
|
td.TraderName = (trader == null && !string.IsNullOrWhiteSpace(accbook?.Description)) ? accbook.Description.Trim() : trader?.Name ?? UserName ?? "系统";
|
|
}
|
|
}
|
|
}
|