FundQ.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10. using System.Text.Json;
  11. using System.Text.Json.Serialization;
  12. using System.Drawing.Text;
  13. using System.IO;
  14. using static ddq.Utility;
  15. using static ddq.DataAccess;
  16. using System.Diagnostics.Eventing.Reader;
  17. using System.Diagnostics.Metrics;
  18. using Org.BouncyCastle.Bcpg.OpenPgp;
  19. namespace ddq
  20. {
  21. public partial class FundQ : Form
  22. {
  23. private string fundId;
  24. private string companyId;
  25. private int userId;
  26. private DataTable fundInfoTable;
  27. public FundQ(string fundId, string companyId, int userId)
  28. {
  29. InitializeComponent();
  30. this.fundId = fundId;
  31. this.companyId = companyId;
  32. this.userId = userId;
  33. InitializeData();
  34. }
  35. private void InitializeData()
  36. {
  37. DataTable dt = DataAccess.Get_fund_info(fundId, null);
  38. if (dt == null || dt.Rows.Count <= 0) return;
  39. this.lblFundName.Text = dt.Rows[0].Field<string>("fund_short_name");
  40. //this.lblMainCode.Text = "Code: " + dt.Rows[0].Field<string>("register_number");
  41. this.lblMainCode.Text = "Code: " + fundId;
  42. this.lblCategory.Text = "Category: " + dt.Rows[0].Field<string>("strategy");
  43. this.lblInceptionDate.Text = "Launched: " + dt.Rows[0].Field<DateTime?>("inception_date")?.ToString("yyyy-MM-dd");
  44. this.lblDomicile.Text = "Domicile: ";
  45. fundInfoTable = DataAccess.Get_dd_fund_info(fundId, null, 1);
  46. if (fundInfoTable != null && fundInfoTable.Rows.Count > 0)
  47. {
  48. string jsonString = fundInfoTable.Rows[0].Field<string>("info").Trim();
  49. JsonDocument document = JsonDocument.Parse(jsonString);
  50. JsonElement root = document.RootElement;
  51. if (root.ValueKind != JsonValueKind.Undefined)
  52. {
  53. //
  54. // General Info
  55. //
  56. LoadTextDataFromJson(this.txtInvestmentObjective, root, "investmentObjective", this.tspInvestmentObjective);
  57. LoadTextDataFromJson(this.txtBenchmark, root, "benchmark", this.tspBenchmark);
  58. LoadTextDataFromJson(this.txtInvestmentPhilosophy, root, "investmentPhilosophy", this.tspInvestmentPhilosophy);
  59. LoadCurrentFees(root);
  60. LoadTextDataFromJson(this.txtPolicyOfClosingFund, root, "policyOfClosingFund", this.tspPolicyOfClosingFund);
  61. LoadPortfolioManager(root);
  62. LoadTER(root);
  63. //
  64. // Process
  65. //
  66. LoadDerivates(root);
  67. LoadPriceTarget(root);
  68. }
  69. }
  70. }
  71. private void LoadPortfolioManager(JsonElement jsonElement)
  72. {
  73. bool hasData = jsonElement.TryGetProperty("portfolioManagers", out JsonElement element);
  74. string strUpdateTime = "";
  75. DataTable dt = new DataTable();
  76. if (hasData == true && element.ValueKind == JsonValueKind.Object)
  77. {
  78. hasData &= element.TryGetProperty("v", out JsonElement elm);
  79. Utility.DDData dddata = Utility.Json2Table2(element);
  80. if (dddata != null)
  81. {
  82. dt = (DataTable)dddata.Value;
  83. strUpdateTime = dddata.UpdateTime;
  84. }
  85. }
  86. else
  87. {
  88. // 没有基金经理数据时,初始化表
  89. dt.Columns.Add("personnel_id", typeof(string));
  90. dt.Columns.Add("name", typeof(string));
  91. dt.Columns.Add("startDate", typeof(DateTime));
  92. dt.Columns.Add("endDate", typeof(DateTime));
  93. }
  94. InitPortfolioManagerGrid(dt, strUpdateTime);
  95. }
  96. private void InitPortfolioManagerGrid(DataTable dt, string updateTime)
  97. {
  98. if (dt == null && updateTime == null) return;
  99. this.grdPortfolioManager.DataSource = dt;
  100. this.grdPortfolioManager.Columns["personnel_id"].Visible = false;
  101. this.grdPortfolioManager.Columns["name"].HeaderText = "Manager Name";
  102. this.grdPortfolioManager.Columns["name"].DisplayIndex = 0;
  103. this.grdPortfolioManager.Columns["startDate"].HeaderText = "Start Date";
  104. this.grdPortfolioManager.Columns["startDate"].DefaultCellStyle.Format = "yyyy-MM-dd";
  105. this.grdPortfolioManager.Columns["startDate"].DisplayIndex = 1;
  106. this.grdPortfolioManager.Columns["endDate"].HeaderText = "End Date";
  107. this.grdPortfolioManager.Columns["endDate"].DefaultCellStyle.Format = "yyyy-MM-dd";
  108. this.grdPortfolioManager.Columns["endDate"].DisplayIndex = 2;
  109. this.tspPortfolioManager.Text = updateTime;
  110. this.tspPortfolioManager.ForeColor = IsChangedRecently(this.tspPortfolioManager.Text) ? COLOR_MODIFIED : COLOR_NORMAL;
  111. }
  112. private void LoadCurrentFees(JsonElement jsonElement)
  113. {
  114. if (jsonElement.ValueKind != JsonValueKind.Object) return;
  115. bool hasData = jsonElement.TryGetProperty("currentFees", out JsonElement element);
  116. if (hasData)
  117. {
  118. if (element.ValueKind == JsonValueKind.Object)
  119. {
  120. hasData = element.TryGetProperty("v", out JsonElement elm);
  121. if (hasData)
  122. {
  123. this.txtManagementFee.Text = Json2Text(elm, "managementFee");
  124. this.txtSubscriptionFee.Text = Json2Text(elm, "subscriptionFee");
  125. this.txtRedemptionFee.Text = Json2Text(elm, "redemptionFee");
  126. this.txtAdministrationFee.Text = Json2Text(elm, "administrationFee");
  127. this.txtSwitchingFee.Text = Json2Text(elm, "switchingFee");
  128. this.txtTrusteeFee.Text = Json2Text(elm, "trusteeFee");
  129. this.txtPerformanceFee.Text = Json2Text(elm, "performanceFee");
  130. this.tspCurrentFee.Text = element.GetProperty("t").ToString();
  131. this.tspCurrentFee.ForeColor = IsChangedRecently(this.tspCurrentFee.Text) ? COLOR_MODIFIED : COLOR_NORMAL;
  132. }
  133. }
  134. }
  135. }
  136. private void LoadTER(JsonElement jsonElement)
  137. {
  138. bool hasData = jsonElement.TryGetProperty("totalExpenseRatio", out JsonElement element);
  139. string strUpdateTime = "";
  140. DataTable dt = new DataTable();
  141. if (hasData == true && element.ValueKind == JsonValueKind.Object)
  142. {
  143. hasData &= element.TryGetProperty("v", out JsonElement elm);
  144. Utility.DDData dddata = Utility.Json2Table2(element);
  145. if (dddata != null)
  146. {
  147. dt = (DataTable)dddata.Value;
  148. strUpdateTime = dddata.UpdateTime;
  149. }
  150. }
  151. else
  152. {
  153. dt.Columns.Add("year", typeof(string));
  154. dt.Columns.Add("ter", typeof(string));
  155. int year = DateTime.Today.Year - 1;
  156. for (int i = 0; i < 5; i++)
  157. {
  158. DataRow row = dt.NewRow();
  159. row["year"] = year - i;
  160. dt.Rows.Add(row);
  161. }
  162. }
  163. InitTERGrid(dt, strUpdateTime);
  164. }
  165. private void InitTERGrid(DataTable dt, string updateTime)
  166. {
  167. if (dt == null || updateTime == null) return;
  168. this.grdTER.DataSource = dt;
  169. this.grdTER.Columns["year"].HeaderText = "Year";
  170. this.grdTER.Columns["year"].ReadOnly = true;
  171. this.grdTER.Columns["year"].DisplayIndex = 0;
  172. this.grdTER.Columns["ter"].HeaderText = "Total Expense Ratio %";
  173. this.grdTER.Columns["ter"].ValueType = typeof(decimal);
  174. this.grdTER.Columns["ter"].DefaultCellStyle.Format = "N3";
  175. this.grdTER.Columns["ter"].DisplayIndex = 1;
  176. this.tspTER.Text = updateTime;
  177. this.tspTER.ForeColor = Utility.IsChangedRecently(updateTime) ? COLOR_MODIFIED : COLOR_NORMAL;
  178. }
  179. private void LoadDerivates(JsonElement jsonElement)
  180. {
  181. if (jsonElement.ValueKind != JsonValueKind.Object) return;
  182. bool hasData = jsonElement.TryGetProperty("derivatives", out JsonElement element);
  183. if (hasData)
  184. {
  185. if (element.ValueKind == JsonValueKind.Object)
  186. {
  187. hasData = element.TryGetProperty("v", out JsonElement elm);
  188. if (hasData)
  189. {
  190. this.chkAllowDerivatives.Checked = Json2Boolean(elm, "allowed");
  191. this.chkUseDerivatives.Checked = Json2Boolean(elm, "currentUse");
  192. this.chkDerivativesForEfficent.Checked = Json2Boolean(elm, "effectiveManagement");
  193. this.chkDerivatesForHedging.Checked = Json2Boolean(elm, "hedging");
  194. this.chkDerivativesForMarket.Checked = Json2Boolean(elm, "marketAccess");
  195. this.chkDerivatesForLeverage.Checked = Json2Boolean(elm, "leverage");
  196. this.tspDerivatives.Text = element.GetProperty("t").ToString();
  197. this.tspDerivatives.ForeColor = IsChangedRecently(this.tspDerivatives.Text) ? COLOR_MODIFIED : COLOR_NORMAL;
  198. }
  199. }
  200. }
  201. }
  202. private void LoadPriceTarget(JsonElement jsonElement)
  203. {
  204. if (jsonElement.ValueKind != JsonValueKind.Object) return;
  205. bool hasData = jsonElement.TryGetProperty("priceTarget", out JsonElement element);
  206. if (hasData)
  207. {
  208. if (element.ValueKind == JsonValueKind.Object)
  209. {
  210. hasData = element.TryGetProperty("v", out JsonElement elm);
  211. if (hasData)
  212. {
  213. this.chkPriceTarget.Checked = Json2Boolean(elm, "assigned");
  214. this.txtOverrunPriceTarget.Text = Json2Text(elm, "overrun");
  215. this.tspPriceTarget.Text = element.GetProperty("t").ToString();
  216. this.tspPriceTarget.ForeColor = IsChangedRecently(this.tspPriceTarget.Text) ? COLOR_MODIFIED : COLOR_NORMAL;
  217. }
  218. }
  219. }
  220. }
  221. private void FundQ_Load(object sender, EventArgs e)
  222. {
  223. }
  224. private void btnSaveGeneralInfo_Click(object sender, EventArgs e)
  225. {
  226. try
  227. {
  228. // 数据对象
  229. var textData = new
  230. {
  231. // -- General Info --
  232. investmentObjective = AddInfo(this.txtInvestmentObjective.Text, userId),
  233. benchmark = AddInfo(this.txtBenchmark.Text, userId),
  234. investmentPhilosophy = AddInfo(this.txtInvestmentPhilosophy.Text, userId),
  235. portfolioManagers = AddInfo(Utility.DataTable2List((DataTable)this.grdPortfolioManager.DataSource), userId),
  236. totalExpenseRatio = AddInfo(Utility.DataTable2List((DataTable)this.grdTER.DataSource), userId),
  237. currentFees = AddInfo(new
  238. {
  239. managementFee = this.txtManagementFee.Text,
  240. subscriptionFee = this.txtSubscriptionFee.Text,
  241. redemptionFee = this.txtRedemptionFee.Text,
  242. administrationFee = this.txtAdministrationFee.Text,
  243. switchingFee = this.txtSwitchingFee.Text,
  244. trusteeFee = this.txtTrusteeFee.Text,
  245. performanceFee = this.txtPerformanceFee.Text
  246. }, userId),
  247. policyOfClosingFund = AddInfo(this.txtPolicyOfClosingFund.Text, userId),
  248. // -- Process --
  249. derivatives = AddInfo( new
  250. {
  251. allowed = this.chkAllowDerivatives.Checked,
  252. currentUse = this.chkUseDerivatives.Checked,
  253. effectiveManagement = this.chkDerivativesForEfficent.Checked,
  254. hedging = this.chkDerivatesForHedging.Checked,
  255. marketAccess = this.chkDerivativesForMarket.Checked,
  256. leverage = this.chkDerivatesForLeverage.Checked
  257. }, userId),
  258. priceTarget = AddInfo( new
  259. {
  260. assigned = this.chkPriceTarget.Checked,
  261. overrun = this.txtOverrunPriceTarget.Text
  262. }, userId)
  263. };
  264. // 序列化选项
  265. var options = new JsonSerializerOptions
  266. {
  267. WriteIndented = true,
  268. PropertyNamingPolicy = JsonNamingPolicy.CamelCase
  269. };
  270. string jsonString = JsonSerializer.Serialize(textData, options);
  271. //MessageBox.Show($"Save JSON: \n{jsonString}", "Json Result");
  272. int ret = DataAccess.Set_dd_fund_info(this.fundId, DateTime.Today, jsonString, 1, 1, userId);
  273. if (ret == 1)
  274. {
  275. this.Close();
  276. }
  277. }
  278. catch (Exception ex)
  279. {
  280. MessageBox.Show($"Error: {ex.Message}", "Error");
  281. }
  282. }
  283. private void grdTER_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
  284. {
  285. string errMsg = "Please enter a number between 0 and 100, with up to 3 decimal digits";
  286. if (grdTER.Columns[e.ColumnIndex].Name == "ter" && e.RowIndex >= 0)
  287. {
  288. // 先清除可能存在的旧错误提示
  289. grdTER.Rows[e.RowIndex].ErrorText = "";
  290. if (e.FormattedValue.ToString().Trim() == "") return;
  291. // 尝试将输入的值转换为小数
  292. if (!decimal.TryParse(e.FormattedValue.ToString(), out decimal newValue))
  293. {
  294. // 如果转换失败,说明输入的不是有效数字
  295. e.Cancel = true;
  296. grdTER.Rows[e.RowIndex].ErrorText = errMsg;
  297. return;
  298. }
  299. // 验证数值范围(例如限制在0到100之间)
  300. if (newValue < 0 || newValue > 100)
  301. {
  302. e.Cancel = true;
  303. grdTER.Rows[e.RowIndex].ErrorText = errMsg;
  304. return;
  305. }
  306. // (可选) 自定义验证小数位数
  307. string[] parts = e.FormattedValue.ToString().Split('.');
  308. if (parts.Length > 1 && parts[1].Length > 3) // 检查小数点后的位数
  309. {
  310. e.Cancel = true;
  311. grdTER.Rows[e.RowIndex].ErrorText = errMsg;
  312. }
  313. }
  314. }
  315. private void btnSaveFundProcess_Click(object sender, EventArgs e)
  316. {
  317. }
  318. private void chkAllowDerivatives_CheckedChanged(object sender, EventArgs e)
  319. {
  320. bool isChecked = chkAllowDerivatives.Checked;
  321. this.chkUseDerivatives.Enabled = isChecked;
  322. if (isChecked == false)
  323. {
  324. this.chkUseDerivatives.Checked = false;
  325. }
  326. }
  327. private void chkUseDerivatives_CheckedChanged(object sender, EventArgs e)
  328. {
  329. bool isChecked = chkUseDerivatives.Checked;
  330. this.chkDerivativesForEfficent.Enabled = isChecked;
  331. this.chkDerivatesForHedging.Enabled = isChecked;
  332. this.chkDerivativesForMarket.Enabled = isChecked;
  333. this.chkDerivatesForLeverage.Enabled = isChecked;
  334. if (isChecked == false)
  335. {
  336. this.chkDerivativesForEfficent.Checked = false;
  337. this.chkDerivatesForHedging.Checked = false;
  338. this.chkDerivativesForMarket.Checked = false;
  339. this.chkDerivatesForLeverage.Checked = false;
  340. }
  341. }
  342. private void chkPriceTarget_CheckedChanged(object sender, EventArgs e)
  343. {
  344. bool isChecked = chkPriceTarget.Checked;
  345. if (!isChecked) { this.txtOverrunPriceTarget.Text = ""; }
  346. }
  347. private void btnUploadProcessDiagram_Click(object sender, EventArgs e)
  348. {
  349. this.ofdProcessDiagram.Title = "Upload Diagram File -- FAKE";
  350. if (this.ofdProcessDiagram.ShowDialog() == DialogResult.OK)
  351. {
  352. // TODO: upload files to server. let's fake it for now
  353. string filePath = ofdProcessDiagram.FileName;
  354. string fileName = Path.GetFileName(filePath);
  355. try
  356. {
  357. }
  358. catch (Exception ex)
  359. {
  360. MessageBox.Show("File was not able to be uploaded:" + ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
  361. }
  362. }
  363. }
  364. private void btnAddManager_Click(object sender, EventArgs e)
  365. {
  366. using (PersonSelectionDialog dialog = new PersonSelectionDialog(companyId))
  367. {
  368. dialog.StartPosition = FormStartPosition.CenterParent;
  369. DialogResult result = dialog.ShowDialog(this);
  370. if (result == DialogResult.OK && dialog.PersonnelId != "")
  371. {
  372. DataTable dt = (DataTable)this.grdPortfolioManager.DataSource;
  373. DataRow row = dt.NewRow();
  374. row["personnel_id"] = dialog.PersonnelId;
  375. row["name"] = dialog.PersonnelName;
  376. row["startDate"] = dialog.StartDate.Date;
  377. dt.Rows.Add(row);
  378. }
  379. }
  380. }
  381. private void grdPortfolioManager_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
  382. {
  383. int rowIndex = e.RowIndex;
  384. int columnIndex = e.ColumnIndex;
  385. if (rowIndex < 0 || columnIndex < 0) { return; }
  386. // 先清除可能存在的旧错误提示
  387. grdPortfolioManager.Rows[rowIndex].ErrorText = "";
  388. // 如果转换失败,说明输入的不是有效日期
  389. bool isPass = DateTime.TryParse(grdPortfolioManager.Rows[rowIndex].Cells["startDate"].Value.ToString(), out DateTime newDate);
  390. if (!isPass || newDate < new DateTime(1900, 1, 1) || newDate > DateTime.Today)
  391. {
  392. e.Cancel = true;
  393. grdPortfolioManager.Rows[columnIndex].ErrorText = "Please enter a proper start date";
  394. return;
  395. }
  396. if (grdPortfolioManager.Rows[rowIndex].Cells["endDate"].Value.ToString() == "") return;
  397. isPass = DateTime.TryParse(grdPortfolioManager.Rows[rowIndex].Cells["endDate"].Value.ToString(), out newDate);
  398. if (!isPass || newDate < new DateTime(1900, 1, 1) || newDate > DateTime.Today)
  399. {
  400. e.Cancel = true;
  401. grdPortfolioManager.Rows[columnIndex].ErrorText = "Please enter a proper end date"; ;
  402. return;
  403. }
  404. }
  405. private void grdPortfolioManager_Validating(object sender, CancelEventArgs e)
  406. {
  407. }
  408. }
  409. }