From 7936a55ee5f2ee521a9889d796d218536d6a183f Mon Sep 17 00:00:00 2001 From: weibo Date: Sun, 16 Jun 2024 23:12:22 +0800 Subject: [PATCH 1/5] =?UTF-8?q?*=20=E6=B7=BB=E5=8A=A0=E6=9C=9F=E8=B4=A7?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .prettierrc | 3 +- README.md | 37 ++++++--- package.json | 25 +++++- src/configuration.ts | 12 ++- src/extension.ts | 95 +++++++++++++++-------- src/futures.ts | 176 +++++++++++++++++++++++++++++++++++++++++++ src/logger.js | 59 +++++++++++++++ src/render.ts | 91 ++++++++++++++++++++++ src/timer.ts | 16 ++-- test/api.http | 24 ++++++ tsconfig.json | 3 +- types/stock-bar.d.ts | 12 ++- 13 files changed, 497 insertions(+), 57 deletions(-) create mode 100644 src/futures.ts create mode 100644 src/logger.js create mode 100644 test/api.http diff --git a/.gitignore b/.gitignore index 0b60dfa..09193d1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist node_modules .vscode-test/ *.vsix +pnpm-lock.yaml diff --git a/.prettierrc b/.prettierrc index 854ffbe..a044392 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,6 @@ { "useTabs": true, "singleQuote": true, - "trailingComma": "all" + "trailingComma": "all", + "endOfLine": "auto" } diff --git a/README.md b/README.md index ba759f3..4806f8f 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,19 @@ VScode 插件 | A 股 | 港股 | 实时股票数据 | 状态栏实时更新 -`Stock Bar`会在开盘期间自动刷新股票数据,并在VScode底部状态栏显示股票基本数据,让你在使用VScode期间能随时关注到你的股票。 +`Stock Bar`会在开盘期间自动刷新股票数据,并在 VScode 底部状态栏显示股票基本数据,让你在使用 VScode 期间能随时关注到你的股票。 为了隐秘性,`Stock Bar`默认只会显示股价、百分点这样的纯数字,当你将鼠标移上去就可以查看详情。当然为了区分,也可以自定义显示股票的名称(建议使用英文字母,别问我为什么) ![image](https://raw.githubusercontent.com/Chef5/stock-bar/main/stock-bar-plugin.png) -插件已开源,开源地址:[Github](https://github.com/Chef5/stock-bar),欢迎点星星⭐️、提issue或者pr +插件已开源,开源地址:[Github](https://github.com/Chef5/stock-bar),欢迎点星星 ⭐️、提 issue 或者 pr ## 插件配置 修改用户配置,添加你所需要监控的股票代码 -``` js +```js // 股票:这是一个数组,你可以直接添加股票代码字符串,也可以添加对象,对象格式如下: // { // code: string, // 股票代码:需要添加股市前缀,前缀参考文档下方:前缀说明 @@ -29,6 +29,22 @@ VScode 插件 | A 股 | 港股 | 实时股票数据 | 状态栏实时更新 } ], +// 期货: 数组,可以添加期货代码 +// { +// code: string, //期货代码, 比如 sa9999, sa2409 +// alias: string, // 期货别名,这个可以不填,会自动根据代码获取 +// hold_price: number, //持仓价格,即多空单的价格 +// hold_number: number //持仓, 正数代表持有多单,负数代表持有空单,当配置了持仓价格和持仓后, +// 会显示持仓盈亏,否则显示当日价格涨跌幅 +// } +"stock-bar.futures": [ + { + "code": "sa9999", + "hold_price": 2500, + "hold_number": -1 + } +], + // 更新数据时间间隔,单位:毫秒 "stock-bar.updateInterval": 10000 @@ -41,10 +57,10 @@ VScode 插件 | A 股 | 港股 | 实时股票数据 | 状态栏实时更新 ## 前缀说明 -- sh:沪市,不加前缀的情况下,6开头的代码默认加上sh(上证指数:sh000001) -- sz:深市,不加前缀的情况下,除6开头的代码外,默认加上sz +- sh:沪市,不加前缀的情况下,6 开头的代码默认加上 sh(上证指数:sh000001) +- sz:深市,不加前缀的情况下,除 6 开头的代码外,默认加上 sz - hk:港股,如:阿里巴巴港股 hk09988 -- US_:美股,如:苹果股票 US_AAPL +- US\_:美股,如:苹果股票 US_AAPL - hkHSC:工商指数(港股指数) - hkHSCEI:恒生中国企业指数(港股指数) - hkHSI:恒生指数(港股指数) @@ -55,7 +71,7 @@ VScode 插件 | A 股 | 港股 | 实时股票数据 | 状态栏实时更新 - hkGEM:标普香港创业板指(港股指数) - US_DOWJONES:道琼斯指数(美股指数) - US_NASDAQ:纳斯达克(美股指数) -- US_SP500:标普500(美股指数) +- US_SP500:标普 500(美股指数) ## 更新日志 @@ -63,7 +79,7 @@ VScode 插件 | A 股 | 港股 | 实时股票数据 | 状态栏实时更新 ## 贡献者 -感谢这些可爱的贡献者参与开发和维护Stock Bar,让`Stock Bar`更加完美! +感谢这些可爱的贡献者参与开发和维护 Stock Bar,让`Stock Bar`更加完美!

@@ -80,7 +96,8 @@ VScode 插件 | A 股 | 港股 | 实时股票数据 | 状态栏实时更新 ## 源 -> 插件源:`Stock Bar`最初Fork自[stock-watch](https://github.com/TDGarden/stock-watch),现在已对其进行了重大重构。 +> 插件源:`Stock Bar`最初 Fork 自[stock-watch](https://github.com/TDGarden/stock-watch),现在已对其进行了重大重构。 > 股票数据来源: -> - 新浪 +> +> - 新浪 diff --git a/package.json b/package.json index 9e64770..dfb239f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "Other" ], "activationEvents": [ - "*" + "*" ], "main": "./dist/extension", "contributes": { @@ -34,6 +34,17 @@ ], "description": "股票代码数组,配置需要监控的股票代码,具体配置规则请查看插件详情页" }, + "stock-bar.futures": { + "type": "array", + "default": [ + { + "code": "sa9999", + "hold_price": 2300, + "hold_number": -1 + } + ], + "description": "商品期货数组,配置需要监控的商品期货,具体配置规则请查看插件详情页" + }, "stock-bar.updateInterval": { "type": "number", "default": 10000, @@ -50,7 +61,17 @@ "description": "股票跌的颜色,默认跟随系统" } } - } + }, + "commands": [ + { + "command": "stockbar.start", + "title": "stock watch start" + }, + { + "command": "stockbar.stop", + "title": "stock watch stop" + } + ] }, "scripts": { "vscode:prepublish": "npm run package", diff --git a/src/configuration.ts b/src/configuration.ts index 5811023..d273528 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { StockOptions } from 'stock-bar'; +import { StockOptions, FutureOptions } from 'stock-bar'; export default class Configuration { /** @@ -24,6 +24,16 @@ export default class Configuration { return stocks as StockOptions; } + static getFutures() { + const futures = Configuration.stockBarConfig().get('futures'); + if (!futures) { + return []; + } + const items = futures as FutureOptions; + items.forEach((item) => (item.code = item.code.toUpperCase())); + return items; + } + static getUpdateInterval() { const updateInterval = Configuration.stockBarConfig().get('updateInterval'); return typeof updateInterval === 'number' ? updateInterval : 10000; diff --git a/src/extension.ts b/src/extension.ts index 2faa008..fe10547 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,9 +2,11 @@ import * as vscode from 'vscode'; import logger from './logger'; import Configuration from './configuration'; import { sinaStockProvider } from './provider'; -import { render } from './render'; +import { render, renderFutures, stopAllRender } from './render'; import timer from './timer'; import Stock from './stock'; +import FutureHandler from './futures'; +import { clearInterval } from 'timers'; function loadChoiceStocks() { return Configuration.getStocks().map((v) => { @@ -20,45 +22,72 @@ function loadChoiceStocks() { }); } -export function activate(context: vscode.ExtensionContext) { - let stocks = loadChoiceStocks(); +let timer = null; +let stocks: Stock[]; - context.subscriptions.push( - vscode.workspace.onDidChangeConfiguration(() => { - stocks = loadChoiceStocks(); - }), - ); +function restart() { + const interval = Configuration.getUpdateInterval(); + if (timer) { + clearInterval(timer); + timer = null; + } + stocks = loadChoiceStocks(); + futureHandler.updateConfig(Configuration.getFutures()); - const task: () => any = async () => { - try { - // 从云端获取最新状态 - logger.debug('call fetchData'); - const data = await sinaStockProvider.fetch(stocks.map((v) => v.code)); - // 更新本地的数据 - for (const origin of data) { - const stock = stocks.find((v) => v.code === origin.code); - if (!stock) { - continue; - } - stock.update(origin); + timer = setInterval(ticker, interval); + ticker(); +} + +const futureHandler = new FutureHandler(); + +async function ticker() { + try { + // 从云端获取最新状态 + logger.debug('call fetchData'); + const [data, _] = await Promise.all([ + sinaStockProvider.fetch(stocks.map((v) => v.code)), + futureHandler.updateData(), + ]); + // 更新本地的数据 + for (const origin of data) { + const stock = stocks.find((v) => v.code === origin.code); + if (!stock) { + continue; } - // 渲染内容 - logger.debug('render'); - render(stocks); - } catch (e) { - logger.error('%O', e); + stock.update(origin); } + // 渲染内容 + logger.debug('render'); + render(stocks); + renderFutures(futureHandler.futures); + } catch (e) { + logger.error('%O', e); + } +} - // 阻塞等待下一个循环 - logger.debug('timer await'); - await timer.await(); +function stop() { + if (timer) { + clearInterval(timer); + timer = null; + } + stopAllRender(); +} + +export function activate(context: vscode.ExtensionContext) { + stocks = loadChoiceStocks(); - // 继续循环 - return task(); - }; + const startCmd = vscode.commands.registerCommand('stockbar.start', restart); + const stopCmd = vscode.commands.registerCommand('stockbar.stop', stop); - // 丢进宏任务队列 - setTimeout(task); + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration(() => { + if (timer) { + restart(); + } + }), + ); + context.subscriptions.push(startCmd); + context.subscriptions.push(stopCmd); } export function deactivate() { diff --git a/src/futures.ts b/src/futures.ts new file mode 100644 index 0000000..6ef8f78 --- /dev/null +++ b/src/futures.ts @@ -0,0 +1,176 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// + +import axios, { AxiosInstance } from 'axios'; +import { FutureOption } from 'stock-bar'; +import logger from './logger'; + +class Provider { + instance: AxiosInstance; + constructor() { + this.instance = axios.create({ + timeout: 8000, + }); + } + + async getBasicInfo(code: string) { + try { + const url = `https://fupage.10jqka.com.cn/futgwapi/api/f10/contract/v1/info?code=${code}`; + const ret = await this.instance.get(url); + const data = ret.data; + if (data.code) { + return null; + } + const name = data.data?.name || ''; + const tradeUnit = (data.data?.trade_unit || '').trim(); + let ratio = 0; + if (tradeUnit) { + const match = tradeUnit.match(/^\d*/); + if (match && match.length > 0) { + ratio = parseFloat(match[0]) || 0; + } + } + return { + code, + name, + ratio, + }; + } catch (err: unknown) { + logger.error('getBasicInfo error %O', err); + return null; + } + } + + async getLastestPrice(code: string) { + try { + const headers = { + Referer: 'https://goodsfu.10jqka.com.cn/', + 'Content-Type': 'application/json', + }; + const url = `https://d.10jqka.com.cn/v6/time/qh_${code}/last.js`; + const ret = (await this.instance.get(url, { headers })).data; + const match = ret.match(/\(({.*})\)/); + if (!match) { + return null; + } + + const js = JSON.parse(match[1]); + const keys = Object.keys(js); + if (keys.length <= 0) { + return null; + } + const data = js[keys[0]]; + const pre_price = parseFloat(data['pre']); + const allLines = (data['data'] || '').split(';'); + if (allLines.length <= 0) { + return null; + } + const lastLine = allLines[allLines.length - 1]; + if (!lastLine) return null; + const lineArr = lastLine.split(','); + const current_price = parseFloat(lineArr[1]); + const name = data['name'] || ''; + return { + name, + pre_price, + current_price, + }; + } catch (err: unknown) { + logger.error('getLastestPrice error %O', err); + return null; + } + } +} + +const provider = new Provider(); + +export class FutureData { + code: string; + name = ''; + alias: string; + hold_price = 0; + hold_number = 0; + ratio = 0; + inited = false; + init_times = 0; + price: { + pre_price: number; + current_price: number; + } = null; + + constructor(option: FutureOption) { + this.syncConfig(option); + } + + syncConfig(option: FutureOption) { + this.code = option.code; + this.alias = option.alias || ''; + this.hold_number = option.hold_number || 0; + this.hold_price = option.hold_price || 0; + } + + async updateConfig() { + if (this.inited) { + return; + } + this.init_times++; + const info = await provider.getBasicInfo(this.code); + if (info) { + this.inited = true; + if (!this.name) { + this.name = info.name; + } + this.ratio = info.ratio; + return; + } + if (this.init_times >= 3) { + this.inited = true; + return; + } + } + + async updatePrice() { + const info = await provider.getLastestPrice(this.code); + if (info) { + this.price = info; + this.name = info.name; + } + } +} + +export default class FutureHandler { + futures: FutureData[] = []; + + updateConfig(options: FutureOption[]) { + //logger.debug('future updateConfig'); + const newFutures = []; + for (const option of options) { + let future = this.futures.find((item) => item.code == option.code); + if (future == null) { + future = new FutureData(option); + } else { + future.syncConfig(option); + } + newFutures.push(future); + } + this.futures = newFutures; + } + + async updateData() { + if (this.futures.length <= 0) { + return; + } + await Promise.all(this.futures.map((item) => item.updateConfig())); + await Promise.all(this.futures.map((item) => item.updatePrice())); + } +} + +async function testGetBasicInfo() { + const data = await provider.getBasicInfo('ag2408'); + console.log(data); +} + +async function testGetPrice() { + const data = await provider.getLastestPrice('ag2408'); + console.log(data); +} diff --git a/src/logger.js b/src/logger.js new file mode 100644 index 0000000..5e7130d --- /dev/null +++ b/src/logger.js @@ -0,0 +1,59 @@ +"use strict"; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +exports.__esModule = true; +var util_1 = require("util"); +var Logger = /** @class */ (function () { + function Logger() { + } + /** + * @private + */ + Logger.prototype.prefix = function (loggerLevel) { + return (0, util_1.format)('[%s] [%s]', new Date().toLocaleString(), loggerLevel); + }; + Logger.prototype.fatal = function (msg) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + console.error(util_1.format.apply(void 0, __spreadArray(["".concat(this.prefix('fatal'), " ").concat(msg)], args, false))); + }; + Logger.prototype.error = function (msg) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + console.error(util_1.format.apply(void 0, __spreadArray(["".concat(this.prefix('error'), " ").concat(msg)], args, false))); + }; + Logger.prototype.warn = function (msg) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + console.warn(util_1.format.apply(void 0, __spreadArray(["".concat(this.prefix('warn'), " ").concat(msg)], args, false))); + }; + Logger.prototype.info = function (msg) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + console.log(util_1.format.apply(void 0, __spreadArray(["".concat(this.prefix('info'), " ").concat(msg)], args, false))); + }; + Logger.prototype.debug = function (msg) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + console.log(util_1.format.apply(void 0, __spreadArray(["".concat(this.prefix('debug'), " ").concat(msg)], args, false))); + }; + return Logger; +}()); +exports["default"] = new Logger(); diff --git a/src/render.ts b/src/render.ts index 3c9eb9b..38948d9 100644 --- a/src/render.ts +++ b/src/render.ts @@ -3,6 +3,8 @@ import * as vscode from 'vscode'; import Configuration from './configuration'; import Stock from './stock'; import { calcFixedNumber, keepDecimal } from './utils'; +import { FutureData } from './futures'; +import logger from './logger'; const stockHub = new Map(); @@ -65,3 +67,92 @@ export const render = (stocks: any) => { stockHub.get(code).barItem.tooltip = getTooltipText(stocks[code]); } }; + +let futureBars: vscode.StatusBarItem[] = []; + +export function stopAllRender() { + for (const [code, item] of stockHub) { + const barItem = item.barItem; + barItem.hide(); + barItem.dispose(); + } + stockHub.clear(); + + for (const item of futureBars) { + const barItem = item; + barItem.hide(); + barItem.dispose(); + } + futureBars = []; +} + +function syncFutureBarItem(futures: FutureData[]) { + const addBars = futures.length - futureBars.length; + for (let i = 0; i < -addBars; i++) { + const barItem = futureBars[futureBars.length - 1]; + barItem.dispose(); + futureBars.pop(); + } + + for (let i = 0; i < addBars; i++) { + const barItem = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Left, + ); + barItem.show(); + futureBars.push(barItem); + } +} + +function formatFuture(item: FutureData) { + const itemName = item.alias || item.name || ''; + let text = `${item.code} ---`; + const tooltip = new vscode.MarkdownString(`**${itemName}** ${item.code}\n\n`); + + try { + if (!item.price) { + return { + text, + tooltip, + }; + } + + const percent = item.price.pre_price + ? (item.price.current_price - item.price.pre_price) / item.price.pre_price + : 0; + + const percentStr = `${keepDecimal(percent * 100, 2)}%`; + const balance = Math.round( + (item.price.current_price - item.hold_price) * + item.hold_number * + item.ratio, + ); + tooltip.appendMarkdown( + `价格: **${item.price.current_price}** 涨跌: **${percentStr}**\n\n`, + ); + + const balanceStr = balance > 0 ? `+${balance}` : `${balance}`; + if (item.hold_price && item.hold_number && item.ratio) { + text = `${item.code} ${item.price.current_price} ${balanceStr}`; + tooltip.appendMarkdown(`盈亏: **${balanceStr}**`); + } else { + text = `${item.code} ${item.price.current_price} ${percentStr}`; + } + } catch (err) { + logger.error('%O', err); + } + + return { + text, + tooltip, + }; +} + +export function renderFutures(futures: FutureData[]) { + //logger.debug('renderFutures', futures); + syncFutureBarItem(futures); + for (const [index, barItem] of futureBars.entries()) { + const { text, tooltip } = formatFuture(futures[index]); + barItem.text = text; + barItem.tooltip = tooltip; + } +} diff --git a/src/timer.ts b/src/timer.ts index 0fd9547..c221423 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -2,14 +2,16 @@ import Configuration from './configuration'; class Timer { async await() { - while (true) { - await this.sleep(Configuration.getUpdateInterval()); + await this.sleep(Configuration.getUpdateInterval()); - // 工作日工作时间 9:00-11.30 13:00-15:00 - if (this.isWorkDay() && this.isWorkTime()) { - return true; - } - } + // while (true) { + // await this.sleep(Configuration.getUpdateInterval()); + + // //工作日工作时间 9:00-11.30 13:00-15:00 + // if (this.isWorkDay() && this.isWorkTime()) { + // return true; + // } + // } } /** diff --git a/test/api.http b/test/api.http new file mode 100644 index 0000000..2f30c28 --- /dev/null +++ b/test/api.http @@ -0,0 +1,24 @@ +### test futures price +GET https://finance.pae.baidu.com/selfselect/getstockquotation?all=1&isIndex=false&isBk=false&isBlock=false&isStock=false&isFutures=true&isForeign=false&code=AU888&stockType=ab&newFormat=1&market_type=ab&group=quotation_futures_minute&finClientType=pc + +### test futures price +GET https://finance.pae.baidu.com/selfselect/getstockquotation?all=3&isIndex=false&isStock=false&isFutures=true&code=AU888&stockType=ab&newFormat=1&finClientType=pc&market_type=ab&group=quotation_futures_minute + +#### +POST https://finance.pae.baidu.com/selfselect/gettrenddata?all=1 +Content-Type: application/x-www-form-urlencoded + +stock=%5B%7B%22code%22%3A%22CU2409%22%2C%22market%22%3A%22ab%22%2C%22type%22%3A%22futures%22%7D%2C%7B%22code%22%3A%22SA409%22%2C%22market%22%3A%22ab%22%2C%22type%22%3A%22futures%22%7D%2C%7B%22code%22%3A%22FG409%22%2C%22market%22%3A%22ab%22%2C%22type%22%3A%22futures%22%7D%2C%7B%22code%22%3A%22JM888%22%2C%22market%22%3A%22ab%22%2C%22type%22%3A%22futures%22%7D%2C%7B%22code%22%3A%22M9888%22%2C%22market%22%3A%22ab%22%2C%22type%22%3A%22futures%22%7D%2C%7B%22code%22%3A%22SM888%22%2C%22market%22%3A%22ab%22%2C%22type%22%3A%22futures%22%7D%5D&finClientType=pc + + +#### +GET https://d.10jqka.com.cn/v6/time/qh_SA2409/last.js +Referer: https://goodsfu.10jqka.com.cn/ +Content-Type: application/json + + +#### 同花顺 +GET https://fupage.10jqka.com.cn/futgwapi/api/market/basis/v2/trend_data?contract=ag2408&spot_index_id=-1 + +### +GET https://fupage.10jqka.com.cn/futgwapi/api/f10/contract/v1/info?code=ag2408 diff --git a/tsconfig.json b/tsconfig.json index 2c290eb..f6e9fc9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,8 @@ "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": false, + "typeRoots": ["./types", "./node_modules/@types"] }, "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] } diff --git a/types/stock-bar.d.ts b/types/stock-bar.d.ts index 09e8b14..e6d0a21 100644 --- a/types/stock-bar.d.ts +++ b/types/stock-bar.d.ts @@ -4,8 +4,16 @@ declare module 'stock-bar' { alias: string; } type color = string; - - export type StockOptions = (string|StockOption)[]; + + export interface FutureOption { + code: string; + alias?: string; + hold_price?: number; //持仓成本 + hold_number?: number; //持仓, 1代表多,-1代表空 + } + + export type StockOptions = (string | StockOption)[]; + export type FutureOptions = FutureOption[]; export type UpdateIntervalOption = number; export type RiseColorOption = color; export type FallColorOption = color; From 149de774c52167e366daf2da557827e50c25b84d Mon Sep 17 00:00:00 2001 From: weibo Date: Sun, 16 Jun 2024 23:25:54 +0800 Subject: [PATCH 2/5] * fix bug * use manual start/stop to show/hide --- src/extension.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index fe10547..d443471 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,7 +3,6 @@ import logger from './logger'; import Configuration from './configuration'; import { sinaStockProvider } from './provider'; import { render, renderFutures, stopAllRender } from './render'; -import timer from './timer'; import Stock from './stock'; import FutureHandler from './futures'; import { clearInterval } from 'timers'; @@ -26,6 +25,7 @@ let timer = null; let stocks: Stock[]; function restart() { + console.log('restart'); const interval = Configuration.getUpdateInterval(); if (timer) { clearInterval(timer); @@ -66,6 +66,7 @@ async function ticker() { } function stop() { + console.log('stop'); if (timer) { clearInterval(timer); timer = null; @@ -74,6 +75,7 @@ function stop() { } export function activate(context: vscode.ExtensionContext) { + console.log('activivate'); stocks = loadChoiceStocks(); const startCmd = vscode.commands.registerCommand('stockbar.start', restart); From 2ba15999b8c5f0a16a0ef0b40907d4c608328179 Mon Sep 17 00:00:00 2001 From: weibo Date: Sun, 16 Jun 2024 23:27:49 +0800 Subject: [PATCH 3/5] remove unused log --- src/extension.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index d443471..fbc9641 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -25,7 +25,7 @@ let timer = null; let stocks: Stock[]; function restart() { - console.log('restart'); + //console.log('restart'); const interval = Configuration.getUpdateInterval(); if (timer) { clearInterval(timer); @@ -66,7 +66,7 @@ async function ticker() { } function stop() { - console.log('stop'); + //console.log('stop'); if (timer) { clearInterval(timer); timer = null; @@ -75,7 +75,7 @@ function stop() { } export function activate(context: vscode.ExtensionContext) { - console.log('activivate'); + //console.log('activivate'); stocks = loadChoiceStocks(); const startCmd = vscode.commands.registerCommand('stockbar.start', restart); From ae111146376c1ece586b05ef60358c7d40d24496 Mon Sep 17 00:00:00 2001 From: weibo Date: Sun, 16 Jun 2024 23:44:36 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++++ package-lock.json | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4806f8f..645b974 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,13 @@ VScode 插件 | A 股 | 港股 | 实时股票数据 | 状态栏实时更新 插件已开源,开源地址:[Github](https://github.com/Chef5/stock-bar),欢迎点星星 ⭐️、提 issue 或者 pr +## 启动 + +(ctrl + shift + P) 后运行命令: + +- stock watch start 开始显示 +- stock watch stop 关闭显示 + ## 插件配置 修改用户配置,添加你所需要监控的股票代码 @@ -101,3 +108,4 @@ VScode 插件 | A 股 | 港股 | 实时股票数据 | 状态栏实时更新 > 股票数据来源: > > - 新浪 +> - 同花顺 diff --git a/package-lock.json b/package-lock.json index 320c68f..e9a33e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "stock-bar", - "version": "1.0.4", + "version": "1.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "stock-bar", - "version": "1.0.4", + "version": "1.0.6", "license": "MIT", "dependencies": { "axios": "^0.27.2" From 0b475964524ff5bb8f6811de7540eb3a2cbd9b2a Mon Sep 17 00:00:00 2001 From: weibo Date: Tue, 18 Jun 2024 01:02:56 +0800 Subject: [PATCH 5/5] fix future api --- src/futures.ts | 61 ++++++++++++++++++++++++++++++++++++++++---------- src/render.ts | 1 + test/api.http | 13 +++++++++-- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/futures.ts b/src/futures.ts index 6ef8f78..6592c65 100644 --- a/src/futures.ts +++ b/src/futures.ts @@ -16,13 +16,13 @@ class Provider { async getBasicInfo(code: string) { try { const url = `https://fupage.10jqka.com.cn/futgwapi/api/f10/contract/v1/info?code=${code}`; - const ret = await this.instance.get(url); - const data = ret.data; - if (data.code) { - return null; - } - const name = data.data?.name || ''; - const tradeUnit = (data.data?.trade_unit || '').trim(); + const results = await Promise.all([ + this.instance.get(`https://fupage.10jqka.com.cn/futgwapi/api/f10/contract/v1/info?code=${code.toUpperCase()}`), + this.instance.get(`https://fupage.10jqka.com.cn/futgwapi/api/f10/contract/v1/info?code=${code.toLowerCase()}`) + ]) + const data = results[0].data.data || results[1].data.data || {}; + const name = data.name || ''; + const tradeUnit = (data.trade_unit || '').trim(); let ratio = 0; if (tradeUnit) { const match = tradeUnit.match(/^\d*/); @@ -41,7 +41,7 @@ class Provider { } } - async getLastestPrice(code: string) { + async getLatestPrice(code: string) { try { const headers = { Referer: 'https://goodsfu.10jqka.com.cn/', @@ -82,7 +82,41 @@ class Provider { } } -const provider = new Provider(); +class ProviderSina extends Provider { + async getLatestPrice(code: string) { + try { + const headers = { + Referer: 'https://finance.sina.com.cn', + 'Content-Type': 'application/json', + }; + const url = `https://hq.sinajs.cn?list=nf_${code}`; + let ret = (await this.instance.get(url, { headers, responseType:'arraybuffer'})).data; + ret = new TextDecoder('GBK').decode(ret); + const match = ret.match(/\"(.*)\"/); + if (!match) { + return null; + } + const arr = match[1].split(','); + if (arr.length <= 5) { return null; } + const name = arr[0]; + const open = parseFloat(arr[2]); + const high = parseFloat(arr[3]); + const low = parseFloat(arr[4]); + const current_price = parseFloat(arr[8]); + const pre_price = parseFloat(arr[10]); + return { + name, + pre_price, + current_price, + }; + } catch (err: unknown) { + logger.error('getLastestPrice error %O', err); + return null; + } + } +} + +const provider = new ProviderSina(); export class FutureData { code: string; @@ -115,6 +149,7 @@ export class FutureData { } this.init_times++; const info = await provider.getBasicInfo(this.code); + console.log('updateConfig', info); if (info) { this.inited = true; if (!this.name) { @@ -130,7 +165,7 @@ export class FutureData { } async updatePrice() { - const info = await provider.getLastestPrice(this.code); + const info = await provider.getLatestPrice(this.code); if (info) { this.price = info; this.name = info.name; @@ -166,11 +201,13 @@ export default class FutureHandler { } async function testGetBasicInfo() { - const data = await provider.getBasicInfo('ag2408'); + const data = await provider.getBasicInfo('cu2409'); console.log(data); } async function testGetPrice() { - const data = await provider.getLastestPrice('ag2408'); + const data = await provider.getLatestPrice('CU2409'); console.log(data); } + +testGetBasicInfo(); \ No newline at end of file diff --git a/src/render.ts b/src/render.ts index 38948d9..cb27d63 100644 --- a/src/render.ts +++ b/src/render.ts @@ -131,6 +131,7 @@ function formatFuture(item: FutureData) { ); const balanceStr = balance > 0 ? `+${balance}` : `${balance}`; + console.log(item); if (item.hold_price && item.hold_number && item.ratio) { text = `${item.code} ${item.price.current_price} ${balanceStr}`; tooltip.appendMarkdown(`盈亏: **${balanceStr}**`); diff --git a/test/api.http b/test/api.http index 2f30c28..e80985c 100644 --- a/test/api.http +++ b/test/api.http @@ -12,7 +12,7 @@ stock=%5B%7B%22code%22%3A%22CU2409%22%2C%22market%22%3A%22ab%22%2C%22type%22%3A% #### -GET https://d.10jqka.com.cn/v6/time/qh_SA2409/last.js +GET https://d.10jqka.com.cn/v6/time/qh_FG2409/last.js Referer: https://goodsfu.10jqka.com.cn/ Content-Type: application/json @@ -21,4 +21,13 @@ Content-Type: application/json GET https://fupage.10jqka.com.cn/futgwapi/api/market/basis/v2/trend_data?contract=ag2408&spot_index_id=-1 ### -GET https://fupage.10jqka.com.cn/futgwapi/api/f10/contract/v1/info?code=ag2408 +GET https://fupage.10jqka.com.cn/futgwapi/api/f10/contract/v1/info?code=sa9999 + +### +GET https://api.jijinhao.com/sQuoteCenter/realTime.htm?code=JO_165681&_=1718636839611 +Referer: https://www.cngold.org/ + +### +GET https://hq.sinajs.cn?list=nf_CU2408 +Referer: https://finance.sina.com.cn +Content-Type: application/json \ No newline at end of file