- 将金石达EOD交易同步服务中的错误日志调整为信息日志 - 将配额监控服务中的错误日志调整为信息日志 - 将掉期消费者服务中的错误日志调整为信息日志 - 将交易确认书控制器中的错误日志调整为信息日志 - 将交易OA服务中的错误日志调整为信息日志 - 统一将验证失败的日志输出从Error级别改为Info级别 - 保持原有的异常抛出逻辑不变,仅调整日志记录级别
526 lines
17 KiB
C#
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; }
|
|
}
|
|
}
|