ledger.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. const apiKey = document.querySelector('#api-key') as HTMLInputElement;
  2. {
  3. const storedApiKey = localStorage.getItem('fio-api-key');
  4. if (storedApiKey)
  5. apiKey.value = storedApiKey;
  6. }
  7. document.querySelector('#fetch')!.addEventListener('click', async () => {
  8. const loader = document.querySelector('#loader') as HTMLElement;
  9. loader.innerHTML = '';
  10. loader.style.display = 'block';
  11. try {
  12. await renderLedger(apiKey.value);
  13. localStorage.setItem('fio-api-key', apiKey.value);
  14. } catch (e) {
  15. console.error(e);
  16. }
  17. loader.style.display = 'none';
  18. });
  19. async function getPrices(): Promise<Record<string, number | null>> {
  20. const allPrices: Price[] = await fetchJSON('https://refined-prun.github.io/refined-prices/all.json');
  21. const priceMap: Record<string, number | null> = {};
  22. for (const price of allPrices)
  23. if (price.ExchangeCode === 'IC1')
  24. priceMap[price.MaterialTicker] = price.VWAP30D;
  25. console.log(priceMap);
  26. return priceMap;
  27. }
  28. const pricePromise = getPrices();
  29. const ledger = document.querySelector('textarea#ledger') as HTMLTextAreaElement;
  30. async function renderLedger(apiKey: string): Promise<void> {
  31. ledger.style.display = 'none';
  32. ledger.value = 'Time,Mat,Quantity,Actual Unit Price,Discounted Unit Price,Gateway,Debit,Credit,Contract ID\n';
  33. const prices = await pricePromise;
  34. const contracts: Contract[] = await fetchJSON('https://rest.fnar.net/contract/allcontracts',
  35. {headers: {'Authorization': apiKey}});
  36. contracts.sort((a, b) => a.DateEpochMs - b.DateEpochMs);
  37. for (const contract of contracts) {
  38. if (contract.PartnerCompanyCode === null || contract.PartnerName.endsWith(' Commodity Exchange')) continue; // NPC contract
  39. contract.Conditions.sort((a, b) => a.ConditionIndex - b.ConditionIndex);
  40. for (let i = 0; i < contract.Conditions.length; i++) {
  41. const condition = contract.Conditions[i];
  42. if ((contract.Party !== condition.Party && condition.Type === 'DELIVERY') || // counterparty is delivering
  43. (contract.Party === condition.Party && condition.Type === 'COMEX_PURCHASE_PICKUP')) { // we are picking up
  44. const time = new Date(contract.DateEpochMs).toISOString();
  45. const mat = condition.MaterialTicker;
  46. const quantity = condition.MaterialAmount;
  47. const totalPrice = contract.Conditions[i-1].Amount;
  48. ledger.value += `${time},${mat},${quantity},${prices[mat]},${totalPrice / quantity},,${totalPrice},,${contract.ContractLocalId}\n`;
  49. } else
  50. continue;
  51. }
  52. }
  53. ledger.style.display = 'block';
  54. }
  55. document.querySelector('#copy')!.addEventListener('click', () => {
  56. navigator.clipboard.writeText(ledger.value);
  57. });
  58. async function fetchJSON(url: string, options: RequestInit = {}): Promise<any> {
  59. const controller = new AbortController();
  60. const timeoutId = setTimeout(() => controller.abort(), 5000);
  61. const doc = await fetch(url, {...options, signal: controller.signal}).then((r) => r.json());
  62. clearTimeout(timeoutId);
  63. return doc;
  64. }
  65. interface Contract {
  66. Conditions: Array<ContractCondition>;
  67. ContractLocalId: string;
  68. Party: string;
  69. Status: string;
  70. PartnerName: string;
  71. PartnerCompanyCode: string;
  72. DateEpochMs: number;
  73. }
  74. interface ContractCondition {
  75. ConditionIndex: number;
  76. Type: string;
  77. Party: string;
  78. MaterialTicker: string;
  79. MaterialAmount: number;
  80. Amount: number;
  81. Currency: string;
  82. }
  83. interface Price {
  84. MaterialTicker: string
  85. ExchangeCode: string
  86. VWAP30D: number | null
  87. }