Files
zszq-trs/YLErpDAL/Modules/IntegrationModule/XiaMenXiangYu/JinshidaEodTradeSyncService.cs
T
hjhan 36419d8211 style(log): 将错误日志级别Error调整为Info日志级别
- 将金石达EOD交易同步服务中的错误日志调整为信息日志
- 将配额监控服务中的错误日志调整为信息日志
- 将掉期消费者服务中的错误日志调整为信息日志
- 将交易确认书控制器中的错误日志调整为信息日志
- 将交易OA服务中的错误日志调整为信息日志
- 统一将验证失败的日志输出从Error级别改为Info级别
- 保持原有的异常抛出逻辑不变,仅调整日志记录级别
2026-03-25 10:24:48 +08:00

526 lines
17 KiB
C#

using CsvHelper;
using Microsoft.Net.Http.Headers;
using System.Globalization;
using YLErp.Modules.ExchangeTradeModule;
namespace YLErp.Modules.IntegrationModule.XiaMenXiangYu
{
/// <summary>
/// 从金仕达日终交易接口同步场内交易
/// </summary>
public class JinshidaEodTradeSyncService : YLBaseService
{
readonly DateTime _now;
readonly IYcLogger _logger;
readonly IHttpClientFactory _httpClientFactory;
public JinshidaEodTradeSyncService(OptUserInfo userInfo, IHttpClientFactory httpClientFactory) : base(userInfo)
{
_now = DateTime.Now;
_logger = LogFactory.GetLogger("金仕达场内交易同步");
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
}
public Task Execute(string url)
{
//15点之前和21点以后不再请求,每隔30分钟执行一次
if (_now.Hour < 15 || _now.Hour > 21 || YLErp.BLL.valuedateBLL.ValueDate > _now || YLErp.QdpModule.QdpCalendarHelper.IsHoliday(DateTime.Today))
{
return Task.CompletedTask;
}
_logger.Info("开始执行");
if (string.IsNullOrEmpty(url))
{
_logger.Info("url 配置为空,不能进行同步");
return Task.CompletedTask;
}
var uri = new Uri(url);
var nv = YieldChain.Helpers.UrlHelper.ParseQueryString(uri.Query?.TrimStart('?')) ?? new System.Collections.Specialized.NameValueCollection();
nv["date"] = _now.ToString("yyyyMMdd");
if (string.IsNullOrWhiteSpace(nv["page_size"]))
{
nv["page_size"] = int.MaxValue.ToString();
}
url = uri.GetLeftPart(UriPartial.Path) + "?" + YieldChain.Helpers.UrlHelper.ToQueryString(nv);
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url)
{
Headers =
{
{ HeaderNames.Accept, "application/json" },
{ HeaderNames.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36" }
}
};
_logger.Info("发送请求:" + url);
return InnerExecute(httpRequestMessage).ContinueWith(t =>
{
if (t.Exception != null)
{
_logger.Error(t.Exception, "请求出错");
}
});
}
private async Task InnerExecute(HttpRequestMessage httpRequestMessage)
{
var httpClient = _httpClientFactory.CreateClient();
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
var content = await httpResponseMessage.Content.ReadAsStringAsync();
if (httpResponseMessage.IsSuccessStatusCode)
{
var result = JsonHelper.Deserialize<JinshidaEodTradeResponse>(content);
if (result.rows != null)
{
foreach (var item in result.rows)
{
try
{
item.SaveResult = SaveExchangeTrade(item);
}
catch (Exception ex)
{
item.SaveResult = "失败:" + ex.Message;
_logger.Error(ex, $"保存交易(成交流水号:{item.DONENO_})失败");
}
}
}
else
{
_logger.Info("请求返回结果:" + content);
}
try
{
var fileName = _now.ToString("yyyy_MM_dd_HH_mm");
var jsdPath = Path.Combine(AppContext.BaseDirectory, "log\\jsd");
Directory.CreateDirectory(jsdPath);
var csvPath = Path.Combine(jsdPath, fileName + ".csv");
using (var writer = new StreamWriter(csvPath, false, System.Text.Encoding.UTF8))
using (var csv = new CsvWriter(writer, System.Globalization.CultureInfo.InvariantCulture))
{
csv.WriteRecords(result.rows ?? Enumerable.Empty<JinshidaEodTradeFields>());
}
//留存取到的json文本
var jsonPath = Path.Combine(jsdPath, fileName + ".json");
File.WriteAllText(jsonPath, content);
}
catch (Exception ex)
{
_logger.Error(ex, "保存csv文件失败");
}
}
else
{
_logger.Error($"请求失败,status code:{(int)httpResponseMessage.StatusCode} {httpResponseMessage.StatusCode};{content}");
}
}
public string SaveExchangeTrade(JinshidaEodTradeFields f)
{
string tradeType;
if (f.DONENO_ == "0")
{
return "不支持此成交编号(DONENO)";
}
switch (f.YWLX_)
{
case "1":
tradeType = "商品期货"; break;
case "6015": //商品期权买卖
case "6016": //商品期权行权
case "6022": //期权到期平仓
tradeType = "场内期权"; break;
default:
return "不支持此种业务类型(YWLX)";
}
string tradeSide;
switch (f.BSTP_)
{
case "0":
tradeSide = "买入"; break;
case "1":
tradeSide = "卖出"; break;
default:
return "不支持此种买卖类型(BSTP)";
}
switch (f.KPTP_)
{
case "0": //开仓
tradeSide = tradeSide == "买入" ? "多头开仓" : "空头开仓";
break;
case "1": //平仓
case "2": //平今
tradeSide = tradeSide == "买入" ? "空头平仓" : "多头平仓";
break;
default:
return "不支持此种开平标记(KPTP)";
}
var exchangeId = ConvertMarket(f.MKCODE_, out var isStock);
if (string.IsNullOrEmpty(exchangeId))
{
return "不支持此市场代码(MKCODE)";
}
if (!double.TryParse(f.FSJG_, out var singlePrice))
{
return "成交价(FSJG)解析失败";
}
if (!double.TryParse(f.FSSL_, out var lots) || lots < 1e-6)
{
return "成交量(FSSL)解析失败";
}
if (!DateTime.TryParseExact(f.DATE_, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var tradeDate))
{
return "成交日期(DATE)解析失败:" + f.DATE_;
}
double.TryParse(f.FEE_, out var commission);
var underlyingCode = f.STKCODE_; //暂时只有商品期货标的,所以可不处理股票标的后缀
var tradeNo = f.DONENO_ + f.BSTP_; //支持自成交
var req = new ExchangeTradeSaveApiReq
{
TradeNumber = tradeNo,
UnderlyingCode = underlyingCode,
TradeDate = tradeDate,
BuySell = tradeSide,
TradePrice = singlePrice,
Lots = lots,
TradeType = tradeType,
ExchangeAccountCode = string.Concat(f.CELLID_, "#", f.PROFLID_),
Commission = commission,
IsHistory = true
};
//金仕达trade api接口有些交易编号带日期前缀,但webapi又不带
//导致同一笔交易写入两笔数据到系统中,以下为解决这个问题
using (var db = DbContextFactory.GetYLDbContext())
{
if (f.YWLX_ == "6016")
{
var option = db.exchange_list_option.Where(e => e.ContractCode == f.STKCODE_)
.Select(n => new
{
n.Strike,
n.OptionType,
n.ContractSize
}).FirstOrDefault();
if (option == null)
{
return "没有找到场内期权合约信息";
}
var pnl = singlePrice - option.Strike;
if (!ConsGlobal.CallPut.IsCall(option.OptionType))
{
pnl = -pnl;
}
if (pnl < 1e-6)
{
pnl = 0;
singlePrice = 0;
}
else
{
var volume = lots * (option.ContractSize < 1 ? 1 : option.ContractSize);
singlePrice = pnl / volume;
}
req.TradePrice = singlePrice;
}
var reqList = new List<string> {
req.TradeNumber,
f.DATE_ + req.TradeNumber
};
if (req.TradeNumber.StartsWith("0"))
{
reqList.Add(req.TradeNumber.TrimStart('0'));
}
//20220415:金仕达接口给的商品期货数据,如果是来源于场内期权行权产生的,则会和场内期权交易编号一致
var query = from x in db.ExchangeTrade
where x.TradeDate == req.TradeDate
&& x.TradeType == req.TradeType
&& reqList.Contains(x.TradeNumber)
select x.TradeNumber;
var resultList = query.ToArray();
if (resultList.Any() && !resultList.Contains(req.TradeNumber, StringComparer.OrdinalIgnoreCase))
{
if (resultList.Contains(reqList[1], StringComparer.OrdinalIgnoreCase))
{
req.TradeNumber = reqList[1];
}
else if (reqList.Count > 2 && resultList.Contains(reqList[2], StringComparer.OrdinalIgnoreCase))
{
req.TradeNumber = reqList[2];
}
}
}
new ExchangeTradeSyncApiService(new OptUserInfo(0, "金仕达", OptUserFrom.System)).Save(req,"下单同步");
return "成功";
}
//转换交易所编号
private static string ConvertMarket(string market_code, out bool isStockMarket)
{
isStockMarket = false;
switch (market_code)
{
case "1"://上交所
isStockMarket = true;
return "SH";
case "2"://深交所
isStockMarket = true;
return "SZ";
case "3"://中金所
return "CFFEX";
case "4"://上期所
return "SHFE";
case "5": //郑商所
return "CZCE";
case "6"://大商所
return "DCE";
case "7": //上海黄金交易所
return "SGE";
case "8"://能源期货交易所
return "INE";
default: return string.Empty;
}
}
}
/// <summary>
/// 金仕达接口数据回应
/// </summary>
public class JinshidaEodTradeResponse
{
public int pageNo { get; set; }
public int pageSize { get; set; }
public int totalCount { get; set; }
public IEnumerable<JinshidaEodTradeFields> rows { get; set; }
}
/// <summary>
///
/// </summary>
public class JinshidaEodTradeFields
{
/// <summary>
/// 日期
/// </summary>
public string DATE_ { get; set; }
/// <summary>
/// 流水号
/// </summary>
public string SERIALID_ { get; set; }
/// <summary>
/// 资产账户编号
/// </summary>
public string FUNDID_ { get; set; }
/// <summary>
/// 资产单元编号
/// </summary>
public string CELLID_ { get; set; }
/// <summary>
/// 投资组合代号
/// </summary>
public string PROFLID_ { get; set; }
/// <summary>
/// 席位号
/// </summary>
public string SEATID_ { get; set; }
/// <summary>
/// 交易编码
/// </summary>
public string HOLDERACC_ { get; set; }
/// <summary>
/// 市场代码
/// </summary>
public string MKCODE_ { get; set; }
/// <summary>
/// 品种合约
/// </summary>
public string STKCODE_ { get; set; }
/// <summary>
/// 品种名称
/// </summary>
public string STKNAME_ { get; set; }
/// <summary>
/// 证券类型 [601-焦煤商品合约,626-沥青商品合约,631-动力煤商品合约,636-铁矿石商品合约,643-鸡蛋商品合约,309-新基金认购,652-动力煤商品合约,700-个股期权,701-豆粕期权,702-白糖期权,703-铜期权,704-沪深300股指期权,430-质押码,639-聚丙烯商品合约,640-胶合板商品合约]
/// </summary>
public string STKTP_ { get; set; }
/// <summary>
/// 投机套保标志 [投保标记-!,投机-0 ,保值(备兑)-1,套利-2]
/// </summary>
public string HEDGINGFLAG_ { get; set; }
/// <summary>
/// 业务类型[1-期货买卖,4201-基金收益,4021-红股登记,4175-净值归一拆分子基金,4176-净值归一合并母基金,4177-净值归一合并子基金,2-资金划入,3-资金划出,4-证券划入,5-证券划出,6- 手动资金冻结,4002-证券买入,2054-支票调出,2052-现金调出,4 036-ETF申购]
/// </summary>
public string YWLX_ { get; set; }
/// <summary>
/// 开平类型[!-开平标记,0-开仓,1-平仓,2-平今,3-空]
/// </summary>
public string KPTP_ { get; set; }
/// <summary>
/// 买卖类型[!-买卖标记,0-买,1-卖,6-转托管]
/// </summary>
public string BSTP_ { get; set; }
/// <summary>
/// 成交量
/// </summary>
public string FSSL_ { get; set; }
/// <summary>
/// 成交价
/// </summary>
public string FSJG_ { get; set; }
/// <summary>
/// 成交金额
/// </summary>
public string FSJE_ { get; set; }
/// <summary>
/// 成交时间
/// </summary>
public string CJTIME_ { get; set; }
/// <summary>
/// 手续费
/// </summary>
public string FEE_ { get; set; }
/// <summary>
/// 手续费1
/// </summary>
public string FEE1_ { get; set; }
/// <summary>
/// 手续费2
/// </summary>
public string FEE2_ { get; set; }
/// <summary>
/// 成交流水号
/// </summary>
public string DONENO_ { get; set; }
///// <summary>
///// 平仓盈亏(逐日盯市)
///// </summary>
//public string SDPROFIT_ { get; set; }
/// <summary>
/// 平仓盈亏(逐笔对冲)
/// </summary>
public string SSPROFIT_ { get; set; }
///// <summary>
///// 交割配对盈亏
///// </summary>
//public string JGPDYK_ { get; set; }
///// <summary>
///// 开仓日期
///// </summary>
//public string KC_DATE_ { get; set; }
///// <summary>
///// 开仓流水号
///// </summary>
//public string KC_SERIALID_ { get; set; }
///// <summary>
///// 开仓成交流水号
///// </summary>
//public string KC_DONENO_ { get; set; }
///// <summary>
///// 开仓价
///// </summary>
//public string KC_PRICE_ { get; set; }
///// <summary>
///// 昨日结算
///// </summary>
//public string ZRJS_ { get; set; }
///// <summary>
///// 今日结算
///// </summary>
//public string JRJS_ { get; set; }
///// <summary>
///// 组合证券代码
///// </summary>
//public string VARICODE_ { get; set; }
///// <summary>
///// 权利金
///// </summary>
//public string QLJ_ { get; set; }
///// <summary>
///// 开仓权利金
///// </summary>
//public string KCQLJ_ { get; set; }
public string SaveResult { get; set; }
}
}