Xueqiu Follow Helper

在雪球组合上显示最近一个交易日调仓的成交价。允许为每个组合设置预算,并根据预算计算应买卖的股数。

当前为 2015-10-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Xueqiu Follow Helper
  3. // @namespace https://github.com/henix/userjs/xueqiu_helper
  4. // @description 在雪球组合上显示最近一个交易日调仓的成交价。允许为每个组合设置预算,并根据预算计算应买卖的股数。
  5. // @author henix
  6. // @version 20151011.1
  7. // @include http://xueqiu.com/P/*
  8. // @license MIT License
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/dom4/1.5.1/dom4.js
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @grant GM_xmlhttpRequest
  13. // @grant unsafeWindow
  14. // @grant GM_addStyle
  15. // ==/UserScript==
  16.  
  17. /**
  18. * https://github.com/jed/domo/blob/master/lib/domo.js
  19. */
  20. // domo.js 0.5.7
  21.  
  22. // (c) 2012 Jed Schmidt
  23. // domo.js is distributed under the MIT license.
  24. // For more details, see http://domo-js.com
  25.  
  26. !function() {
  27. // Determine the global object.
  28. var global = Function("return this")()
  29.  
  30. // Valid HTML5 tag names used to generate DOM functions.
  31. var tags = [
  32. "A", "ABBR", "ACRONYM", "ADDRESS", "AREA", "ARTICLE", "ASIDE", "AUDIO",
  33. "B", "BDI", "BDO", "BIG", "BLOCKQUOTE", "BODY", "BR", "BUTTON",
  34. "CANVAS", "CAPTION", "CITE", "CODE", "COL", "COLGROUP", "COMMAND",
  35. "DATALIST", "DD", "DEL", "DETAILS", "DFN", "DIV", "DL", "DT", "EM",
  36. "EMBED", "FIELDSET", "FIGCAPTION", "FIGURE", "FOOTER", "FORM", "FRAME",
  37. "FRAMESET", "H1", "H2", "H3", "H4", "H5", "H6", "HEAD", "HEADER",
  38. "HGROUP", "HR", "HTML", "I", "IFRAME", "IMG", "INPUT", "INS", "KBD",
  39. "KEYGEN", "LABEL", "LEGEND", "LI", "LINK", "MAP", "MARK", "META",
  40. "METER", "NAV", "NOSCRIPT", "OBJECT", "OL", "OPTGROUP", "OPTION",
  41. "OUTPUT", "P", "PARAM", "PRE", "PROGRESS", "Q", "RP", "RT", "RUBY",
  42. "SAMP", "SCRIPT", "SECTION", "SELECT", "SMALL", "SOURCE", "SPAN",
  43. "SPLIT", "STRONG", "STYLE", "SUB", "SUMMARY", "SUP", "TABLE", "TBODY",
  44. "TD", "TEXTAREA", "TFOOT", "TH", "THEAD", "TIME", "TITLE", "TR",
  45. "TRACK", "TT", "UL", "VAR", "VIDEO", "WBR"
  46. ]
  47.  
  48. // Turn a camelCase string into a hyphenated one.
  49. // Used for CSS property names and DOM element attributes.
  50. function hyphenify(text) {
  51. return text.replace(/[A-Z]/g, "-$&").toLowerCase()
  52. }
  53.  
  54. // Cache select Array/Object methods
  55. var shift = Array.prototype.shift
  56. var unshift = Array.prototype.unshift
  57. var concat = Array.prototype.concat
  58. var has = Object.prototype.hasOwnProperty
  59.  
  60. // Export the Domo constructor for a CommonJS environment,
  61. // or create a new Domo namespace otherwise.
  62. typeof module == "object"
  63. ? module.exports = Domo
  64. : new Domo(global.document).global(true)
  65.  
  66. // Create a new domo namespace, scoped to the given document.
  67. function Domo(document) {
  68. if (!document) throw new Error("No document provided.")
  69.  
  70. this.domo = this
  71.  
  72. // Create a DOM comment
  73. this.COMMENT = function(nodeValue) {
  74. return document.createComment(nodeValue)
  75. }
  76.  
  77. // Create a DOM text node
  78. this.TEXT = function(nodeValue) {
  79. return document.createTextNode(nodeValue)
  80. }
  81.  
  82. // Create a DOM fragment
  83. this.FRAGMENT = function() {
  84. var fragment = document.createDocumentFragment()
  85. var childNodes = concat.apply([], arguments)
  86. var length = childNodes.length
  87. var i = 0
  88. var child
  89.  
  90. while (i < length) {
  91. child = childNodes[i++]
  92.  
  93. while (typeof child == "function") child = child()
  94.  
  95. if (child == null) child = this.COMMENT(child)
  96.  
  97. else if (!child.nodeType) child = this.TEXT(child)
  98.  
  99. fragment.appendChild(child)
  100. }
  101.  
  102. return fragment
  103. }
  104.  
  105. // Create a DOM element
  106. this.ELEMENT = function() {
  107. var childNodes = concat.apply([], arguments)
  108. var nodeName = childNodes.shift()
  109. var element = document.createElement(nodeName)
  110. var attributes = childNodes[0]
  111.  
  112. if (attributes) {
  113. if (typeof attributes == "object" && !attributes.nodeType) {
  114. for (var name in attributes) if (has.call(attributes, name)) {
  115. element.setAttribute(hyphenify(name), attributes[name])
  116. }
  117.  
  118. childNodes.shift()
  119. }
  120. }
  121.  
  122. if (childNodes.length) {
  123. element.appendChild(
  124. this.FRAGMENT.apply(this, childNodes)
  125. )
  126. }
  127.  
  128. switch (nodeName) {
  129. case "HTML":
  130. case "HEAD":
  131. case "BODY":
  132. var replaced = document.getElementsByTagName(nodeName)[0]
  133.  
  134. if (replaced) replaced.parentNode.replaceChild(element, replaced)
  135. }
  136.  
  137. return element
  138. }
  139.  
  140. // Convenience functions to create each HTML5 element
  141. var i = tags.length
  142. while (i--) !function(domo, nodeName) {
  143. domo[nodeName] =
  144. domo[nodeName.toLowerCase()] =
  145.  
  146. function() {
  147. unshift.call(arguments, nodeName)
  148. return domo.ELEMENT.apply(domo, arguments)
  149. }
  150. }(this, tags[i])
  151.  
  152. // Create a CSS style rule
  153. this.STYLE.on = function() {
  154. var selector = String(shift.call(arguments))
  155. var rules = concat.apply([], arguments)
  156. var css = selector + "{"
  157. var i = 0
  158. var l = rules.length
  159. var key
  160. var block
  161.  
  162. while (i < l) {
  163. block = rules[i++]
  164.  
  165. switch (typeof block) {
  166. case "object":
  167. for (key in block) {
  168. css += hyphenify(key) + ":" + block[key] + ";"
  169. }
  170. break
  171.  
  172. case "string":
  173. css = selector + " " + block + css
  174. break
  175. }
  176. }
  177.  
  178. css += "}\n"
  179.  
  180. return css
  181. }
  182.  
  183. // Pollute the global scope for convenience.
  184. this.global = function(on) {
  185. var values = this.global.values
  186. var key
  187. var code
  188.  
  189. if (on !== false) {
  190. global.domo = this
  191.  
  192. for (key in this) {
  193. code = key.charCodeAt(0)
  194.  
  195. if (code < 65 || code > 90) continue
  196.  
  197. if (this[key] == global[key]) continue
  198.  
  199. if (key in global) values[key] = global[key]
  200.  
  201. global[key] = this[key]
  202. }
  203. }
  204.  
  205. else {
  206. try {
  207. delete global.domo
  208. } catch (e) {
  209. global.domo = undefined
  210. }
  211.  
  212. for (key in this) {
  213. if (key in values) {
  214. if (global[key] == this[key]) global[key] = values[key]
  215. }
  216.  
  217. else delete global[key]
  218. }
  219. }
  220.  
  221. return this
  222. }
  223.  
  224. // A place to store previous global properties
  225. this.global.values = {}
  226. }
  227. }()
  228. ;
  229.  
  230. /**
  231. * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
  232. */
  233. Math.sign = Math.sign || function(x) {
  234. x = +x; // convert to a number
  235. if (x === 0 || isNaN(x)) {
  236. return x;
  237. }
  238. return x > 0 ? 1 : -1;
  239. };
  240.  
  241. domo.global(true);
  242.  
  243. var symbol = unsafeWindow.SNB.cubeInfo.symbol;
  244.  
  245. function myround(x) {
  246. return Math.sign(x) * Math.round(Math.abs(x));
  247. }
  248.  
  249. function renderActions(actions, budget) {
  250. var trs = actions.map(function(a) {
  251. var utime = new Date(a.updated_at);
  252. function pad(x) { return x > 10 ? x : "0" + x; }
  253. return [TR(TD({colspan:4}, utime.getFullYear() + "-" + (utime.getMonth()+1) + "-" + utime.getDate() + " " + utime.getHours() + ":" + pad(utime.getMinutes()) + ":" + pad(utime.getSeconds())))].concat(a.rebalancing_histories.map(function(r) {
  254. var prev_weight = r.prev_weight_adjusted || 0;
  255. return TR(TD(r.stock_name + "(" + r.stock_symbol + ")"), TD(prev_weight + "% → " + r.target_weight + "%"), TD(r.price), TD(myround(budget * (r.target_weight - prev_weight) / 100 / r.price)));
  256. }));
  257. }).reduce(function(a, b) { return a.concat(b); }, []);
  258.  
  259. var input = INPUT({value:budget});
  260. var saveBut = INPUT({type:"button",value:"保存"});
  261. saveBut.addEventListener("click", function() {
  262. GM_setValue("budget." + symbol, input.value);
  263. alert("保存成功,请刷新页面");
  264. });
  265. var settings = DIV({"class":"budget-setting"}, "预算 ", input, " 元 ", saveBut);
  266.  
  267. return DIV({"class":"follow-details"},
  268. TABLE.apply(null, [TR(TH("名称"), TH("百分比"), TH("成交价"), TH("买卖股数"))].concat(trs)),
  269. settings
  270. );
  271. }
  272.  
  273. GM_xmlhttpRequest({
  274. method: "GET",
  275. url: "http://xueqiu.com/cubes/rebalancing/history.json?cube_symbol=" + symbol + "&count=20&page=1",
  276. onload: function(resp) {
  277. var histories = JSON.parse(resp.responseText);
  278. var now = new Date(histories.list[0].updated_at);
  279. var lastday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
  280. var actions = histories.list.filter(function(o) { return o.status == "success" && o.updated_at > lastday; });
  281. var weightCircle = document.getElementById("weight-circle");
  282. weightCircle.parentNode.insertBefore(renderActions(actions, parseInt(GM_getValue("budget." + symbol, 10000), 10)), weightCircle);
  283. }
  284. });
  285.  
  286. GM_addStyle(
  287. ".follow-details table { width: 100%; margin: 10px auto; }" +
  288. ".follow-details th { font-weight: bold; }" +
  289. ".follow-details th, .follow-details td { border: 1px solid black; padding: 0.5em; }" +
  290. ".follow-details .budget-setting { margin: 10px 0 20px 0; }"
  291. );

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址