client_g.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <html>
  2. <head>
  3. <title>Simple client</title>
  4. <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  5. <script type="text/javascript" src="https://www.google.com/jsapi"></script>
  6. </head>
  7. <style>
  8. ul#menu li {
  9. display:inline;
  10. }
  11. table.minimalistBlack {
  12. border: 1px solid #000000;
  13. width: 100%;
  14. text-align: left;
  15. border-collapse: collapse;
  16. }
  17. table.minimalistBlack td, table.minimalistBlack th {
  18. border: 1px solid #000000;
  19. padding: 5px 4px;
  20. }
  21. table.minimalistBlack tbody td {
  22. font-size: 12px;
  23. }
  24. table.minimalistBlack thead {
  25. background: #CFCFCF;
  26. background: -moz-linear-gradient(top, #dbdbdb 0%, #d3d3d3 66%, #CFCFCF 100%);
  27. background: -webkit-linear-gradient(top, #dbdbdb 0%, #d3d3d3 66%, #CFCFCF 100%);
  28. background: linear-gradient(to bottom, #dbdbdb 0%, #d3d3d3 66%, #CFCFCF 100%);
  29. border-bottom: 3px solid #000000;
  30. }
  31. table.minimalistBlack thead th {
  32. font-size: 12px;
  33. font-weight: bold;
  34. color: #000000;
  35. text-align: left;
  36. }
  37. table.minimalistBlack tfoot {
  38. font-size: 13px;
  39. font-weight: bold;
  40. color: #000000;
  41. border-top: 3px solid #000000;
  42. }
  43. table.minimalistBlack tfoot td {
  44. font-size: 13px;
  45. }
  46. .container{
  47. max-width: 20px;
  48. margin: o auto;
  49. padding: 5px;
  50. }
  51. .bg-led{
  52. padding: 5px 8px;
  53. max-width: 20px;
  54. margin: o auto;
  55. background-color:green;
  56. }
  57. .chartWithMarkerOverlay {
  58. position: relative;
  59. width: 700px;
  60. }
  61. .overlay-text {
  62. width: 200px;
  63. height: 200px;
  64. position: absolute;
  65. top: 30px; /* chartArea top */
  66. left: 200px; /* chartArea left */
  67. }
  68. .overlay-marker {
  69. width: 50px;
  70. height: 50px;
  71. position: absolute;
  72. top: 375px; /* chartArea top */
  73. left: 350px; /* chartArea left */
  74. color: #000066;
  75. font: 15px arial;
  76. }
  77. </style>
  78. <body>
  79. <ul id="menu">
  80. <li><input type="radio" name='acct' id="acct" value="U9050568" checked="checked"> U9050568</li>
  81. <li><input type="radio" name='acct' id="acct" value="U8080985"> U8080985</li>
  82. <li><button id="reload_port">Reload Portfolio</button></li>
  83. <li><button id="port-btn">Update summary information</button></li>
  84. <li class="bg-led indicator"></li>
  85. </ul>
  86. <table id='port_sum' class="minimalistBlack">
  87. <thead>
  88. <tr>
  89. <th>Total Delta/Theta</th>
  90. <th>Futures Delta/Theta</th>
  91. <th>Call Delta/Theta</th>
  92. <th>Put Delta/Theta</th>
  93. <th>Calls/Puts</th>
  94. <th>PL / Potential Gain<div class="container"></th>
  95. <th>HSIF Curr / Next Mth</th>
  96. <th>change %</th>
  97. </tr>
  98. </thead>
  99. <tbody>
  100. <tr>
  101. <td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td></tr>
  102. <tr>
  103. <td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td><td>(nan)</td></tr>
  104. <tr>
  105. </tbody>
  106. </tr>
  107. </table>
  108. <div id="chart_div"></div>
  109. <div id="table_div" style="height:400"></div>
  110. <div class="overlay-marker">
  111. <img src="public/green_marker.png" height="50"><div id='spot'></div>
  112. </div>
  113. <button id="change-btn">change columns</button>
  114. <form onsubmit="onSubmit(); return false;">
  115. <input type="text" id="input">
  116. <input type="submit" value="Send">
  117. <button onclick="onCloseClick(); return false;">close</button>
  118. </form>
  119. <div id="log"></div>
  120. <script>
  121. var account;
  122. $(document).ready(function () {
  123. // retrieve the last saved preference
  124. var account = localStorage.getItem('account');
  125. console.log('retrieving the account code from storage => ' + (account == null ? '<not found>' : account));
  126. if (account == null){
  127. account = $('input[id=acct]:checked').val();
  128. }
  129. qs = "input[id=acct][value='" + account + "']"
  130. document.querySelectorAll(qs)[0].checked=true;
  131. // test: setCellValue('port_sum', 1, 2, 'x6yz');
  132. });
  133. $('#port-btn').click(function(){
  134. account = $('input[id=acct]:checked').val()
  135. ws.send(JSON.stringify({event: 'event_request_port_summary', target_resource:{}, 'account': account}));
  136. });
  137. $('#reload_port').click(function(){
  138. alert('Reloading portfolio...');
  139. // save the account number that was selected
  140. // so that it could be retrieved when the page
  141. // is reloaded later
  142. localStorage.setItem('account', $('input[id=acct]:checked').val());
  143. if (ws != null){
  144. ws.close();
  145. location.reload();
  146. }
  147. });
  148. var chartOptions = {
  149. width: 800,
  150. height: 400,
  151. legend: { position: 'right', maxLines: 3 },
  152. bar: { groupWidth: '85%' },
  153. isStacked: true,allowHtml: true,
  154. /*series: {
  155. 0:{color:'lightgreen'},
  156. 1:{color:'black'},
  157. 2:{color:'#1E90FF',},
  158. }*/
  159. };
  160. var columnChart = {'view': null, 'data': null, 'chart': null, 'options': chartOptions};
  161. var tableOptions = {allowHtml: true, sortColumn:1,
  162. showRowNumber: true, width: '95%', height: '100%'};
  163. var tableChart = {'view': null, 'data': null, 'chart': null, 'options': tableOptions};
  164. google.load("visualization", "1.1", {packages:["corechart", 'table','gauge']});
  165. //google.setOnLoadCallback(drawTable);
  166. google.setOnLoadCallback(init);
  167. function setCellValue(table, rowNum, colNum, newValue)
  168. {
  169. //$('#port_sum').find("tr:eq(1)").find("td:eq(1)").html('new stuff');
  170. $('#'+table).find('tr:eq('+rowNum+')').find('td:eq(' + colNum + ')').html(newValue);
  171. };
  172. function getCellValue(table, rowNum, colNum)
  173. {
  174. //$('#port_sum').find("tr:eq(1)").find("td:eq(1)").html('new stuff');
  175. return $('#'+table).find('tr:eq('+rowNum+')').find('td:eq(' + colNum + ')').html();
  176. };
  177. function getRandomInt(min, max) {
  178. return Math.floor(Math.random() * (max - min + 1)) + min;
  179. }
  180. function setupFormatter(tableData){
  181. var numF = new google.visualization.NumberFormat(
  182. {prefix: '$', pattern:'0.00', negativeColor: 'red', negativeParens: true});
  183. var percentF = new google.visualization.NumberFormat(
  184. {pattern: '##.#%'})
  185. var arrowF = new google.visualization.ArrowFormat();
  186. var barF = new google.visualization.BarFormat({width: 80,
  187. colorPositive: 'green', max:100 });
  188. var barIV = new google.visualization.BarFormat({width: 80,
  189. colorPositive: 'red', min: 0, max:0.5 });
  190. var colorF = new google.visualization.ColorFormat();
  191. colorF.addRange(-100, 0, 'white', 'red');
  192. colorF.addRange(0, 100, 'white', 'blue');
  193. var colorGF = new google.visualization.ColorFormat();
  194. colorGF.addGradientRange(null, null, 'white', 'orange', 'blue');
  195. numF.format(tableChart.data, 2); //avg cost
  196. numF.format(tableChart.data, 3); //market value
  197. numF.format(tableChart.data, 4); //avg px
  198. colorF.format(tableChart.data, 6); // position
  199. percentF.format(tableChart.data, 7); //delta
  200. numF.format(tableChart.data, 10); // position delta
  201. numF.format(tableChart.data, 11); // position theta
  202. numF.format(tableChart.data, 12); // position gamma
  203. colorGF.format(tableChart.data, 13); //unreal p/l
  204. barF.format(tableChart.data, 14) //% gain loss
  205. barIV.format(tableChart.data, 15); //iv
  206. }
  207. var ws;
  208. var ws_url = "ws://localhost:9001/";
  209. var tableChart;
  210. function init() {
  211. var account = $('input[id=acct]:checked').val();
  212. // Connect to Web Socket
  213. ws = new WebSocket(ws_url);
  214. // Set event handlers.
  215. ws.onopen = function() {
  216. var account = $('input[id=acct]:checked').val();
  217. output("onopen account = " + account );
  218. ws.send(JSON.stringify({event: 'event_tm_request_table_structure', target_resource:{'class': 'Portfolio'}, 'account': account}));
  219. ws.send(JSON.stringify({event: 'event_tm_request_table_structure', target_resource:{'class': 'PortfolioColumnChartTM'}, 'account': account}));
  220. ws.send(JSON.stringify({event: 'event_request_port_summary', target_resource:{}, 'account': account}));
  221. };
  222. ws.onmessage = function(e) {
  223. // e.data contains received string.
  224. // signal incoming by flashing a green box
  225. $('.indicator').fadeIn(50).fadeOut(50);
  226. try {
  227. d1= JSON.parse(e.data);
  228. }
  229. catch(err) {
  230. output("onmessage:json parse exception: value= " + e.data);
  231. console.log('error parsing e.data' + e.data);
  232. }
  233. // 2019.1 if the messageis not intended
  234. if (d1.event == 'event_tm_table_structure_changed' && d1.account != account)
  235. return;
  236. else if (d1.event == 'event_tm_table_row_updated' && d1['value']['source']['account'] != account)
  237. return;
  238. if (d1.event == "event_tm_table_structure_changed"){
  239. if (d1.source.class == 'Portfolio'){
  240. tableChart.data = new google.visualization.DataTable(d1.value);
  241. tableChart.view = new google.visualization.DataView(tableChart.data);
  242. tableChart.view.setRows(tableChart.view.getFilteredRows([{column: 16, test: function(value, row, column, table) {
  243. return (value == 'HSI' || value == 'MHI')
  244. }},
  245. {column: 6, test: function(value, row, column, table) {
  246. return (value != 0)
  247. }},
  248. ]));
  249. setupFormatter(tableChart.data);
  250. tableChart.chart = new google.visualization.Table(document.getElementById('table_div'));
  251. tableChart.chart.draw(tableChart.view, tableChart.options);
  252. output('number of rows: ' + tableChart.data.getNumberOfRows());
  253. } else if (d1.source.class == 'PortfolioColumnChartTM'){
  254. columnChart.data = new google.visualization.DataTable(d1.value);
  255. console.log('updating pcc for ' + d1.account);
  256. columnChart.view = new google.visualization.DataView(columnChart.data);
  257. columnChart.chart = new google.visualization.ColumnChart(
  258. document.getElementById('chart_div'));
  259. columnChart.chart.draw(columnChart.view, columnChart.options);
  260. google.visualization.events.addListener(columnChart.chart, 'ready', placeIndexMarker);
  261. }
  262. } else if (d1.event == 'event_tm_table_row_updated'){
  263. //console.log(d1.value.row.toString()+ ':' + d1.value.row_values[0]['v']);
  264. for (var c=2; c < d1.value.row_values.length; c++){
  265. tableChart.data.setCell(d1.value.row, c, d1.value.row_values[c]["v"]);
  266. }
  267. //data.setCell(d1.value.row, 1, d1.value.row_values[0]['v']);
  268. tableChart.options.sortColumn = tableChart.chart.getSortInfo().column;
  269. tableChart.chart.draw(tableChart.view, tableChart.options);
  270. } else if (d1.event == 'event_tm_table_row_inserted'){
  271. var newRow = d1.value.row_values.map(function(x){
  272. return x['v'];
  273. });
  274. output('adding new row: ' + newRow.toString());
  275. data.addRow(newRow);
  276. options.sortColumn = table.getSortInfo().column;
  277. table.draw(view, options);
  278. } else if (d1.event == 'event_port_values_updated' && d1.account == account){
  279. setPortSummaryTableVal(d1.value);
  280. } else if (d1.event == 'tickPrice'){
  281. if (d1.current_or_next == 'current'){
  282. setCellValue('port_sum', 1, 6, d1.price);
  283. if (d1.change)
  284. setCellValue('port_sum', 1, 7, d1.change.toFixed(2));
  285. }
  286. else{ // next month
  287. setCellValue('port_sum', 2, 6, d1.price);
  288. if (d1.change)
  289. setCellValue('port_sum', 2, 7, d1.change.toFixed(2));
  290. }
  291. if (columnChart){
  292. placeIndexMarker();
  293. }
  294. }
  295. };
  296. ws.onclose = function() {
  297. output("onclose");
  298. };
  299. ws.onerror = function(e) {
  300. output("onerror");
  301. console.log(e)
  302. };
  303. }
  304. function placeIndexMarker(){
  305. // get the current month futures spot px
  306. var spot = getCellValue('port_sum', 1, 6);
  307. var cli = columnChart.chart.getChartLayoutInterface();
  308. //top = cli.getChartAreaBoundingBox().top;
  309. //height = cli.getChartAreaBoundingBox().height;
  310. top = cli.getBoundingBox('chartarea').top;
  311. height = cli.getBoundingBox('chartarea').height;
  312. document.querySelector('.overlay-marker').style.top = Math.floor(top - height) + "px";
  313. document.querySelector('.overlay-marker').style.left = Math.floor(cli.getXLocation(spot) - 25) + "px";
  314. }
  315. function setPortSummaryTableVal(port_values){
  316. var val2cellDict = {
  317. 9000: [1,0],
  318. 9001: [1,1],
  319. 9002: [1,2],
  320. 9003: [1,3],
  321. 9031: [1,4],
  322. 9040: [1,5],
  323. 9010: [2,0],
  324. 9012: [2,2],
  325. 9013: [2,3],
  326. 9032: [2,4],
  327. 9041: [2,5]
  328. }
  329. for(var port_id in val2cellDict) {
  330. //console.log('in setPortSummaryTableVal ' + String(port_id) + ", ");
  331. var num = Number(port_values[port_id]);
  332. num = (isFloat(num)) ? num.toFixed(2) : num;
  333. setCellValue('port_sum', val2cellDict[port_id][0], val2cellDict[port_id][1], num);
  334. }
  335. }
  336. function isFloat(n){
  337. return Number(n) === n && n % 1 !== 0;
  338. }
  339. function onSubmit() {
  340. var input = document.getElementById("input");
  341. // You can send message to the Web Socket using ws.send.
  342. ws.send(input.value);
  343. output("send: " + input.value);
  344. input.value = "";
  345. input.focus();
  346. }
  347. function onCloseClick() {
  348. ws.close();
  349. }
  350. function output(str) {
  351. var log = document.getElementById("log");
  352. var escaped = str.replace(/&/, "&amp;").replace(/</, "&lt;").
  353. replace(/>/, "&gt;").replace(/"/, "&quot;"); // "
  354. log.innerHTML = escaped + "<br>" + log.innerHTML;
  355. }
  356. </script>
  357. </body>
  358. </html>