using System.Linq.Expressions;
using YLErp.DBModels.Enums;
using YLErp.DBModels.Helpers;
using YLErp.Modules.DataProviderModule;
namespace YLErp.Modules.ExchangeTradeModule
{
///
/// 场内交易保存基类
///
public class ExchangeTradeSaveServiceBase : YLBaseService
{
public ExchangeTradeSaveServiceBase(YLBaseService baseService) : base(baseService)
{
}
public ExchangeTradeSaveServiceBase(OptUserInfo userInfo) : base(userInfo)
{
}
///
/// 保存对冲交易持仓(dbTradOld为null说明reqTrad是新增交易)
///
public void SaveExchangeTradePosition(ExchangeTrade reqTrad, ExchangeTrade dbTradOld = null)
{
TradePosition dbPosition;
TradePositionParam para;
//比较请求的数据和数据库中的旧数据的基本要素是否相同
var isSame = dbTradOld == null || reqTrad.AssetBookId == dbTradOld.AssetBookId
&& reqTrad.TradeSide == dbTradOld.TradeSide && reqTrad.TradeType == dbTradOld.TradeType
&& string.Equals(reqTrad.UnderlyingCode, dbTradOld.UnderlyingCode, StringComparison.OrdinalIgnoreCase)
&& (reqTrad.TradeType != "场内期权" || string.Equals(reqTrad.OptionCode, dbTradOld.OptionCode, StringComparison.OrdinalIgnoreCase));
//如果不相同就从旧持仓份额中减去
if (!isSame)
{
//准备旧数据参数查询持仓
para = PrepareTradePositionParam(dbTradOld);
dbPosition = DbContext.TradePosition.FirstOrDefault(para.predicate);
if (dbPosition != null)
{
var dbNotional = TradeHelper.GetPositionNotional(para.tradeSide, dbTradOld.Notional);
dbPosition.Position -= dbNotional;
dbPosition.PositionCost -= dbNotional * dbTradOld.TradeSinglePrice;
dbPosition.UpdateTime = OptDate;
}
}
//准备新的参数进行查询持仓
para = PrepareTradePositionParam(reqTrad);
var reqNotional = TradeHelper.GetPositionNotional(para.tradeSide, reqTrad.Notional);
dbPosition = DbContext.TradePosition.FirstOrDefault(para.predicate);
//如果数据库中不存在对应的持仓就使用请求数据创建持仓数据
if (dbPosition == null)
{
DateTime maturityDate;
if (reqTrad.TradeType == "股票")
{
maturityDate = DateTime.Today.AddYears(10);
}
else if (reqTrad.TradeType == "场内期权")
{
if (!ExchangeOptionDataProvider.TryGetMaturityDate(reqTrad.OptionCode, out maturityDate) &&
!UnderlyingDataProvider.TryGetMaturityDate(reqTrad.UnderlyingCode, out maturityDate))
{
maturityDate = DateTime.Today.AddMonths(1);
}
}
else if (!UnderlyingDataProvider.TryGetMaturityDate(reqTrad.UnderlyingCode, out maturityDate))
{
maturityDate = DateTime.Today.AddMonths(1);
}
dbPosition = new TradePosition
{
TradeType = reqTrad.TradeType,
BookId = reqTrad.AssetBookId,
InstrumentCode = reqTrad.OptionCode,
PositionType = para.positionType,
Position = reqNotional,
TradeType1 = para.tradeType1,
UnderlyingId = reqTrad.UnderlyingId,
UnderlyingCode = reqTrad.UnderlyingCode,
CreateTime = OptDate,
MaturityDate = maturityDate
};
dbPosition.PositionCost = dbPosition.Position * reqTrad.TradeSinglePrice;
DbContext.TradePosition.Add(dbPosition);
}
//如果数据库中不存在旧交易数据在现有持仓数据上增加
else if (dbTradOld == null)
{
dbPosition.Position += reqNotional;
dbPosition.PositionCost += reqNotional * reqTrad.TradeSinglePrice;
}
else
{
//如果基本要素相同就删除旧的持仓份额改用新的持仓份额
if (isSame)
{
var dbNotional = TradeHelper.GetPositionNotional(dbTradOld.TradeSide, dbTradOld.Notional);
dbPosition.Position -= dbNotional;
dbPosition.PositionCost -= dbNotional * dbTradOld.TradeSinglePrice;
}
dbPosition.Position += reqNotional;
dbPosition.PositionCost += reqNotional * reqTrad.TradeSinglePrice;
}
if (dbPosition != null)
{
dbPosition.UpdateTime = OptDate;
}
}
///
/// 根据trde对象生成的持仓参数
///
protected static TradePositionParam PrepareTradePositionParam(ExchangeTrade td)
{
var para = new TradePositionParam
{
tradeSide = TradeHelper.GetTradeSide(td.TradeSide),
tradeType1 = TradeHelper.GetTradeType1(td.TradeType)
};
para.positionType = TradeHelper.GetPositionType(para.tradeSide);
var predicate = PredicateBuilder.Create(n => n.BookId == td.AssetBookId
&& n.TradeType1 == para.tradeType1 && n.UnderlyingCode == td.UnderlyingCode);
if (td.TradeType == "场内期权")
{
predicate = predicate.And(n => n.InstrumentCode == td.OptionCode);
}
para.predicate = predicate;
return para;
}
///
/// 检查交易的交易类型
///
protected string CheckTradeType(string tradeType)
{
if (string.IsNullOrWhiteSpace(tradeType))
{
throw new ServiceException("交易类型 必须填写");
}
switch (tradeType)
{
case "股票":
case "商品现货":
case "场内期权":
case "商品期货":
case "信用债":
case "利率债":
case "其它债券":
return tradeType;
case "期货": return "商品期货";
default: throw new ServiceException("交易类型未能识别:" + tradeType);
}
}
///
/// 检查交易的交易方向
///
protected void CheckTradeSide(string tradeSide)
{
if (string.IsNullOrWhiteSpace(tradeSide))
{
throw new ServiceException("交易方向 必须填写");
}
switch (tradeSide)
{
case ConsGlobal.TradeSide.多头开仓:
case ConsGlobal.TradeSide.多头平仓:
case ConsGlobal.TradeSide.空头开仓:
case ConsGlobal.TradeSide.空头平仓: break;
default: throw new ServiceException("交易方向未能识别:" + tradeSide);
}
}
///
/// 设置交易的场内期权信息
///
protected void SetTradeOptionInfo(ExchangeTrade td, string optionCode, out ExchangeListOption option, bool useCachedDataSource = true)
{
if (string.IsNullOrWhiteSpace(optionCode))
{
throw new ServiceException("场内期权交易必须有对应的期权合约存在");
}
if (useCachedDataSource)
{
option = DataCacheProvider.GetExchangeListOptionDataSource().GetData(optionCode);
}
else
{
option = DbContext.exchange_list_option.AsNoTracking().FirstOrDefault(e => e.ContractCode == optionCode);
}
if (option == null)
{
throw new ServiceException("场内期权合约信息不存在:" + optionCode);
}
if (option.MaturityDate == DateTime.MinValue)
{
throw new ServiceException("场内期权合约到期日不存在:" + optionCode);
}
if (option.MaturityDate < td.TradeDate)
{
throw new ServiceException($"交易日期'{td.TradeDate:yyyy-MM-dd}'不能大于场内期权合约到期日'{option.MaturityDate:yyyy-MM-dd}'");
}
td.OptionCode = option.ContractCode;
td.UnderlyingCode = option.UnderlyingCode;
td.OptionStrike = option.Strike;
td.OptionType = option.OptionType;
td.MaturityDate = option.MaturityDate;
td.ExerciseMode = option.ExerciseMode.TrimToNull() ?? "European";
if (optionCode.StartsWith("IO", StringComparison.OrdinalIgnoreCase))
{
td.UnderlyingCode = "000300.SH";
}
}
///
/// 设置交易的标的信息
///
protected void SetTradeUnderlyingInfo(ExchangeTrade td, string underlyingCode, out underlying_manager um, bool useCachedDataSource = true)
{
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(td.TradeType));
if (string.IsNullOrWhiteSpace(underlyingCode))
{
throw new ServiceException("标的代码 必须填写");
}
if (useCachedDataSource)
{
um = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
}
else
{
um = DbContext.underlying_manager.AsNoTracking().FirstOrDefault(un => un.UnderlyingCode == underlyingCode);
}
if (um == null)
{
throw new ServiceException("标的信息不存在:" + underlyingCode);
}
if (td.TradeType != "场内期权")
{
if (um.IsFutures())
{
if (um.MaturityDate == null)
{
throw new ServiceException("商品期货合约到期日不存在:" + underlyingCode);
}
if (um.MaturityDate.Value < td.TradeDate)
{
throw new ServiceException($"交易日期'{td.TradeDate:yyyy-MM-dd}'不能大于商品期货标的到期日'{um.MaturityDate:yyyy-MM-dd}'");
}
}
td.MaturityDate = um.MaturityDate;
}
td.UnderlyingId = um.id;
td.UnderlyingCode = um.UnderlyingCode;
td.InstrumentType = um.UnderlyingInstrumentType;
}
///
/// 设置交易的数量信息
///
protected void SetTradeLotsInfo(ExchangeTrade td, double lots, double contractSize, int varietyId, bool useCachedDataSource = true)
{
System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(td.TradeType));
td.TradeLots = lots;
if (td.TradeType == "股票")
{
td.Notional = td.TradeAmount = lots * (contractSize < 1 ? 100 : contractSize);
}
else
{
Variety variety = null;
if (useCachedDataSource)
{
variety = DataCacheProvider.GetVarietyDataSource().GetData(varietyId);
}
else
{
variety = DbContext.variety.AsNoTracking().FirstOrDefault(n => n.id == varietyId);
}
if (variety == null)
{
throw new ServiceException("标的的品种信息不存在:" + td.UnderlyingCode);
}
if (contractSize < 1)
{
contractSize = VarietyHelper.GetTradeUnitValue(variety.VarietyCode, variety.TradeUnit) ?? 1;
}
var countRatio = VarietyHelper.GetCountRatio(variety.QuoteUnit);
td.Notional = lots * contractSize;
td.TradeAmount = td.Notional / countRatio;
}
}
///
/// 设置交易员信息
///
protected void SetTraderInfo(ExchangeTrade td, string traderName)
{
td.TraderId = UserId;
td.TraderName = UserName;
if (!string.IsNullOrWhiteSpace(traderName))
{
using (var sysdb = DbContextFactory.GetErpBaseContext())
{
var systemUsers = sysdb.SystemUsers.Where(su => su.LoginName == traderName || su.Name == traderName)
.Select(n => new { n.Id, n.Name }).ToArray();
if (!systemUsers.Any())
{
throw new ServiceException("交易员不存在:" + traderName);
}
else
{
var systemUser = sysdb.SystemUsers.FirstOrDefault(su => su.LoginName == traderName)
?? sysdb.SystemUsers.FirstOrDefault(su => su.Name == traderName);
td.TraderId = systemUser.Id;
td.TraderName = systemUser.Name;
}
}
}
}
///
/// 设置簿记账户和对冲账户
///
protected void SetAssetAndExchangeAccount(ExchangeTrade td, string assetBookName, string exchangeAccountCode, int varietyId)
{
//簿记账户
if (!string.IsNullOrWhiteSpace(assetBookName))
{
var book = DataCacheProvider.GetAssetUnitDataSource().AsQueryable().FirstOrDefault(n => n.Name == assetBookName);
if (book == null)
{
throw new ServiceException($"簿记账户'{assetBookName}'没有找到");
}
td.AssetBookId = book.id;
}
//对冲账户
if (!string.IsNullOrWhiteSpace(exchangeAccountCode))
{
var exAccounts = DataCacheProvider.GetExchangeAccountDataSource().AsQueryable()
.Where(a => a.AccountCode == exchangeAccountCode && a.Status == 1).ToArray();
if (!exAccounts.Any())
{
throw new ServiceException($"对冲账户'{exchangeAccountCode}'在系统中不存在");
}
var exAccount = exAccounts[0];
if (varietyId > 0)
{
var commaVarietyId = $",{varietyId},";
exAccount = exAccounts.FirstOrDefault(n => string.IsNullOrWhiteSpace(n.VarietyIds)
|| string.Concat(",", n.VarietyIds, ",").Contains(commaVarietyId)) ?? exAccounts[0];
}
//对冲账户
td.ExchangeAccountId = exAccount.id;
//使用对冲账户绑定的簿记账户
if (string.IsNullOrWhiteSpace(assetBookName))
{
if ((exAccount.DefaultBookId ?? 0) < 1 ||
DataCacheProvider.GetAssetUnitDataSource().GetData(exAccount.DefaultBookId.Value) == null)
{
throw new ServiceException($"对冲账户'{exchangeAccountCode}'没有设置簿记账户");
}
td.AssetBookId = exAccount.DefaultBookId.Value;
}
}
}
///
/// 根据trde对象生成的持仓参数
///
protected class TradePositionParam
{
public TradeSideFlag tradeSide;
public TradeTypeFlag tradeType1;
public PositionTypeFlag positionType;
public Expression> predicate;
}
}
}