├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── jestconfig.json ├── package-lock.json ├── package.json ├── src ├── index.ts ├── stocks │ ├── base │ │ ├── index.ts │ │ ├── transforms │ │ │ ├── api-code.ts │ │ │ ├── common-code.ts │ │ │ └── stock.ts │ │ └── utils │ │ │ └── constant.ts │ ├── index.ts │ ├── netease │ │ ├── index.ts │ │ ├── transforms │ │ │ ├── api-code.ts │ │ │ ├── common-code.ts │ │ │ └── stock.ts │ │ └── utils │ │ │ └── constant.ts │ ├── sina │ │ ├── index.ts │ │ ├── transforms │ │ │ ├── api-code.ts │ │ │ ├── common-code.ts │ │ │ └── stock.ts │ │ └── utils │ │ │ └── constant.ts │ ├── tencent │ │ ├── index.ts │ │ ├── transforms │ │ │ ├── api-code.ts │ │ │ ├── common-code.ts │ │ │ └── stock.ts │ │ └── utils │ │ │ └── constant.ts │ └── xueqiu │ │ ├── index.ts │ │ ├── transforms │ │ ├── api-code.ts │ │ ├── common-code.ts │ │ └── stock.ts │ │ └── utils │ │ └── constant.ts ├── types │ ├── index.d.ts │ ├── stocks │ │ ├── index.d.ts │ │ └── transforms │ │ │ ├── api-code.d.ts │ │ │ ├── common-code.d.ts │ │ │ └── stock.d.ts │ └── utils │ │ ├── dictionary.d.ts │ │ └── stock.d.ts └── utils │ ├── constant.ts │ ├── fetch.ts │ └── iconv.ts ├── tea.yaml ├── test └── stocks │ ├── base │ ├── index.test.ts │ └── transforms │ │ ├── api-code.test.ts │ │ ├── common-code.test.ts │ │ └── stock.test.ts │ ├── netease │ ├── index.test.ts │ └── transforms │ │ ├── api-code.test.ts │ │ ├── common-code.test.ts │ │ └── stock.test.ts │ ├── sina │ ├── index.test.ts │ └── transforms │ │ ├── api-code.test.ts │ │ ├── common-code.test.ts │ │ └── stock.test.ts │ ├── tencent │ ├── index.test.ts │ └── transforms │ │ ├── api-code.test.ts │ │ ├── common-code.test.ts │ │ └── stock.test.ts │ └── xueqiu │ ├── index.test.ts │ └── transforms │ ├── api-code.test.ts │ ├── common-code.test.ts │ └── stock.test.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .DS_Store 3 | node_modules/ 4 | coverage/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 zhangxiangliang 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

股票数据小助手

2 | 3 |

4 | Coverage Status 5 | Downloads 6 | Version 7 | License 8 | License 9 |

10 | 11 | ## 简介 12 | 13 | 一款聚焦在 `股票实时数据` 和 `周边相关服务` 的接口小助手。 14 | 15 | ## 股票数据 16 | 17 | ### 已实现 18 | 19 | | 名称 | 接口名 | 搜索股票代码 | 获取股票实时数据 | 获取股票组实时数据 | 官网 | 20 | | :------: | :-----: | :---------------------: | :-------------------------: | :---------------------------: | :------------------------------: | 21 | | 网易财经 | netease | [已实现](#搜索股票代码) | [已实现](#获取股票实时数据) | [已实现](#获取股票组实时数据) | [传送门](https://money.163.com/) | 22 | | 腾讯股票 | tencent | [已实现](#搜索股票代码) | [已实现](#获取股票实时数据) | [已实现](#获取股票组实时数据) | [传送门](http://gu.qq.com/) | 23 | 24 | ### 待实现 25 | 26 | | 名称 | 接口名 | 搜索股票代码 | 获取股票实时数据 | 获取股票组实时数据 | 官网 | 27 | | :------: | :----: | :---------------------: | :-------------------------: | :---------------------------: | ----------------------------------------- | 28 | | 雪球 | xueqiu | [已实现](#搜索股票代码) | [待实现](#获取股票实时数据) | [待实现](#获取股票组实时数据) | [传送门](https://xueqiu.com/) | 29 | | 新浪股票 | sina | [已实现](#搜索股票代码) | [待实现](#获取股票实时数据) | [待实现](#获取股票组实时数据) | [传送门](https://finance.netease.com.cn/) | 30 | 31 | ## 安装 32 | 33 | ```shell 34 | npm install stock-api 35 | ``` 36 | 37 | ## 使用 38 | 39 | ### 接口概览 40 | 41 | - [选择数据源](#选择数据源) 42 | - [搜索股票代码](#搜索股票代码) 43 | - [获取股票实时数据](#获取股票实时数据) 44 | - [获取股票组实时数据](#获取股票组实时数据) 45 | 46 | ### 股票代码 47 | 48 | 由于每个交易所数据规则不一样,为了能统一规范对代码定义了规则 `交易所+股票代码`。 49 | 50 | | 交易所 | 代号 | 实例 | 51 | | ---------- | ---- | -------- | 52 | | 上海交易所 | SH | SH000001 | 53 | | 深圳交易所 | SZ | SZ399001 | 54 | | 香港交易所 | HK | HKHSI | 55 | | 美国交易所 | US | USDJI | 56 | 57 | ### 选择数据源 58 | 59 | ##### 可选导入 60 | 61 | ```typescript 62 | import { stocks } from "stock-api"; 63 | 64 | // 数据源 65 | const netease = stocks.netease; 66 | const tencent = stocks.tencent; 67 | ``` 68 | 69 | ### 搜索股票代码 70 | 71 | ##### 示例 72 | 73 | ```typescript 74 | import { stocks } from "stock-api"; 75 | 76 | // 获取股票组实时数据 77 | stocks.netease.searchStocks(["510500"]).then(console.log); 78 | ``` 79 | 80 | ##### 输出 81 | 82 | ```typescript 83 | [ 84 | { 85 | code: "SH510500", 86 | name: "500ETF", 87 | percent: 0.028383, 88 | now: 7.174, 89 | low: 6.93, 90 | high: 7.184, 91 | yesterday: 6.976, 92 | }, 93 | ]; 94 | ``` 95 | 96 | ### 获取股票实时数据 97 | 98 | ##### 示例 99 | 100 | ```typescript 101 | import { stocks } from "stock-api"; 102 | 103 | // 获取股票实时数据 104 | stocks.netease.getStock("SH510500").then(console.log); 105 | ``` 106 | 107 | ##### 输出 108 | 109 | ```typescript 110 | { 111 | code: 'SH510500', 112 | name: '500ETF', 113 | percent: 0.028383, 114 | now: 7.174, 115 | low: 6.93, 116 | high: 7.184, 117 | yesterday: 6.976 118 | } 119 | ``` 120 | 121 | ### 获取股票组实时数据 122 | 123 | ##### 示例 124 | 125 | ```typescript 126 | import { stocks } from "stock-api"; 127 | 128 | // 获取股票组实时数据 129 | stocks.netease.getStocks(["SH510500"]).then(console.log); 130 | ``` 131 | 132 | ##### 输出 133 | 134 | ```typescript 135 | [ 136 | { 137 | code: "SH510500", 138 | name: "500ETF", 139 | percent: 0.028383, 140 | now: 7.174, 141 | low: 6.93, 142 | high: 7.184, 143 | yesterday: 6.976, 144 | }, 145 | ]; 146 | ``` 147 | 148 | ## 一起成长 149 | 150 | - 在困惑的城市里总少不了并肩同行的 伙伴 让我们一起成长。 151 | - 如果您想激励小二可以到 Github 给个 小星星。 152 | -------------------------------------------------------------------------------- /jestconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "testTimeout": 15000, 4 | "transform": { 5 | "^.+\\.(t|j)sx?$": "ts-jest" 6 | }, 7 | "testRegex": "(/test/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 8 | "moduleFileExtensions": [ 9 | "ts", 10 | "tsx", 11 | "js", 12 | "jsx", 13 | "json", 14 | "node" 15 | ], 16 | "moduleDirectories": ["node_modules", "src"], 17 | "moduleNameMapper": { 18 | "^@stocks/(.*)$": "/src/stocks/$1", 19 | "^@utils/(.*)$": "/src/utils/$1", 20 | "^types/(.*)$": "/types/$1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stock-api", 3 | "version": "2.0.8", 4 | "description": "免费股票数据分析相关接口", 5 | "repository": "https://github.com/zhangxiangliang/stock-api", 6 | "author": "zhangxiangliang", 7 | "keywords": [ 8 | "股票", 9 | "数据分析", 10 | "接口" 11 | ], 12 | "main": "dist/index.js", 13 | "types": "dist/index.d.ts", 14 | "scripts": { 15 | "test": "jest --config jestconfig.json --coverage", 16 | "build": "tsc" 17 | }, 18 | "license": "MIT", 19 | "files": [ 20 | "dist/**/*" 21 | ], 22 | "devDependencies": { 23 | "@types/jest": "^29.5.12", 24 | "@types/lodash": "^4.14.202", 25 | "@types/superagent": "^8.1.4", 26 | "jest": "^29.7.0", 27 | "ts-jest": "^29.1.2", 28 | "typescript": "^5.3.3" 29 | }, 30 | "dependencies": { 31 | "iconv-lite": "^0.6.3", 32 | "lodash": "^4.17.21", 33 | "superagent": "^8.1.2" 34 | } 35 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import stocks from "./stocks/index"; 3 | 4 | export { stocks }; 5 | export default { stocks }; 6 | -------------------------------------------------------------------------------- /src/stocks/base/index.ts: -------------------------------------------------------------------------------- 1 | // Utils 2 | import { 3 | ERROR_UNDEFINED_GET_STOCK, 4 | ERROR_UNDEFINED_GET_STOCKS, 5 | ERROR_UNDEFINED_SEARCH_STOCK, 6 | } from "../../utils/constant"; 7 | 8 | // Types 9 | import Stock from "../../types/utils/stock"; 10 | import StockApi from "../../types/stocks/index"; 11 | 12 | /** 13 | * 基础股票代码接口 14 | */ 15 | const Base: StockApi = { 16 | /** 17 | * 获取股票数据 18 | * @param code 股票代码 19 | */ 20 | async getStock(code: string): Promise { 21 | throw new Error(ERROR_UNDEFINED_GET_STOCK); 22 | }, 23 | 24 | /** 25 | * 获取股票数据组 26 | * @param codes 股票代码组 27 | */ 28 | async getStocks(codes: string[]): Promise { 29 | throw new Error(ERROR_UNDEFINED_GET_STOCKS); 30 | }, 31 | 32 | /** 33 | * 搜索股票代码 34 | * @param key 关键字 35 | */ 36 | async searchStocks(key: string): Promise { 37 | throw new Error(ERROR_UNDEFINED_SEARCH_STOCK); 38 | }, 39 | }; 40 | 41 | export default Base; 42 | -------------------------------------------------------------------------------- /src/stocks/base/transforms/api-code.ts: -------------------------------------------------------------------------------- 1 | // Utils 2 | import { 3 | COMMON_SH, 4 | COMMON_SZ, 5 | COMMON_HK, 6 | COMMON_US, 7 | } from "../../../stocks/base/utils/constant"; 8 | import { 9 | ERROR_UNDEFINED_API_CODE, 10 | ERROR_UNDEFINED_SZ_API_CODE, 11 | ERROR_UNDEFINED_SH_API_CODE, 12 | ERROR_UNDEFINED_HK_API_CODE, 13 | ERROR_UNDEFINED_US_API_CODE, 14 | ERROR_API_CODE, 15 | } from "../../../utils/constant"; 16 | 17 | // Types 18 | import ApiCodeTransform from "../../../types/stocks/transforms/api-code"; 19 | 20 | /** 21 | * 【基础】股票代码转换统一代码 22 | */ 23 | class BaseApiCodeTransform implements ApiCodeTransform { 24 | /** 25 | * 股票代码转换统一代码 26 | * @param code 股票代码 27 | */ 28 | public transform(code: string): string { 29 | if (code.indexOf(COMMON_SH) === 0) { 30 | return this.SHTransform(code); 31 | } 32 | 33 | if (code.indexOf(COMMON_SZ) === 0) { 34 | return this.SZTransform(code); 35 | } 36 | 37 | if (code.indexOf(COMMON_HK) === 0) { 38 | return this.HKTransform(code); 39 | } 40 | 41 | if (code.indexOf(COMMON_US) === 0) { 42 | return this.USTransform(code); 43 | } 44 | 45 | throw new Error(ERROR_API_CODE); 46 | } 47 | 48 | /** 49 | * 股票代码组转换统一代码组 50 | * @param codes 股票代码组 51 | */ 52 | public transforms(codes: string[]): string[] { 53 | return codes.map((code) => this.transform(code)); 54 | } 55 | 56 | /** 57 | * 深交所股票代码转换统一代码 58 | * @param code 股票代码 59 | */ 60 | public SZTransform(code: string): string { 61 | throw new Error(ERROR_UNDEFINED_SZ_API_CODE); 62 | } 63 | 64 | /** 65 | * 上交所股票代码转换统一代码 66 | * @param code 股票代码 67 | */ 68 | public SHTransform(code: string): string { 69 | throw new Error(ERROR_UNDEFINED_SH_API_CODE); 70 | } 71 | 72 | /** 73 | * 港交所股票代码转换统一代码 74 | * @param code 股票代码 75 | */ 76 | public HKTransform(code: string): string { 77 | throw new Error(ERROR_UNDEFINED_HK_API_CODE); 78 | } 79 | 80 | /** 81 | * 美交所股票代码转换统一代码 82 | * @param code 股票代码 83 | */ 84 | public USTransform(code: string): string { 85 | throw new Error(ERROR_UNDEFINED_US_API_CODE); 86 | } 87 | } 88 | 89 | export default BaseApiCodeTransform; 90 | -------------------------------------------------------------------------------- /src/stocks/base/transforms/common-code.ts: -------------------------------------------------------------------------------- 1 | // Utils 2 | import { 3 | ERROR_COMMON_CODE, 4 | ERROR_UNDEFINED_SZ_COMMON_CODE, 5 | ERROR_UNDEFINED_SH_COMMON_CODE, 6 | ERROR_UNDEFINED_HK_COMMON_CODE, 7 | ERROR_UNDEFINED_US_COMMON_CODE, 8 | } from "../../../utils/constant"; 9 | 10 | // Types 11 | import CommonCodeTransform from "../../../types/stocks/transforms/common-code"; 12 | import { COMMON_SH, COMMON_SZ, COMMON_HK, COMMON_US } from "../utils/constant"; 13 | 14 | /** 15 | * 【基础】统一代码转换股票代码 16 | */ 17 | class BaseCommonCodeTransform implements CommonCodeTransform { 18 | /** 19 | * 交易所统一代码转换股票代码 20 | * @param code 统一代码 21 | */ 22 | public transform(code: string): string { 23 | if (code.indexOf(COMMON_SH) === 0) { 24 | return this.SHTransform(code); 25 | } 26 | 27 | if (code.indexOf(COMMON_SZ) === 0) { 28 | return this.SZTransform(code); 29 | } 30 | 31 | if (code.indexOf(COMMON_HK) === 0) { 32 | return this.HKTransform(code); 33 | } 34 | 35 | if (code.indexOf(COMMON_US) === 0) { 36 | return this.USTransform(code); 37 | } 38 | 39 | throw new Error(ERROR_COMMON_CODE); 40 | } 41 | 42 | /** 43 | * 交易所统一代码组转股票代码组 44 | * @param codes 统一代码组 45 | */ 46 | public transforms(codes: string[]): string[] { 47 | return codes.map((code) => this.transform(code)); 48 | } 49 | 50 | /** 51 | * 深交所统一代码转换股票代码 52 | * @param code 统一代码 53 | */ 54 | public SZTransform(code: string): string { 55 | throw new Error(ERROR_UNDEFINED_SZ_COMMON_CODE); 56 | } 57 | 58 | /** 59 | * 上交所统一代码转换股票代码 60 | * @param code 统一代码 61 | */ 62 | public SHTransform(code: string): string { 63 | throw new Error(ERROR_UNDEFINED_SH_COMMON_CODE); 64 | } 65 | 66 | /** 67 | * 港交所统一代码转换股票代码 68 | * @param code 统一代码 69 | */ 70 | public HKTransform(code: string): string { 71 | throw new Error(ERROR_UNDEFINED_HK_COMMON_CODE); 72 | } 73 | 74 | /** 75 | * 美交所统一代码转换股票代码 76 | * @param code 统一代码 77 | */ 78 | public USTransform(code: string): string { 79 | throw new Error(ERROR_UNDEFINED_US_COMMON_CODE); 80 | } 81 | } 82 | 83 | export default BaseCommonCodeTransform; 84 | -------------------------------------------------------------------------------- /src/stocks/base/transforms/stock.ts: -------------------------------------------------------------------------------- 1 | // Utils 2 | import { 3 | ERROR_UNDEFINED_GET_CODE, 4 | ERROR_UNDEFINED_GET_NAME, 5 | ERROR_UNDEFINED_GET_NOW, 6 | ERROR_UNDEFINED_GET_LOW, 7 | ERROR_UNDEFINED_GET_HIGH, 8 | ERROR_UNDEFINED_GET_YESTERDAY, 9 | ERROR_UNDEFINED_GET_PERCENT, 10 | ERROR_UNDEFINED_GET_STOCK, 11 | } from "../../../utils/constant"; 12 | 13 | // Types 14 | import Stock from "../../../types/utils/stock"; 15 | 16 | /** 17 | * 基础股票数据解析 18 | */ 19 | class BaseStockTransform { 20 | /** 21 | * 获取代码 22 | */ 23 | getCode(): string { 24 | throw new Error(ERROR_UNDEFINED_GET_CODE); 25 | } 26 | 27 | /** 28 | * 获取名称 29 | */ 30 | getName(): string { 31 | throw new Error(ERROR_UNDEFINED_GET_NAME); 32 | } 33 | 34 | /** 35 | * 获取现价 36 | */ 37 | getNow(): number { 38 | throw new Error(ERROR_UNDEFINED_GET_NOW); 39 | } 40 | 41 | /** 42 | * 获取最低价 43 | */ 44 | getLow(): number { 45 | throw new Error(ERROR_UNDEFINED_GET_LOW); 46 | } 47 | 48 | /** 49 | * 获取最高价 50 | */ 51 | getHigh(): number { 52 | throw new Error(ERROR_UNDEFINED_GET_HIGH); 53 | } 54 | 55 | /** 56 | * 获取昨日收盘价 57 | */ 58 | getYesterday(): number { 59 | throw new Error(ERROR_UNDEFINED_GET_YESTERDAY); 60 | } 61 | 62 | /** 63 | * 获取涨跌 64 | */ 65 | getPercent(): number { 66 | throw new Error(ERROR_UNDEFINED_GET_PERCENT); 67 | } 68 | 69 | /** 70 | * 获取股票数据 71 | */ 72 | getStock(): Stock { 73 | throw new Error(ERROR_UNDEFINED_GET_STOCK); 74 | } 75 | } 76 | 77 | export default BaseStockTransform; 78 | -------------------------------------------------------------------------------- /src/stocks/base/utils/constant.ts: -------------------------------------------------------------------------------- 1 | // 默认交易所常量 2 | export const COMMON_SZ = "SZ"; // 深交所相关常量 3 | export const COMMON_SH = "SH"; // 上交所相关常量 4 | export const COMMON_HK = "HK"; // 港交所相关常量 5 | export const COMMON_US = "US"; // 美交所相关常量 6 | -------------------------------------------------------------------------------- /src/stocks/index.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import base from "./base"; 3 | import sina from "./sina"; 4 | import xueqiu from "./xueqiu"; 5 | import netease from "./netease"; 6 | import tencent from "./tencent"; 7 | 8 | export { base, sina, xueqiu, netease, tencent }; 9 | 10 | export default { 11 | base, 12 | sina, 13 | xueqiu, 14 | netease, 15 | tencent, 16 | }; 17 | -------------------------------------------------------------------------------- /src/stocks/netease/index.ts: -------------------------------------------------------------------------------- 1 | // NPM 2 | import { uniq } from "lodash"; 3 | 4 | // Stocks 5 | import NeteaseStockTransform from "../../stocks/netease/transforms/stock"; 6 | import NeteaseCommonCodeTransform from "../../stocks/netease/transforms/common-code"; 7 | 8 | // Utils 9 | import fetch from "../../utils/fetch"; 10 | import { DEFAULT_STOCK } from "../../utils/constant"; 11 | import { COMMON_SZ, COMMON_SH } from "../../stocks/base/utils/constant"; 12 | 13 | // Types 14 | import Stock from "../../types/utils/stock"; 15 | import StockApi from "../../types/stocks/index"; 16 | import Dictionary from "../../types/utils/dictionary"; 17 | 18 | /** 19 | * 网易股票代码接口 20 | */ 21 | const Netease: StockApi = { 22 | /** 23 | * 获取股票数据 24 | * @param code 股票代码 25 | */ 26 | async getStock(code: string): Promise { 27 | const transform = new NeteaseCommonCodeTransform().transform(code); 28 | 29 | const url = `https://api.money.126.net/data/feed/${transform},money.api?callback=topstock`; 30 | const res = await fetch.get(url); 31 | 32 | const items = JSON.parse( 33 | res.body 34 | .toString() 35 | .replace(/\(|\)|;|(topstock)|\s/g, "") 36 | .replace("{{", "{") 37 | .replace("}}}", "}}") 38 | ); 39 | const params = items[transform]; 40 | 41 | if (!params) { 42 | return { ...DEFAULT_STOCK, code }; 43 | } 44 | 45 | const data = new NeteaseStockTransform(code, params); 46 | return data.getStock(); 47 | }, 48 | 49 | /** 50 | * 获取股票数据组 51 | * @param codes 股票代码组 52 | */ 53 | async getStocks(codes: string[]): Promise { 54 | codes = uniq(codes.filter((i) => i !== "")); 55 | 56 | // 无股票时返回空数组 57 | if (codes.length === 0) { 58 | return []; 59 | } 60 | 61 | const transforms = new NeteaseCommonCodeTransform().transforms(codes); 62 | 63 | // 数据获取 64 | const url = `https://api.money.126.net/data/feed/${transforms.join( 65 | "," 66 | )},money.api?callback=topstock`; 67 | const res = await fetch.get(url); 68 | 69 | // 解析数据 70 | const items = JSON.parse( 71 | res.body 72 | .toString() 73 | .replace(/\(|\)|;|(topstock)|\s/g, "") 74 | .replace("{{", "{") 75 | .replace("}}}", "}}") 76 | ); 77 | return codes.map((code) => { 78 | const transform = new NeteaseCommonCodeTransform().transform(code); 79 | const params = items[transform]; 80 | 81 | if (!params) { 82 | return { ...DEFAULT_STOCK, code }; 83 | } 84 | 85 | const data = new NeteaseStockTransform(code, params); 86 | 87 | return data.getStock(); 88 | }); 89 | }, 90 | 91 | /** 92 | * 搜索股票代码 93 | * @param key 关键字 94 | */ 95 | async searchStocks(key: string): Promise { 96 | // 数据获取 97 | const url = `https://quotes.money.163.com/stocksearch/json.do?type=&count=5&word=${encodeURIComponent( 98 | key 99 | )}&callback=topstock`; 100 | const res = await fetch.get(url); 101 | 102 | // 解析数据 103 | const row = res.body 104 | .toString() 105 | .replace(/\(|\)|;|(topstock)|\s/g, "") 106 | .replace("{{", "{") 107 | .replace("}}}", "}}"); 108 | const items: Dictionary[] = JSON.parse(row); 109 | 110 | const codes: string[] = items.map((i) => { 111 | if (i.tag.includes(COMMON_SZ) || i.type.includes(COMMON_SZ)) { 112 | return COMMON_SZ + i.symbol; 113 | } 114 | 115 | if (i.tag.includes(COMMON_SH) || i.type.includes(COMMON_SH)) { 116 | return COMMON_SH + i.symbol; 117 | } 118 | 119 | return ""; 120 | }); 121 | 122 | return await Netease.getStocks(uniq(codes.filter((i) => i !== ""))); 123 | }, 124 | }; 125 | 126 | export default Netease; 127 | -------------------------------------------------------------------------------- /src/stocks/netease/transforms/api-code.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseApiCodeTransform from "../../../stocks/base/transforms/api-code"; 3 | 4 | // Utils 5 | import { ERROR_API_CODE } from "../../../utils/constant"; 6 | import { 7 | COMMON_SH, 8 | COMMON_SZ, 9 | COMMON_HK, 10 | COMMON_US, 11 | } from "../../../stocks/base/utils/constant"; 12 | import { 13 | NETEASE_SZ, 14 | NETEASE_SH, 15 | NETEASE_HK, 16 | NETEASE_US, 17 | } from "../../../stocks/netease/utils/constant"; 18 | 19 | /** 20 | * 网易股票代码转统一代码 21 | */ 22 | class NeteaseApiCodeTransform extends BaseApiCodeTransform { 23 | /** 24 | * 股票代码转统一代码 25 | * @param code 股票代码 26 | */ 27 | public transform(code: string): string { 28 | if (code.indexOf(NETEASE_SZ) === 0) { 29 | return this.SZTransform(code); 30 | } 31 | 32 | if (code.indexOf(NETEASE_SH) === 0) { 33 | return this.SHTransform(code); 34 | } 35 | 36 | if (code.indexOf(NETEASE_HK) === 0) { 37 | return this.HKTransform(code); 38 | } 39 | 40 | if (code.indexOf(NETEASE_US) === 0) { 41 | return this.USTransform(code); 42 | } 43 | 44 | throw new Error(ERROR_API_CODE); 45 | } 46 | 47 | /** 48 | * 股票代码组转统一代码组 49 | * @param codes 股票代码组 50 | */ 51 | public transforms(codes: string[]): string[] { 52 | return codes.map((code) => this.transform(code)); 53 | } 54 | 55 | /** 56 | * 深交所股票代码转统一代码 57 | * @param code 股票代码 58 | */ 59 | public SZTransform(code: string): string { 60 | if (code.indexOf(NETEASE_SZ) !== 0) { 61 | throw new Error(ERROR_API_CODE); 62 | } 63 | 64 | return COMMON_SZ + code.replace(NETEASE_SZ, ""); 65 | } 66 | 67 | /** 68 | * 上交所股票代码转统一代码 69 | * @param code 股票代码 70 | */ 71 | public SHTransform(code: string): string { 72 | if (!code.includes(NETEASE_SH)) { 73 | throw new Error(ERROR_API_CODE); 74 | } 75 | 76 | return COMMON_SH + code.replace(NETEASE_SH, ""); 77 | } 78 | 79 | /** 80 | * 港交所股票代码转统一代码 81 | * @param code 股票代码 82 | */ 83 | public HKTransform(code: string): string { 84 | if (!code.includes(NETEASE_HK)) { 85 | throw new Error(ERROR_API_CODE); 86 | } 87 | 88 | return COMMON_HK + code.replace(NETEASE_HK, ""); 89 | } 90 | 91 | /** 92 | * 美交所股票代码转统一代码 93 | * @param code 股票代码 94 | */ 95 | public USTransform(code: string): string { 96 | if (!code.includes(NETEASE_US)) { 97 | throw new Error(ERROR_API_CODE); 98 | } 99 | 100 | return COMMON_US + code.replace(NETEASE_US, ""); 101 | } 102 | } 103 | 104 | export default NeteaseApiCodeTransform; 105 | -------------------------------------------------------------------------------- /src/stocks/netease/transforms/common-code.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseCommonCodeTransform from "../../../stocks/base/transforms/common-code"; 3 | 4 | // Utils 5 | import { ERROR_COMMON_CODE } from "../../../utils/constant"; 6 | import { 7 | COMMON_SH, 8 | COMMON_SZ, 9 | COMMON_HK, 10 | COMMON_US, 11 | } from "../../../stocks/base/utils/constant"; 12 | import { 13 | NETEASE_SZ, 14 | NETEASE_SH, 15 | NETEASE_HK, 16 | NETEASE_US, 17 | } from "../../../stocks/netease/utils/constant"; 18 | 19 | /** 20 | * 【网易】统一代码转股票代码 21 | */ 22 | class NeteaseCommonCodeTransform extends BaseCommonCodeTransform { 23 | /** 24 | * 统一代码转股票代码 25 | * @param code 统一代码 26 | */ 27 | public transform(code: string): string { 28 | return super.transform(code); 29 | } 30 | 31 | /** 32 | * 统一代码组转换股票代码组 33 | * @param codes 统一代码组 34 | */ 35 | public transforms(codes: string[]): string[] { 36 | return super.transforms(codes); 37 | } 38 | 39 | /** 40 | * 深交所统一代码转股票代码 41 | * @param code 统一代码 42 | */ 43 | public SZTransform(code: string): string { 44 | if (!code.includes(COMMON_SZ)) { 45 | throw new Error(ERROR_COMMON_CODE); 46 | } 47 | 48 | return NETEASE_SZ + code.replace(COMMON_SZ, ""); 49 | } 50 | 51 | /** 52 | * 上交所统一代码转股票代码 53 | * @param code 统一代码 54 | */ 55 | public SHTransform(code: string): string { 56 | if (!code.includes(COMMON_SH)) { 57 | throw new Error(ERROR_COMMON_CODE); 58 | } 59 | 60 | return NETEASE_SH + code.replace(COMMON_SH, ""); 61 | } 62 | 63 | /** 64 | * 港交所统一代码转股票代码 65 | * @param code 统一代码 66 | */ 67 | public HKTransform(code: string): string { 68 | if (!code.includes(COMMON_HK)) { 69 | throw new Error(ERROR_COMMON_CODE); 70 | } 71 | 72 | return NETEASE_HK + code.replace(COMMON_HK, ""); 73 | } 74 | 75 | /** 76 | * 美交所统一代码转股票代码 77 | * @param code 统一代码 78 | */ 79 | public USTransform(code: string): string { 80 | if (!code.includes(COMMON_US)) { 81 | throw new Error(ERROR_COMMON_CODE); 82 | } 83 | 84 | return NETEASE_US + code.replace(COMMON_US, ""); 85 | } 86 | } 87 | 88 | export default NeteaseCommonCodeTransform; 89 | -------------------------------------------------------------------------------- /src/stocks/netease/transforms/stock.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseStockTransform from "../../../stocks/base/transforms/stock"; 3 | 4 | // Utils 5 | import { DEFAULT_STRING, DEFAULT_NUMBER } from "../../../utils/constant"; 6 | 7 | // Types 8 | import Stock from "../../../types/utils/stock"; 9 | import Dictionary from "../../../types/utils/dictionary"; 10 | 11 | /** 12 | * 网易股票数据解析 13 | */ 14 | class NeteaseStockTransform extends BaseStockTransform { 15 | /** 16 | * 构造函数 17 | */ 18 | constructor(public code: string, public params: Dictionary) { 19 | super(); 20 | } 21 | 22 | /** 23 | * 获取代码 24 | */ 25 | getCode(): string { 26 | return String(this.code).toUpperCase(); 27 | } 28 | 29 | /** 30 | * 获取名称 31 | */ 32 | getName(): string { 33 | return String(this.params.name || DEFAULT_STRING); 34 | } 35 | 36 | /** 37 | * 获取现价 38 | */ 39 | getNow(): number { 40 | return Number(this.params.price || DEFAULT_NUMBER); 41 | } 42 | 43 | /** 44 | * 获取最低价 45 | */ 46 | getLow(): number { 47 | return Number(this.params.low || DEFAULT_NUMBER); 48 | } 49 | 50 | /** 51 | * 获取最高价 52 | */ 53 | getHigh(): number { 54 | return Number(this.params.high || DEFAULT_NUMBER); 55 | } 56 | 57 | /** 58 | * 获取昨日收盘价 59 | */ 60 | getYesterday(): number { 61 | return Number(this.params.yestclose || DEFAULT_NUMBER); 62 | } 63 | 64 | /** 65 | * 获取涨跌 66 | */ 67 | getPercent(): number { 68 | return this.getNow() ? this.getNow() / this.getYesterday() - 1 : 0; 69 | } 70 | 71 | /** 72 | * 获取股票数据 73 | */ 74 | getStock(): Stock { 75 | return { 76 | code: this.getCode(), 77 | name: this.getName(), 78 | percent: this.getPercent(), 79 | 80 | now: this.getNow(), 81 | low: this.getLow(), 82 | high: this.getHigh(), 83 | yesterday: this.getYesterday(), 84 | }; 85 | } 86 | } 87 | 88 | export default NeteaseStockTransform; 89 | -------------------------------------------------------------------------------- /src/stocks/netease/utils/constant.ts: -------------------------------------------------------------------------------- 1 | // 默认交易所常量 2 | export const NETEASE_SZ = "1"; // 深交所相关常量 3 | export const NETEASE_SH = "0"; // 上交所相关常量 4 | export const NETEASE_HK = "hk"; // 港交所相关常量 5 | export const NETEASE_US = "US_"; // 美交所相关常量 6 | -------------------------------------------------------------------------------- /src/stocks/sina/index.ts: -------------------------------------------------------------------------------- 1 | // NPM 2 | import { uniq } from "lodash"; 3 | 4 | // Stocks 5 | import SinaStockTransform from "../../stocks/sina/transforms/stock"; 6 | import SinaCommonCodeTransform from "../../stocks/sina/transforms/common-code"; 7 | 8 | // Utils 9 | import fetch from "../../utils/fetch"; 10 | import iconv from "../../utils/iconv"; 11 | import { DEFAULT_STOCK } from "../../utils/constant"; 12 | import { 13 | COMMON_SZ, 14 | COMMON_SH, 15 | COMMON_US, 16 | COMMON_HK, 17 | } from "../../stocks/base/utils/constant"; 18 | 19 | // Types 20 | import Stock from "../../types/utils/stock"; 21 | import StockApi from "../../types/stocks/index"; 22 | 23 | /** 24 | * 新浪股票代码接口 25 | */ 26 | const Sina: StockApi = { 27 | /** 28 | * 获取股票数据 29 | * @param code 股票代码 30 | */ 31 | async getStock(code: string): Promise { 32 | const transform = new SinaCommonCodeTransform().transform(code); 33 | 34 | // 数据获取 35 | const url = `https://hq.sinajs.cn/list=${transform}`; 36 | const res = await fetch.get(url); 37 | 38 | const body = iconv.decode(res.body, "gb18030"); 39 | const rows = body.split(";\n"); 40 | const row = rows[0]; 41 | 42 | // 数据深解析 43 | const [_, paramsUnformat] = row.split("="); 44 | 45 | if (paramsUnformat === '""') { 46 | return { ...DEFAULT_STOCK, code }; 47 | } 48 | 49 | const params = paramsUnformat.replace('"', "").split(","); 50 | const data = new SinaStockTransform(code, params); 51 | 52 | return data.getStock(); 53 | }, 54 | 55 | /** 56 | * 获取股票数据组 57 | * @param codes 股票代码组 58 | */ 59 | async getStocks(codes: string[]): Promise { 60 | codes = uniq(codes.filter((i) => i !== "")); 61 | 62 | // 无股票时返回空数组 63 | if (codes.length === 0) { 64 | return []; 65 | } 66 | 67 | const transforms = new SinaCommonCodeTransform().transforms(codes); 68 | 69 | // 数据获取 70 | const url = `https://hq.sinajs.cn/list=${transforms.join(",")}`; 71 | const res = await fetch.get(url); 72 | 73 | const body = iconv.decode(res.body, "gb18030"); 74 | const rows = body.split(";\n"); 75 | 76 | return codes.map((code, index) => { 77 | // 数据深解析 78 | const [_, paramsUnformat] = rows[index].split("="); 79 | 80 | if (paramsUnformat === '""') { 81 | return { ...DEFAULT_STOCK, code }; 82 | } 83 | 84 | const params = paramsUnformat.replace('"', "").split(","); 85 | const data = new SinaStockTransform(code, params); 86 | 87 | return data.getStock(); 88 | }); 89 | }, 90 | 91 | /** 92 | * 搜索股票代码 93 | * @param key 关键字 94 | */ 95 | async searchStocks(key: string): Promise { 96 | // 数据获取 97 | const url = `http://suggest3.sinajs.cn/suggest/type=2&key=${encodeURIComponent( 98 | key 99 | )}`; 100 | const res = await fetch.get(url); 101 | 102 | const body = iconv.decode(res.body, "gb18030"); 103 | const rows: string[] = body 104 | .replace('var suggestvalue="', "") 105 | .replace('";', "") 106 | .split(";"); 107 | 108 | let codes: string[] = []; 109 | for (let i = 0; i < rows.length; i++) { 110 | let code: string = rows[i].split(",")[0]; 111 | 112 | if (code.indexOf("us") === 0) { 113 | code = code.replace("us", ""); 114 | codes = [...codes, COMMON_US + code]; 115 | } 116 | 117 | if (code.indexOf("sz") === 0) { 118 | code = code.replace("sz", ""); 119 | codes = [...codes, COMMON_SZ + code]; 120 | } 121 | 122 | if (code.indexOf("sh") === 0) { 123 | code = code.replace("sh", ""); 124 | codes = [...codes, COMMON_SH + code]; 125 | } 126 | 127 | if (code.indexOf("hk") === 0) { 128 | code = code.replace("hk", ""); 129 | codes = [...codes, COMMON_HK + code]; 130 | } 131 | 132 | if (code.indexOf("of") === 0) { 133 | code = code.replace("of", ""); 134 | codes = [...codes, COMMON_SZ + code, COMMON_SH + code]; 135 | } 136 | } 137 | 138 | return await Sina.getStocks(uniq(codes.filter((i) => i !== ""))); 139 | }, 140 | }; 141 | 142 | export default Sina; 143 | -------------------------------------------------------------------------------- /src/stocks/sina/transforms/api-code.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseCommonCodeTransform from "../../../stocks/base/transforms/common-code"; 3 | 4 | // Utils 5 | import { ERROR_API_CODE } from "../../../utils/constant"; 6 | import { 7 | SINA_SZ, 8 | SINA_SH, 9 | SINA_HK, 10 | SINA_US, 11 | } from "../../../stocks/sina/utils/constant"; 12 | import { 13 | COMMON_SH, 14 | COMMON_SZ, 15 | COMMON_HK, 16 | COMMON_US, 17 | } from "../../../stocks/base/utils/constant"; 18 | 19 | /** 20 | * 【新浪】股票代码转换统一代码 21 | */ 22 | class SinaCommonCodeTransform extends BaseCommonCodeTransform { 23 | /** 24 | * 交易所股票代码转换统一代码 25 | * @param code 股票代码 26 | */ 27 | public transform(code: string): string { 28 | if (code.indexOf(SINA_SZ) === 0) { 29 | return this.SZTransform(code); 30 | } 31 | 32 | if (code.indexOf(SINA_SH) === 0) { 33 | return this.SHTransform(code); 34 | } 35 | 36 | if (code.indexOf(SINA_HK) === 0) { 37 | return this.HKTransform(code); 38 | } 39 | 40 | if (code.indexOf(SINA_US) === 0) { 41 | return this.USTransform(code); 42 | } 43 | 44 | throw new Error(ERROR_API_CODE); 45 | } 46 | 47 | /** 48 | * 交易所股票代码组转换统一代码组 49 | * @param codes 股票代码组 50 | */ 51 | public transforms(codes: string[]): string[] { 52 | return super.transforms(codes); 53 | } 54 | 55 | /** 56 | * 深交所股票代码转换统一代码 57 | * @param code 股票代码 58 | */ 59 | public SZTransform(code: string): string { 60 | if (code.indexOf(SINA_SZ) !== 0) { 61 | throw new Error(ERROR_API_CODE); 62 | } 63 | 64 | return COMMON_SZ + code.replace(SINA_SZ, ""); 65 | } 66 | 67 | /** 68 | * 上交所股票代码转换统一代码 69 | * @param code 股票代码 70 | */ 71 | public SHTransform(code: string): string { 72 | if (code.indexOf(SINA_SH) !== 0) { 73 | throw new Error(ERROR_API_CODE); 74 | } 75 | 76 | return COMMON_SH + code.replace(SINA_SH, ""); 77 | } 78 | 79 | /** 80 | * 港交所股票代码转换统一代码 81 | * @param code 股票代码 82 | */ 83 | public HKTransform(code: string): string { 84 | if (code.indexOf(SINA_HK) !== 0) { 85 | throw new Error(ERROR_API_CODE); 86 | } 87 | 88 | return COMMON_HK + code.replace(SINA_HK, ""); 89 | } 90 | 91 | /** 92 | * 美交所股票代码转换统一代码 93 | * @param code 股票代码 94 | */ 95 | public USTransform(code: string): string { 96 | if (code.indexOf(SINA_US) !== 0) { 97 | throw new Error(ERROR_API_CODE); 98 | } 99 | 100 | return COMMON_US + code.replace(SINA_US, "").toLowerCase(); 101 | } 102 | } 103 | 104 | export default SinaCommonCodeTransform; 105 | -------------------------------------------------------------------------------- /src/stocks/sina/transforms/common-code.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseCommonCodeTransform from "../../../stocks/base/transforms/common-code"; 3 | 4 | // Utils 5 | import { ERROR_COMMON_CODE } from "../../../utils/constant"; 6 | import { 7 | SINA_SZ, 8 | SINA_SH, 9 | SINA_HK, 10 | SINA_US, 11 | } from "../../../stocks/sina/utils/constant"; 12 | import { 13 | COMMON_SH, 14 | COMMON_SZ, 15 | COMMON_HK, 16 | COMMON_US, 17 | } from "../../../stocks/base/utils/constant"; 18 | 19 | /** 20 | * 【新浪】统一代码转换股票代码 21 | */ 22 | class SinaCommonCodeTransform extends BaseCommonCodeTransform { 23 | /** 24 | * 交易所统一代码转换股票代码 25 | * @param code 统一代码 26 | */ 27 | public transform(code: string): string { 28 | return super.transform(code); 29 | } 30 | 31 | /** 32 | * 交易所统一代码组转换股票代码组 33 | * @param codes 统一代码组 34 | */ 35 | public transforms(codes: string[]): string[] { 36 | return super.transforms(codes); 37 | } 38 | 39 | /** 40 | * 深交所统一代码转换股票代码 41 | * @param code 统一代码 42 | */ 43 | public SZTransform(code: string): string { 44 | if (code.indexOf(COMMON_SZ) !== 0) { 45 | throw new Error(ERROR_COMMON_CODE); 46 | } 47 | 48 | return SINA_SZ + code.replace(COMMON_SZ, ""); 49 | } 50 | 51 | /** 52 | * 上交所统一代码转换股票代码 53 | * @param code 统一代码 54 | */ 55 | public SHTransform(code: string): string { 56 | if (code.indexOf(COMMON_SH) !== 0) { 57 | throw new Error(ERROR_COMMON_CODE); 58 | } 59 | 60 | return SINA_SH + code.replace(COMMON_SH, ""); 61 | } 62 | 63 | /** 64 | * 港交所统一代码转换股票代码 65 | * @param code 统一代码 66 | */ 67 | public HKTransform(code: string): string { 68 | if (code.indexOf(COMMON_HK) !== 0) { 69 | throw new Error(ERROR_COMMON_CODE); 70 | } 71 | 72 | return SINA_HK + code.replace(COMMON_HK, ""); 73 | } 74 | 75 | /** 76 | * 美交所统一代码转换股票代码 77 | * @param code 统一代码 78 | */ 79 | public USTransform(code: string): string { 80 | if (code.indexOf(COMMON_US) !== 0) { 81 | throw new Error(ERROR_COMMON_CODE); 82 | } 83 | 84 | return SINA_US + code.replace(COMMON_US, "").toLowerCase(); 85 | } 86 | } 87 | 88 | export default SinaCommonCodeTransform; 89 | -------------------------------------------------------------------------------- /src/stocks/sina/transforms/stock.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseStockTransform from "../../../stocks/base/transforms/stock"; 3 | 4 | // Utils 5 | import { DEFAULT_STRING, DEFAULT_NUMBER } from "../../../utils/constant"; 6 | import { 7 | COMMON_SH, 8 | COMMON_SZ, 9 | COMMON_HK, 10 | COMMON_US, 11 | } from "../../../stocks/base/utils/constant"; 12 | 13 | // Types 14 | import Stock from "../../../types/utils/stock"; 15 | 16 | /** 17 | * 新浪股票数据解析 18 | */ 19 | class SinaStockTransform extends BaseStockTransform { 20 | /** 21 | * 构造函数 22 | */ 23 | constructor(public code: string, public params: string[]) { 24 | super(); 25 | } 26 | 27 | /** 28 | * 获取代码 29 | */ 30 | getCode(): string { 31 | return String(this.code).toUpperCase(); 32 | } 33 | 34 | /** 35 | * 获取名称 36 | */ 37 | getName(): string { 38 | switch (this.code.slice(0, 2)) { 39 | case COMMON_SH: 40 | return String(this.params[0] || DEFAULT_STRING); 41 | case COMMON_SZ: 42 | return String(this.params[0] || DEFAULT_STRING); 43 | case COMMON_HK: 44 | return String(this.params[1] || DEFAULT_STRING); 45 | case COMMON_US: 46 | return String(this.params[0] || DEFAULT_STRING); 47 | default: 48 | return DEFAULT_STRING; 49 | } 50 | } 51 | 52 | /** 53 | * 获取现价 54 | */ 55 | getNow(): number { 56 | switch (this.code.slice(0, 2)) { 57 | case COMMON_SH: 58 | return Number(this.params[3] || DEFAULT_NUMBER); 59 | case COMMON_SZ: 60 | return Number(this.params[3] || DEFAULT_NUMBER); 61 | case COMMON_HK: 62 | return Number(this.params[6] || DEFAULT_NUMBER); 63 | case COMMON_US: 64 | return Number(this.params[1] || DEFAULT_NUMBER); 65 | default: 66 | return DEFAULT_NUMBER; 67 | } 68 | } 69 | 70 | /** 71 | * 获取最低价 72 | */ 73 | getLow(): number { 74 | switch (this.code.slice(0, 2)) { 75 | case COMMON_SH: 76 | return Number(this.params[5] || DEFAULT_NUMBER); 77 | case COMMON_SZ: 78 | return Number(this.params[5] || DEFAULT_NUMBER); 79 | case COMMON_HK: 80 | return Number(this.params[5] || DEFAULT_NUMBER); 81 | case COMMON_US: 82 | return Number(this.params[7] || DEFAULT_NUMBER); 83 | default: 84 | return DEFAULT_NUMBER; 85 | } 86 | } 87 | 88 | /** 89 | * 获取最高价 90 | */ 91 | getHigh(): number { 92 | switch (this.code.slice(0, 2)) { 93 | case COMMON_SH: 94 | return Number(this.params[4] || DEFAULT_NUMBER); 95 | case COMMON_SZ: 96 | return Number(this.params[4] || DEFAULT_NUMBER); 97 | case COMMON_HK: 98 | return Number(this.params[4] || DEFAULT_NUMBER); 99 | case COMMON_US: 100 | return Number(this.params[6] || DEFAULT_NUMBER); 101 | default: 102 | return DEFAULT_NUMBER; 103 | } 104 | } 105 | 106 | /** 107 | * 获取昨日收盘价 108 | */ 109 | getYesterday(): number { 110 | switch (this.code.slice(0, 2)) { 111 | case COMMON_SH: 112 | return Number(this.params[2] || DEFAULT_NUMBER); 113 | case COMMON_SZ: 114 | return Number(this.params[2] || DEFAULT_NUMBER); 115 | case COMMON_HK: 116 | return Number(this.params[3] || DEFAULT_NUMBER); 117 | case COMMON_US: 118 | return Number(this.params[26] || DEFAULT_NUMBER); 119 | default: 120 | return DEFAULT_NUMBER; 121 | } 122 | } 123 | 124 | /** 125 | * 获取涨跌 126 | */ 127 | getPercent(): number { 128 | return this.getNow() ? this.getNow() / this.getYesterday() - 1 : 0; 129 | } 130 | 131 | /** 132 | * 获取股票数据 133 | */ 134 | getStock(): Stock { 135 | return { 136 | code: this.getCode(), 137 | name: this.getName(), 138 | percent: this.getPercent(), 139 | 140 | now: this.getNow(), 141 | low: this.getLow(), 142 | high: this.getHigh(), 143 | yesterday: this.getYesterday(), 144 | }; 145 | } 146 | } 147 | 148 | export default SinaStockTransform; 149 | -------------------------------------------------------------------------------- /src/stocks/sina/utils/constant.ts: -------------------------------------------------------------------------------- 1 | // 默认交易所常量 2 | export const SINA_SZ = "sz"; // 深交所相关常量 3 | export const SINA_SH = "sh"; // 上交所相关常量 4 | export const SINA_HK = "hk"; // 港交所相关常量 5 | export const SINA_US = "gb_"; // 美交所相关常量 6 | -------------------------------------------------------------------------------- /src/stocks/tencent/index.ts: -------------------------------------------------------------------------------- 1 | // NPM 2 | import { uniq } from "lodash"; 3 | 4 | // Stocks 5 | import TencentStockTransform from "../../stocks/tencent/transforms/stock"; 6 | import TencentCommonCodeTransform from "../../stocks/tencent/transforms/common-code"; 7 | 8 | // Utils 9 | import fetch from "../../utils/fetch"; 10 | import iconv from "../../utils/iconv"; 11 | import { DEFAULT_STOCK } from "../../utils/constant"; 12 | import { 13 | COMMON_SH, 14 | COMMON_SZ, 15 | COMMON_US, 16 | COMMON_HK, 17 | } from "../../stocks/base/utils/constant"; 18 | 19 | // Types 20 | import Stock from "../../types/utils/stock"; 21 | import StockApi from "../../types/stocks/index"; 22 | 23 | /** 24 | * 腾讯股票代码接口 25 | */ 26 | const Tencent: StockApi = { 27 | /** 28 | * 获取股票数据 29 | * @param code 股票代码 30 | */ 31 | async getStock(code: string): Promise { 32 | const transform = new TencentCommonCodeTransform().transform(code); 33 | 34 | // 数据获取 35 | const url = `https://qt.gtimg.cn/q=${transform}`; 36 | const res = await fetch.get(url).responseType("blob"); 37 | 38 | const body = iconv.decode(res.body, "gbk"); 39 | const rows = body.split(";\n"); 40 | const row = rows[0]; 41 | 42 | if (!row.includes(transform)) { 43 | return { ...DEFAULT_STOCK, code }; 44 | } 45 | 46 | // 数据深解析 47 | const [_, paramsUnformat] = row.split("="); 48 | const params = paramsUnformat.replace('"', "").split("~"); 49 | const data = new TencentStockTransform(code, params); 50 | 51 | return data.getStock(); 52 | }, 53 | 54 | /** 55 | * 获取股票数据组 56 | * @param codes 股票代码组 57 | */ 58 | async getStocks(codes: string[]): Promise { 59 | codes = uniq(codes.filter((i) => i !== "")); 60 | 61 | // 无股票时返回空数组 62 | if (codes.length === 0) { 63 | return []; 64 | } 65 | 66 | const transforms = new TencentCommonCodeTransform().transforms(codes); 67 | 68 | // 数据获取 69 | const url = `https://qt.gtimg.cn/q=${transforms.join(",")}`; 70 | const res = await fetch.get(url).responseType("blob"); 71 | 72 | const body = iconv.decode(res.body, "gbk"); 73 | const rows: string[] = body.split(";\n"); 74 | 75 | return codes.map((code, index) => { 76 | const transform = new TencentCommonCodeTransform().transform(code); 77 | if (!rows.find((row) => row.includes(transform))) { 78 | return { ...DEFAULT_STOCK, code }; 79 | } 80 | 81 | // 数据深解析 82 | const [_, paramsUnformat] = rows[index].split("="); 83 | const params = paramsUnformat.replace('"', "").split("~"); 84 | const data = new TencentStockTransform(code, params); 85 | 86 | return data.getStock(); 87 | }); 88 | }, 89 | 90 | /** 91 | * 搜索股票代码 92 | * @param key 关键字 93 | */ 94 | async searchStocks(key: string): Promise { 95 | const url = `https://smartbox.gtimg.cn/s3/?v=2&t=all&c=1&q=${encodeURIComponent( 96 | key 97 | )}`; 98 | const res = await fetch.get(url).responseType("blob"); 99 | 100 | const body = iconv.decode(res.body, "gbk"); 101 | const rows: string[] = body 102 | .replace('v_hint="', "") 103 | .replace('"', "") 104 | .split("^"); 105 | 106 | const codes: string[] = rows.map((row) => { 107 | const [type, code] = row.split("~"); 108 | 109 | switch (type) { 110 | case "sz": 111 | return COMMON_SZ + code; 112 | case "sh": 113 | return COMMON_SH + code; 114 | case "hk": 115 | return COMMON_HK + code; 116 | case "us": 117 | return COMMON_US + code.split(".")[0].toUpperCase(); 118 | default: 119 | return ""; 120 | } 121 | }); 122 | 123 | return await Tencent.getStocks(uniq(codes.filter((i) => i !== ""))); 124 | }, 125 | }; 126 | 127 | export default Tencent; 128 | -------------------------------------------------------------------------------- /src/stocks/tencent/transforms/api-code.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseApiCodeTransform from "../../../stocks/base/transforms/api-code"; 3 | 4 | // Utils 5 | import { ERROR_API_CODE } from "../../../utils/constant"; 6 | import { 7 | COMMON_SH, 8 | COMMON_SZ, 9 | COMMON_HK, 10 | COMMON_US, 11 | } from "../../../stocks/base/utils/constant"; 12 | import { 13 | TENCENT_SZ, 14 | TENCENT_SH, 15 | TENCENT_HK, 16 | TENCENT_US, 17 | } from "../../../stocks/tencent/utils/constant"; 18 | 19 | /** 20 | * 【腾讯】股票代码转换统一代码 21 | */ 22 | class TencentApiCodeTransform extends BaseApiCodeTransform { 23 | /** 24 | * 交易所股票代码转换统一代码 25 | * @param code 股票代码 26 | */ 27 | public transform(code: string): string { 28 | if (code.indexOf(TENCENT_SZ) === 0) { 29 | return this.SZTransform(code); 30 | } 31 | 32 | if (code.indexOf(TENCENT_SH) === 0) { 33 | return this.SHTransform(code); 34 | } 35 | 36 | if (code.indexOf(TENCENT_HK) === 0) { 37 | return this.HKTransform(code); 38 | } 39 | 40 | if (code.indexOf(TENCENT_US) === 0) { 41 | return this.USTransform(code); 42 | } 43 | 44 | throw new Error(ERROR_API_CODE); 45 | } 46 | 47 | /** 48 | * 交易所股票代码组转换统一代码组 49 | * @param codes 股票代码组 50 | */ 51 | public transforms(codes: string[]): string[] { 52 | return super.transforms(codes); 53 | } 54 | 55 | /** 56 | * 深交所股票代码转换统一代码 57 | * @param code 股票代码 58 | */ 59 | public SZTransform(code: string): string { 60 | if (!code.includes(TENCENT_SZ)) { 61 | throw new Error(ERROR_API_CODE); 62 | } 63 | 64 | return COMMON_SZ + code.replace(TENCENT_SZ, ""); 65 | } 66 | 67 | /** 68 | * 上交所股票代码转换统一代码 69 | * @param code 股票代码 70 | */ 71 | public SHTransform(code: string): string { 72 | if (!code.includes(TENCENT_SH)) { 73 | throw new Error(ERROR_API_CODE); 74 | } 75 | 76 | return COMMON_SH + code.replace(TENCENT_SH, ""); 77 | } 78 | 79 | /** 80 | * 港交所股票代码转换统一代码 81 | * @param code 股票代码 82 | */ 83 | public HKTransform(code: string): string { 84 | if (!code.includes(TENCENT_HK)) { 85 | throw new Error(ERROR_API_CODE); 86 | } 87 | 88 | return COMMON_HK + code.replace(TENCENT_HK, ""); 89 | } 90 | 91 | /** 92 | * 美交所股票代码转换统一代码 93 | * @param code 股票代码 94 | */ 95 | public USTransform(code: string): string { 96 | if (!code.includes(TENCENT_US)) { 97 | throw new Error(ERROR_API_CODE); 98 | } 99 | 100 | return COMMON_US + code.replace(TENCENT_US, ""); 101 | } 102 | } 103 | 104 | export default TencentApiCodeTransform; 105 | -------------------------------------------------------------------------------- /src/stocks/tencent/transforms/common-code.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseCommonCodeTransform from "../../../stocks/base/transforms/common-code"; 3 | 4 | // Utils 5 | import { ERROR_COMMON_CODE } from "../../../utils/constant"; 6 | import { 7 | COMMON_SH, 8 | COMMON_SZ, 9 | COMMON_HK, 10 | COMMON_US, 11 | } from "../../../stocks/base/utils/constant"; 12 | import { 13 | TENCENT_SZ, 14 | TENCENT_SH, 15 | TENCENT_HK, 16 | TENCENT_US, 17 | } from "../../../stocks/tencent/utils/constant"; 18 | 19 | /** 20 | * 【腾讯】统一代码转换股票代码 21 | */ 22 | class TencentCommonCodeTransform extends BaseCommonCodeTransform { 23 | /** 24 | * 构造函数 25 | */ 26 | constructor() { 27 | super(); 28 | } 29 | 30 | /** 31 | * 交易所统一代码转换股票代码 32 | * @param code 统一代码 33 | */ 34 | public transform(code: string): string { 35 | return super.transform(code); 36 | } 37 | 38 | /** 39 | * 交易所统一代码组转换股票代码组 40 | * @param codes 统一代码组 41 | */ 42 | public transforms(codes: string[]): string[] { 43 | return super.transforms(codes); 44 | } 45 | 46 | /** 47 | * 深交所统一代码转换股票代码 48 | * @param code 统一代码 49 | */ 50 | public SZTransform(code: string): string { 51 | if (!code.includes(COMMON_SZ)) { 52 | throw new Error(ERROR_COMMON_CODE); 53 | } 54 | 55 | return TENCENT_SZ + code.replace(COMMON_SZ, ""); 56 | } 57 | 58 | /** 59 | * 上交所统一代码转换股票代码 60 | * @param code 统一代码 61 | */ 62 | public SHTransform(code: string): string { 63 | if (!code.includes(COMMON_SH)) { 64 | throw new Error(ERROR_COMMON_CODE); 65 | } 66 | 67 | return TENCENT_SH + code.replace(COMMON_SH, ""); 68 | } 69 | 70 | /** 71 | * 港交所统一代码转换股票代码 72 | * @param code 统一代码 73 | */ 74 | public HKTransform(code: string): string { 75 | if (!code.includes(COMMON_HK)) { 76 | throw new Error(ERROR_COMMON_CODE); 77 | } 78 | 79 | return TENCENT_HK + code.replace(COMMON_HK, "").toUpperCase(); 80 | } 81 | 82 | /** 83 | * 美交所统一代码转换股票代码 84 | * @param code 统一代码 85 | */ 86 | public USTransform(code: string): string { 87 | if (!code.includes(COMMON_US)) { 88 | throw new Error(ERROR_COMMON_CODE); 89 | } 90 | 91 | return TENCENT_US + code.replace(COMMON_US, "").toUpperCase(); 92 | } 93 | } 94 | 95 | export default TencentCommonCodeTransform; 96 | -------------------------------------------------------------------------------- /src/stocks/tencent/transforms/stock.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseStockTransform from "../../../stocks/base/transforms/stock"; 3 | 4 | // Utils 5 | import { DEFAULT_STRING, DEFAULT_NUMBER } from "../../../utils/constant"; 6 | 7 | // Types 8 | import Stock from "../../../types/utils/stock"; 9 | 10 | /** 11 | * 腾讯股票数据解析 12 | */ 13 | class TencentStockTransform extends BaseStockTransform { 14 | /** 15 | * 构造函数 16 | */ 17 | constructor(public code: string, public params: string[]) { 18 | super(); 19 | } 20 | 21 | /** 22 | * 获取代码 23 | */ 24 | getCode(): string { 25 | return String(this.code).toUpperCase(); 26 | } 27 | 28 | /** 29 | * 获取名称 30 | */ 31 | getName(): string { 32 | return String(this.params[1] || DEFAULT_STRING); 33 | } 34 | 35 | /** 36 | * 获取现价 37 | */ 38 | getNow(): number { 39 | return Number(this.params[3] || DEFAULT_NUMBER); 40 | } 41 | 42 | /** 43 | * 获取最低价 44 | */ 45 | getLow(): number { 46 | return Number(this.params[34] || DEFAULT_NUMBER); 47 | } 48 | 49 | /** 50 | * 获取最高价 51 | */ 52 | getHigh(): number { 53 | return Number(this.params[33] || DEFAULT_NUMBER); 54 | } 55 | 56 | /** 57 | * 获取昨日收盘价 58 | */ 59 | getYesterday(): number { 60 | return Number(this.params[4] || DEFAULT_NUMBER); 61 | } 62 | 63 | /** 64 | * 获取涨跌 65 | */ 66 | getPercent(): number { 67 | return this.getNow() ? this.getNow() / this.getYesterday() - 1 : 0; 68 | } 69 | 70 | /** 71 | * 获取股票数据 72 | */ 73 | getStock(): Stock { 74 | return { 75 | code: this.getCode(), 76 | name: this.getName(), 77 | percent: this.getPercent(), 78 | 79 | now: this.getNow(), 80 | low: this.getLow(), 81 | high: this.getHigh(), 82 | yesterday: this.getYesterday(), 83 | }; 84 | } 85 | } 86 | 87 | export default TencentStockTransform; 88 | -------------------------------------------------------------------------------- /src/stocks/tencent/utils/constant.ts: -------------------------------------------------------------------------------- 1 | // 默认交易所常量 2 | export const TENCENT_SZ = "sz"; // 深交所相关常量 3 | export const TENCENT_SH = "sh"; // 上交所相关常量 4 | export const TENCENT_HK = "hk"; // 港交所相关常量 5 | export const TENCENT_US = "us"; // 美交所相关常量 6 | -------------------------------------------------------------------------------- /src/stocks/xueqiu/index.ts: -------------------------------------------------------------------------------- 1 | // NPM 2 | import { uniq } from "lodash"; 3 | 4 | // Stocks 5 | import XueqiuStockTransform from "../xueqiu/transforms/stock"; 6 | import XueqiuCommonCodeTransform from "../xueqiu/transforms/common-code"; 7 | 8 | // Utils 9 | import fetch from "../../utils/fetch"; 10 | import { DEFAULT_STOCK } from "../../utils/constant"; 11 | import { 12 | COMMON_SZ, 13 | COMMON_SH, 14 | COMMON_HK, 15 | COMMON_US, 16 | } from "../base/utils/constant"; 17 | 18 | // Types 19 | import Stock from "../../types/utils/stock"; 20 | import StockApi from "../../types/stocks/index"; 21 | import Dictionary from "../../types/utils/dictionary"; 22 | 23 | let token: string = ""; 24 | 25 | /** 26 | * 雪球股票代码接口 27 | */ 28 | const Xueqiu: StockApi & { getToken(): Promise } = { 29 | /** 30 | * 获取 Token 31 | */ 32 | async getToken(): Promise { 33 | if (token !== "") return token; 34 | 35 | const res = await fetch.get("https://xueqiu.com/"); 36 | const cookies: string[] = res.headers["set-cookie"] as any; 37 | 38 | const param: string = 39 | cookies.filter((key) => key.includes("xq_a_token"))[0] || ""; 40 | token = param.split(";")[0] || ""; 41 | 42 | return token; 43 | }, 44 | 45 | /** 46 | * 获取股票数据 47 | * @param code 股票代码 48 | */ 49 | async getStock(code: string): Promise { 50 | const token = await this.getToken(); 51 | const transform = new XueqiuCommonCodeTransform().transform(code); 52 | 53 | // 数据获取 54 | const url = `https://stock.xueqiu.com/v5/stock/quote.json?symbol=${transform}`; 55 | const res = await fetch.get(url).set("Cookie", token); 56 | 57 | const body = JSON.parse(res.text); 58 | const row: Dictionary = body.data.quote; 59 | 60 | // 数据深解析 61 | const params = row; 62 | const data = new XueqiuStockTransform(code, params); 63 | 64 | return params ? data.getStock() : { ...DEFAULT_STOCK, code }; 65 | }, 66 | 67 | /** 68 | * 获取股票数据组 69 | * @param codes 股票代码组 70 | */ 71 | async getStocks(codes: string[]): Promise { 72 | codes = uniq(codes.filter((i) => i !== "")); 73 | 74 | // 无股票时返回空数组 75 | if (codes.length === 0) { 76 | return []; 77 | } 78 | 79 | const token = await this.getToken(); 80 | const transforms = new XueqiuCommonCodeTransform().transforms(codes); 81 | 82 | // 数据获取 83 | const url = `https://stock.xueqiu.com/v5/stock/batch/quote.json?symbol=${transforms.join( 84 | "," 85 | )}`; 86 | const res = await fetch.get(url).set("Cookie", token); 87 | 88 | const body = JSON.parse(res.text); 89 | const rows: Dictionary>[] = body.data.items; 90 | 91 | return codes.map((code) => { 92 | // 数据深解析 93 | const transform = new XueqiuCommonCodeTransform().transform(code); 94 | 95 | const params = 96 | rows.find((i) => { 97 | if (!i.quote) { 98 | return false; 99 | } 100 | 101 | if (i.market.region === "US") { 102 | return i.quote.code === transform; 103 | } 104 | 105 | if (i.market.region === "CN") { 106 | return i.quote.symbol === transform; 107 | } 108 | 109 | if (i.market.region === "HK") { 110 | return i.market.region + i.quote.code === transform; 111 | } 112 | }) || null; 113 | 114 | if (!params) { 115 | return { ...DEFAULT_STOCK, code }; 116 | } 117 | 118 | const data = new XueqiuStockTransform(code, params.quote); 119 | return data.getStock(); 120 | }); 121 | }, 122 | 123 | /** 124 | * 搜索股票代码 125 | * @param key 关键字 126 | */ 127 | async searchStocks(key: string): Promise { 128 | const token = await this.getToken(); 129 | 130 | // 数据获取 131 | const url = `https://xueqiu.com/stock/search.json?code=${encodeURIComponent( 132 | key 133 | )}`; 134 | const res = await fetch.get(url).set("Cookie", token); 135 | 136 | const body = JSON.parse(res.text); 137 | const rows: Dictionary[] = body.stocks; 138 | 139 | let codes: string[] = []; 140 | for (let i = 0; i < rows.length; i++) { 141 | let code: string = rows[i].code; 142 | 143 | if (code.indexOf("SZ") === 0) { 144 | code = code.replace("SZ", ""); 145 | codes = [...codes, COMMON_SZ + code]; 146 | continue; 147 | } 148 | 149 | if (code.indexOf("SH") === 0) { 150 | code = code.replace("SH", ""); 151 | codes = [...codes, COMMON_SH + code]; 152 | continue; 153 | } 154 | 155 | codes = [...codes, COMMON_HK + code, COMMON_US + code]; 156 | } 157 | 158 | return await Xueqiu.getStocks(uniq(codes.filter((i) => i !== ""))); 159 | }, 160 | }; 161 | 162 | export default Xueqiu; 163 | -------------------------------------------------------------------------------- /src/stocks/xueqiu/transforms/api-code.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseApiCodeTransform from "../../../stocks/base/transforms/api-code"; 3 | 4 | // Utils 5 | import { ERROR_API_CODE } from "../../../utils/constant"; 6 | import { 7 | COMMON_SH, 8 | COMMON_SZ, 9 | COMMON_HK, 10 | COMMON_US, 11 | } from "../../../stocks/base/utils/constant"; 12 | import { 13 | XUEQIU_SZ, 14 | XUEQIU_SH, 15 | XUEQIU_HK, 16 | XUEQIU_US, 17 | } from "../../../stocks/xueqiu/utils/constant"; 18 | 19 | /** 20 | * 【雪球】股票代码转换统一代码 21 | */ 22 | class XueqiuApiCodeTransform extends BaseApiCodeTransform { 23 | /** 24 | * 交易所股票代码转换统一代码 25 | * @param code 股票代码 26 | */ 27 | public transform(code: string): string { 28 | if (code.includes(XUEQIU_SZ)) { 29 | return this.SZTransform(code); 30 | } 31 | 32 | if (code.includes(XUEQIU_SH)) { 33 | return this.SHTransform(code); 34 | } 35 | 36 | if (code.includes(XUEQIU_HK)) { 37 | return this.HKTransform(code); 38 | } 39 | 40 | return this.USTransform(code); 41 | } 42 | 43 | /** 44 | * 交易所股票组代码组转换统一代码组 45 | * @param codes 股票代码组 46 | */ 47 | public transforms(codes: string[]): string[] { 48 | return super.transforms(codes); 49 | } 50 | 51 | /** 52 | * 深交所股票代码转换统一代码 53 | * @param code 股票代码 54 | */ 55 | public SZTransform(code: string): string { 56 | if (!code.includes(XUEQIU_SZ)) { 57 | throw new Error(ERROR_API_CODE); 58 | } 59 | 60 | return COMMON_SZ + code.replace(XUEQIU_SZ, ""); 61 | } 62 | 63 | /** 64 | * 上交所股票代码转换统一代码 65 | * @param code 股票代码 66 | */ 67 | public SHTransform(code: string): string { 68 | if (!code.includes(XUEQIU_SH)) { 69 | throw new Error(ERROR_API_CODE); 70 | } 71 | 72 | return COMMON_SH + code.replace(XUEQIU_SH, ""); 73 | } 74 | 75 | /** 76 | * 港交所股票代码转换统一代码 77 | * @param code 股票代码 78 | */ 79 | public HKTransform(code: string): string { 80 | if (!code.includes(XUEQIU_HK)) { 81 | throw new Error(ERROR_API_CODE); 82 | } 83 | 84 | return COMMON_HK + code.replace(XUEQIU_HK, ""); 85 | } 86 | 87 | /** 88 | * 美交所股票代码转换统一代码 89 | * @param code 股票代码 90 | */ 91 | public USTransform(code: string): string { 92 | if ( 93 | code.includes(XUEQIU_SZ) || 94 | code.includes(XUEQIU_SH) || 95 | code.includes(XUEQIU_HK) 96 | ) { 97 | throw new Error(ERROR_API_CODE); 98 | } 99 | 100 | return COMMON_US + code.replace(XUEQIU_US, ""); 101 | } 102 | } 103 | 104 | export default XueqiuApiCodeTransform; 105 | -------------------------------------------------------------------------------- /src/stocks/xueqiu/transforms/common-code.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseCommonCodeTransform from "../../../stocks/base/transforms/common-code"; 3 | 4 | // Utils 5 | import { ERROR_COMMON_CODE } from "../../../utils/constant"; 6 | import { 7 | COMMON_SH, 8 | COMMON_SZ, 9 | COMMON_HK, 10 | COMMON_US, 11 | } from "../../../stocks/base/utils/constant"; 12 | import { 13 | XUEQIU_SZ, 14 | XUEQIU_SH, 15 | XUEQIU_HK, 16 | XUEQIU_US, 17 | } from "../../../stocks/xueqiu/utils/constant"; 18 | 19 | /** 20 | * 【雪球】统一代码转换股票代码 21 | */ 22 | class XueqiuCommonCodeTransform extends BaseCommonCodeTransform { 23 | /** 24 | * 交易所统一代码转换股票代码 25 | * @param code 统一代码 26 | */ 27 | public transform(code: string): string { 28 | return super.transform(code); 29 | } 30 | 31 | /** 32 | * 交易所股 统一代码 转换 票组代码 33 | * @param codes 统一代码组 34 | */ 35 | public transforms(codes: string[]): string[] { 36 | return super.transforms(codes); 37 | } 38 | 39 | /** 40 | * 深交所统一代码转换股票代码 41 | * @param code 统一代码 42 | */ 43 | public SZTransform(code: string): string { 44 | if (!code.includes(COMMON_SZ)) { 45 | throw new Error(ERROR_COMMON_CODE); 46 | } 47 | 48 | return XUEQIU_SZ + code.replace(COMMON_SZ, ""); 49 | } 50 | 51 | /** 52 | * 上交所统一代码转换股票代码 53 | * @param code 统一代码 54 | */ 55 | public SHTransform(code: string): string { 56 | if (!code.includes(COMMON_SH)) { 57 | throw new Error(ERROR_COMMON_CODE); 58 | } 59 | 60 | return XUEQIU_SH + code.replace(COMMON_SH, ""); 61 | } 62 | 63 | /** 64 | * 港交所统一代码转换股票代码 65 | * @param code 统一代码 66 | */ 67 | public HKTransform(code: string): string { 68 | if (!code.includes(COMMON_HK)) { 69 | throw new Error(ERROR_COMMON_CODE); 70 | } 71 | 72 | return XUEQIU_HK + code.replace(COMMON_HK, ""); 73 | } 74 | 75 | /** 76 | * 美交所统一代码转换股票代码 77 | * @param code 统一代码 78 | */ 79 | public USTransform(code: string): string { 80 | if (!code.includes(COMMON_US)) { 81 | throw new Error(ERROR_COMMON_CODE); 82 | } 83 | 84 | return XUEQIU_US + code.replace(COMMON_US, ""); 85 | } 86 | } 87 | 88 | export default XueqiuCommonCodeTransform; 89 | -------------------------------------------------------------------------------- /src/stocks/xueqiu/transforms/stock.ts: -------------------------------------------------------------------------------- 1 | // Stocks 2 | import BaseStockTransform from "../../../stocks/base/transforms/stock"; 3 | 4 | // Utils 5 | import { DEFAULT_STRING, DEFAULT_NUMBER } from "../../../utils/constant"; 6 | 7 | // Types 8 | import Stock from "../../../types/utils/stock"; 9 | import Dictionary from "../../../types/utils/dictionary"; 10 | 11 | /** 12 | * 雪球股票数据解析 13 | */ 14 | class XueqiuStockTransform extends BaseStockTransform { 15 | /** 16 | * 构造函数 17 | */ 18 | constructor(public code: string, public params: Dictionary) { 19 | super(); 20 | } 21 | 22 | /** 23 | * 获取代码 24 | */ 25 | getCode(): string { 26 | return String(this.code).toUpperCase(); 27 | } 28 | 29 | /** 30 | * 获取名称 31 | */ 32 | getName(): string { 33 | return String(this.params.name || DEFAULT_STRING); 34 | } 35 | 36 | /** 37 | * 获取现价 38 | */ 39 | getNow(): number { 40 | return Number(this.params.current || DEFAULT_NUMBER); 41 | } 42 | 43 | /** 44 | * 获取最低价 45 | */ 46 | getLow(): number { 47 | return Number(this.params.low || DEFAULT_NUMBER); 48 | } 49 | 50 | /** 51 | * 获取最高价 52 | */ 53 | getHigh(): number { 54 | return Number(this.params.high || DEFAULT_NUMBER); 55 | } 56 | 57 | /** 58 | * 获取昨日收盘价 59 | */ 60 | getYesterday(): number { 61 | return Number(this.params.last_close || DEFAULT_NUMBER); 62 | } 63 | 64 | /** 65 | * 获取涨跌 66 | */ 67 | getPercent(): number { 68 | return this.getNow() ? this.getNow() / this.getYesterday() - 1 : 0; 69 | } 70 | 71 | /** 72 | * 获取股票数据 73 | */ 74 | getStock(): Stock { 75 | return { 76 | code: this.getCode(), 77 | name: this.getName(), 78 | percent: this.getPercent(), 79 | 80 | now: this.getNow(), 81 | low: this.getLow(), 82 | high: this.getHigh(), 83 | yesterday: this.getYesterday(), 84 | }; 85 | } 86 | } 87 | 88 | export default XueqiuStockTransform; 89 | -------------------------------------------------------------------------------- /src/stocks/xueqiu/utils/constant.ts: -------------------------------------------------------------------------------- 1 | // 默认交易所常量 2 | export const XUEQIU_SZ = "SZ"; // 深交所相关常量 3 | export const XUEQIU_SH = "SH"; // 上交所相关常量 4 | export const XUEQIU_HK = "HK"; // 港交所相关常量 5 | export const XUEQIU_US = ""; // 美交所相关常量 6 | -------------------------------------------------------------------------------- /src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import StockApi from "./stocks/index"; 2 | 3 | export declare const stocks: { 4 | base: StockApi; 5 | netease: StockApi; 6 | sina: StockApi; 7 | tencent: StockApi; 8 | xueqiu: StockApi; 9 | }; 10 | 11 | export declare const root: { 12 | stocks: { 13 | base: StockApi; 14 | netease: StockApi; 15 | sina: StockApi; 16 | tencent: StockApi; 17 | xueqiu: StockApi; 18 | }; 19 | }; 20 | 21 | export default root; 22 | -------------------------------------------------------------------------------- /src/types/stocks/index.d.ts: -------------------------------------------------------------------------------- 1 | import Stock from "../utils/stock"; 2 | 3 | export interface StockApi { 4 | /** 5 | * 获取股票数据 6 | * @param code 股票代码 7 | */ 8 | getStock(code: string): Promise; 9 | 10 | /** 11 | * 获取股票数据组 12 | * @param codes 股票代码组 13 | */ 14 | getStocks(codes: string[]): Promise; 15 | 16 | /** 17 | * 搜索股票代码 18 | * @param key 关键字 19 | */ 20 | searchStocks(key: string): Promise; 21 | } 22 | 23 | export default StockApi; 24 | -------------------------------------------------------------------------------- /src/types/stocks/transforms/api-code.d.ts: -------------------------------------------------------------------------------- 1 | export interface ApiCodeTransform { 2 | transform(code: string): string; 3 | transforms(codes: string[]): string[]; 4 | SZTransform(code: string): string; 5 | SHTransform(code: string): string; 6 | HKTransform(code: string): string; 7 | USTransform(code: string): string; 8 | } 9 | 10 | export default ApiCodeTransform; 11 | -------------------------------------------------------------------------------- /src/types/stocks/transforms/common-code.d.ts: -------------------------------------------------------------------------------- 1 | export interface CommonCodeTransform { 2 | transform(code: string): string; 3 | transforms(codes: string[]): string[]; 4 | SZTransform(code: string): string; 5 | SHTransform(code: string): string; 6 | HKTransform(code: string): string; 7 | USTransform(code: string): string; 8 | } 9 | 10 | export default CommonCodeTransform; 11 | -------------------------------------------------------------------------------- /src/types/stocks/transforms/stock.d.ts: -------------------------------------------------------------------------------- 1 | // Types 2 | import Stock from "../../utils/stock"; 3 | 4 | /** 5 | * 基础股票数据解析 6 | */ 7 | export interface StockTransform { 8 | /** 9 | * 获取代码 10 | */ 11 | getCode(): string; 12 | /** 13 | * 获取名称 14 | */ 15 | getName(): string; 16 | 17 | /** 18 | * 获取现价 19 | */ 20 | getNow(): number; 21 | 22 | /** 23 | * 获取最低价 24 | */ 25 | getLow(): number; 26 | 27 | /** 28 | * 获取最高价 29 | */ 30 | getHigh(): number; 31 | 32 | /** 33 | * 获取昨日收盘价 34 | */ 35 | getYesterday(): number; 36 | 37 | /** 38 | * 获取涨跌 39 | */ 40 | getPercent(): number; 41 | 42 | /** 43 | * 获取股票数据 44 | */ 45 | getStock(): Stock; 46 | } 47 | 48 | export default StockTransform; 49 | -------------------------------------------------------------------------------- /src/types/utils/dictionary.d.ts: -------------------------------------------------------------------------------- 1 | export type Dictionary = { 2 | [key in K]: T; 3 | }; 4 | 5 | export default Dictionary; 6 | -------------------------------------------------------------------------------- /src/types/utils/stock.d.ts: -------------------------------------------------------------------------------- 1 | export interface Stock { 2 | name: string; 3 | code: string; 4 | 5 | now: number; 6 | low: number; 7 | high: number; 8 | percent: number; 9 | yesterday: number; 10 | } 11 | 12 | export default Stock; 13 | -------------------------------------------------------------------------------- /src/utils/constant.ts: -------------------------------------------------------------------------------- 1 | // 默认股票名称常量 2 | export const DEFAULT_NUMBER = 0; 3 | export const DEFAULT_STRING = "---"; 4 | 5 | // 默认错误提示常量 6 | export const ERROR_API_CODE = "请检查股票代码是否正确"; 7 | export const ERROR_COMMON_CODE = "请检查统一代码是否正确"; 8 | 9 | export const ERROR_UNDEFINED_GET_STOCK = "未实现获取股票数据"; 10 | export const ERROR_UNDEFINED_GET_STOCKS = "未实现获取股票数据组"; 11 | export const ERROR_UNDEFINED_SEARCH_STOCK = "未实现搜索股票代码"; 12 | 13 | export const ERROR_UNDEFINED_COMMON_CODE = "未实现统一代码转换股票代码"; 14 | export const ERROR_UNDEFINED_SZ_COMMON_CODE = 15 | "未实现深交所统一代码转换股票代码"; 16 | export const ERROR_UNDEFINED_SH_COMMON_CODE = 17 | "未实现上交所统一代码转换股票代码"; 18 | export const ERROR_UNDEFINED_HK_COMMON_CODE = 19 | "未实现港交所统一代码转换股票代码"; 20 | export const ERROR_UNDEFINED_US_COMMON_CODE = 21 | "未实现美交所统一代码转换股票代码"; 22 | 23 | export const ERROR_UNDEFINED_API_CODE = "未实现股票代码转换统一代码"; 24 | export const ERROR_UNDEFINED_API_CODES = "未实现股票代码组转换统一代码组"; 25 | export const ERROR_UNDEFINED_SZ_API_CODE = "未实现深交所股票代码转换统一代码"; 26 | export const ERROR_UNDEFINED_SH_API_CODE = "未实现上交所股票代码转换统一代码"; 27 | export const ERROR_UNDEFINED_HK_API_CODE = "未实现港交所股票代码转换统一代码"; 28 | export const ERROR_UNDEFINED_US_API_CODE = "未实现美交所股票代码转换统一代码"; 29 | 30 | export const ERROR_UNDEFINED_GET_CODE = "未实现获取代码"; 31 | export const ERROR_UNDEFINED_GET_NAME = "未实现获取名称"; 32 | export const ERROR_UNDEFINED_GET_NOW = "未实现获取现价"; 33 | export const ERROR_UNDEFINED_GET_LOW = "未实现获取最低价"; 34 | export const ERROR_UNDEFINED_GET_HIGH = "未实现获取最高价"; 35 | export const ERROR_UNDEFINED_GET_PERCENT = "未实现获取涨跌"; 36 | export const ERROR_UNDEFINED_GET_YESTERDAY = "未实现获取昨日收盘价"; 37 | 38 | // 默认股票数据常量 39 | export const DEFAULT_STOCK = { 40 | code: DEFAULT_STRING, 41 | name: DEFAULT_STRING, 42 | percent: DEFAULT_NUMBER, 43 | 44 | now: DEFAULT_NUMBER, 45 | low: DEFAULT_NUMBER, 46 | high: DEFAULT_NUMBER, 47 | yesterday: DEFAULT_NUMBER, 48 | }; 49 | -------------------------------------------------------------------------------- /src/utils/fetch.ts: -------------------------------------------------------------------------------- 1 | import superagent from "superagent"; 2 | 3 | export default superagent; 4 | -------------------------------------------------------------------------------- /src/utils/iconv.ts: -------------------------------------------------------------------------------- 1 | import iconv from "iconv-lite"; 2 | 3 | export default iconv; 4 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0xA1390CC463264639535Be612FC5358787a86B865' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /test/stocks/base/index.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const Base = require("stocks/base").default; 3 | 4 | describe("【基础】股票代码接口", () => { 5 | const { 6 | ERROR_UNDEFINED_GET_STOCK, 7 | ERROR_UNDEFINED_GET_STOCKS, 8 | ERROR_UNDEFINED_SEARCH_STOCK, 9 | } = require("utils/constant"); 10 | 11 | it("需要获取的股票代码", async () => { 12 | await expect(Base.getStock("SZ000000")) 13 | .rejects 14 | .toThrow(new Error(ERROR_UNDEFINED_GET_STOCK)); 15 | }); 16 | 17 | it("需要获取的股票代码组", async () => { 18 | await expect(Base.getStocks(["SZ000000"])) 19 | .rejects 20 | .toThrow(new Error(ERROR_UNDEFINED_GET_STOCKS)); 21 | }); 22 | 23 | it("搜索股票代码", async () => { 24 | await expect(Base.searchStocks("510500")) 25 | .rejects 26 | .toThrow(new Error(ERROR_UNDEFINED_SEARCH_STOCK)); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/stocks/base/transforms/api-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const BaseApiCodeTransform = require("stocks/base/transforms/api-code").default; 3 | 4 | describe("【基础】股票代码转换统一代码", () => { 5 | const { 6 | ERROR_API_CODE, 7 | ERROR_UNDEFINED_SZ_API_CODE, 8 | ERROR_UNDEFINED_SH_API_CODE, 9 | ERROR_UNDEFINED_HK_API_CODE, 10 | ERROR_UNDEFINED_US_API_CODE 11 | } = require("utils/constant"); 12 | 13 | it("深交所股票代码转换统一代码", async () => { 14 | expect(() => (new BaseApiCodeTransform()).SZTransform("SZ000000")) 15 | .toThrow(new Error(ERROR_UNDEFINED_SZ_API_CODE)); 16 | }); 17 | 18 | it("上交所股票代码转换统一代码", async () => { 19 | expect(() => (new BaseApiCodeTransform()).SHTransform("SH000000")) 20 | .toThrow(new Error(ERROR_UNDEFINED_SH_API_CODE)); 21 | }); 22 | 23 | it("港交所股票代码转换统一代码", async () => { 24 | expect(() => new BaseApiCodeTransform().HKTransform("HK000000")) 25 | .toThrow(new Error(ERROR_UNDEFINED_HK_API_CODE)); 26 | }); 27 | 28 | it("美交所股票代码转换统一代码", async () => { 29 | expect(() => (new BaseApiCodeTransform()).USTransform("US000000")) 30 | .toThrow(new Error(ERROR_UNDEFINED_US_API_CODE)); 31 | }); 32 | 33 | it("交易所股票代码转换统一代码", async () => { 34 | expect(() => (new BaseApiCodeTransform()).transform("000000")) 35 | .toThrow(new Error(ERROR_API_CODE)); 36 | 37 | expect(() => (new BaseApiCodeTransform()).transform("SZ000000")) 38 | .toThrow(new Error(ERROR_UNDEFINED_SZ_API_CODE)); 39 | 40 | expect(() => (new BaseApiCodeTransform()).transform("SH000000")) 41 | .toThrow(new Error(ERROR_UNDEFINED_SH_API_CODE)); 42 | 43 | expect(() => (new BaseApiCodeTransform()).transform("HK000000")) 44 | .toThrow(new Error(ERROR_UNDEFINED_HK_API_CODE)); 45 | 46 | expect(() => (new BaseApiCodeTransform()).transform("US000000")) 47 | .toThrow(new Error(ERROR_UNDEFINED_US_API_CODE)); 48 | }); 49 | 50 | it("交易所股票代码组转换统一代码组", async () => { 51 | expect(() => (new BaseApiCodeTransform()).transforms(["000000"])) 52 | .toThrow(new Error(ERROR_API_CODE)); 53 | 54 | expect(() => (new BaseApiCodeTransform()).transforms(["SZ000000"])) 55 | .toThrow(new Error(ERROR_UNDEFINED_SZ_API_CODE)); 56 | 57 | expect(() => (new BaseApiCodeTransform()).transforms(["SH000000"])) 58 | .toThrow(new Error(ERROR_UNDEFINED_SH_API_CODE)); 59 | 60 | expect(() => (new BaseApiCodeTransform()).transforms(["HK000000"])) 61 | .toThrow(new Error(ERROR_UNDEFINED_HK_API_CODE)); 62 | 63 | expect(() => (new BaseApiCodeTransform()).transforms(["US000000"])) 64 | .toThrow(new Error(ERROR_UNDEFINED_US_API_CODE)); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/stocks/base/transforms/common-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const BaseCommonCodeTransform = require("stocks/base/transforms/common-code").default; 3 | 4 | describe("【基础】统一代码转换股票代码", () => { 5 | const { 6 | ERROR_COMMON_CODE, 7 | ERROR_UNDEFINED_SZ_COMMON_CODE, 8 | ERROR_UNDEFINED_SH_COMMON_CODE, 9 | ERROR_UNDEFINED_HK_COMMON_CODE, 10 | ERROR_UNDEFINED_US_COMMON_CODE 11 | } = require("utils/constant"); 12 | 13 | it("深交所统一代码转换股票代码", async () => { 14 | expect(() => (new BaseCommonCodeTransform()).SZTransform("SZ000000")) 15 | .toThrow(new Error(ERROR_UNDEFINED_SZ_COMMON_CODE)); 16 | }); 17 | 18 | it("上交所统一代码转换股票代码", async () => { 19 | expect(() => (new BaseCommonCodeTransform()).SHTransform("SH000000")) 20 | .toThrow(new Error(ERROR_UNDEFINED_SH_COMMON_CODE)); 21 | }); 22 | 23 | it("港交所统一代码转换股票代码", async () => { 24 | expect(() => new BaseCommonCodeTransform().HKTransform("HK000000")) 25 | .toThrow(new Error(ERROR_UNDEFINED_HK_COMMON_CODE)); 26 | }); 27 | 28 | it("美交所统一代码转换股票代码", async () => { 29 | expect(() => (new BaseCommonCodeTransform()).USTransform("US000000")) 30 | .toThrow(new Error(ERROR_UNDEFINED_US_COMMON_CODE)); 31 | }); 32 | 33 | it("交易所统一代码转换股票代码", async () => { 34 | expect(() => (new BaseCommonCodeTransform()).transform("000000")) 35 | .toThrow(new Error(ERROR_COMMON_CODE)); 36 | 37 | expect(() => (new BaseCommonCodeTransform()).transform("SZ000000")) 38 | .toThrow(new Error(ERROR_UNDEFINED_SZ_COMMON_CODE)); 39 | 40 | expect(() => (new BaseCommonCodeTransform()).transform("SH000000")) 41 | .toThrow(new Error(ERROR_UNDEFINED_SH_COMMON_CODE)); 42 | 43 | expect(() => (new BaseCommonCodeTransform()).transform("HK000000")) 44 | .toThrow(new Error(ERROR_UNDEFINED_HK_COMMON_CODE)); 45 | 46 | expect(() => (new BaseCommonCodeTransform()).transform("US000000")) 47 | .toThrow(new Error(ERROR_UNDEFINED_US_COMMON_CODE)); 48 | }); 49 | 50 | it("交易所统一代码组转换股票代码组", async () => { 51 | expect(() => (new BaseCommonCodeTransform()).transforms(["000000"])) 52 | .toThrow(new Error(ERROR_COMMON_CODE)); 53 | 54 | expect(() => (new BaseCommonCodeTransform()).transforms(["SZ000000"])) 55 | .toThrow(new Error(ERROR_UNDEFINED_SZ_COMMON_CODE)); 56 | 57 | expect(() => (new BaseCommonCodeTransform()).transforms(["SH000000"])) 58 | .toThrow(new Error("未实现上交所统一代码转换股票代码")); 59 | 60 | expect(() => (new BaseCommonCodeTransform()).transforms(["HK000000"])) 61 | .toThrow(new Error(ERROR_UNDEFINED_HK_COMMON_CODE)); 62 | 63 | expect(() => (new BaseCommonCodeTransform()).transforms(["US000000"])) 64 | .toThrow(new Error(ERROR_UNDEFINED_US_COMMON_CODE)); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/stocks/base/transforms/stock.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const BaseStockTransform = require("stocks/base/transforms/stock").default; 3 | 4 | describe("【基础】股票数据转换测试", () => { 5 | const { 6 | ERROR_UNDEFINED_GET_CODE, 7 | ERROR_UNDEFINED_GET_NAME, 8 | ERROR_UNDEFINED_GET_NOW, 9 | ERROR_UNDEFINED_GET_LOW, 10 | ERROR_UNDEFINED_GET_HIGH, 11 | ERROR_UNDEFINED_GET_STOCK, 12 | ERROR_UNDEFINED_GET_PERCENT, 13 | ERROR_UNDEFINED_GET_YESTERDAY, 14 | } = require("utils/constant"); 15 | 16 | it("获取股票代码", async () => { 17 | expect(() => (new BaseStockTransform()).getCode()) 18 | .toThrow(new Error(ERROR_UNDEFINED_GET_CODE)); 19 | }); 20 | 21 | it("获取股票名称", async () => { 22 | expect(() => (new BaseStockTransform()).getName()) 23 | .toThrow(new Error(ERROR_UNDEFINED_GET_NAME)); 24 | }); 25 | 26 | it("获取股票现价", async () => { 27 | expect(() => (new BaseStockTransform()).getNow()) 28 | .toThrow(new Error(ERROR_UNDEFINED_GET_NOW)); 29 | }); 30 | 31 | it("获取股票最低价", async () => { 32 | expect(() => (new BaseStockTransform()).getLow()) 33 | .toThrow(new Error(ERROR_UNDEFINED_GET_LOW)); 34 | }); 35 | 36 | it("获取股票最高价", async () => { 37 | expect(() => (new BaseStockTransform()).getHigh()) 38 | .toThrow(new Error(ERROR_UNDEFINED_GET_HIGH)); 39 | }); 40 | 41 | it("获取股票昨日收盘价", async () => { 42 | expect(() => (new BaseStockTransform()).getYesterday()) 43 | .toThrow(new Error(ERROR_UNDEFINED_GET_YESTERDAY)); 44 | }); 45 | 46 | it("获取股票涨跌", async () => { 47 | expect(() => (new BaseStockTransform()).getPercent()) 48 | .toThrow(new Error(ERROR_UNDEFINED_GET_PERCENT)); 49 | }); 50 | 51 | it("获取股票数据", async () => { 52 | expect(() => (new BaseStockTransform()).getStock()) 53 | .toThrow(new Error(ERROR_UNDEFINED_GET_STOCK)); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/stocks/netease/index.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const Netease = require("stocks/netease").default; 3 | 4 | describe("【网易】股票代码接口", () => { 5 | it("基础测试", async () => { 6 | await expect(true).toEqual(true); 7 | }); 8 | 9 | it("需要获取的股票代码", async () => { 10 | await expect(Netease.getStock("SH510500")).resolves.toMatchObject({ 11 | code: "SH510500", 12 | name: "500ETF", 13 | }); 14 | 15 | await expect(Netease.getStock("SZ510500")).resolves.toMatchObject({ 16 | code: "SZ510500", 17 | name: "---", 18 | }); 19 | }); 20 | 21 | it("需要获取的股票代码组", async () => { 22 | await expect(Netease.getStocks(["SH510500"])).resolves.toEqual( 23 | expect.arrayContaining([ 24 | expect.objectContaining({ code: "SH510500", name: "500ETF" }), 25 | ]) 26 | ); 27 | 28 | await expect(Netease.getStocks(["SZ510500"])).resolves.toEqual( 29 | expect.arrayContaining([ 30 | expect.objectContaining({ code: "SZ510500", name: "---" }), 31 | ]) 32 | ); 33 | 34 | await expect(Netease.getStocks([])).resolves.toEqual([]); 35 | }); 36 | 37 | it("搜索股票代码", async () => { 38 | await expect(Netease.searchStocks("格力电器")).resolves.toEqual( 39 | expect.arrayContaining([ 40 | expect.objectContaining({ code: "SZ000651", name: "格力电器" }), 41 | ]) 42 | ); 43 | 44 | await expect(Netease.searchStocks("贵州茅台")).resolves.toEqual( 45 | expect.arrayContaining([ 46 | expect.objectContaining({ code: "SH600519", name: "贵州茅台" }), 47 | ]) 48 | ); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/stocks/netease/transforms/api-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const NeteaseApiCodeTransform = require("stocks/netease/transforms/api-code").default; 3 | 4 | describe("【网易】股票代码转换统一代码", () => { 5 | const { 6 | ERROR_API_CODE, 7 | } = require("utils/constant"); 8 | 9 | it("深交所股票代码转换统一代码", async () => { 10 | expect(() => (new NeteaseApiCodeTransform()).SZTransform("STOCKAPI")) 11 | .toThrow(new Error(ERROR_API_CODE)); 12 | 13 | expect((new NeteaseApiCodeTransform()).SZTransform("1000000")) 14 | .toBe("SZ000000"); 15 | }); 16 | 17 | it("上交所股票代码转换统一代码", async () => { 18 | expect(() => (new NeteaseApiCodeTransform()).SHTransform("STOCKAPI")) 19 | .toThrow(new Error(ERROR_API_CODE)); 20 | 21 | expect((new NeteaseApiCodeTransform()).SHTransform("0000000")) 22 | .toBe("SH000000"); 23 | }); 24 | 25 | it("港交所股票代码转换统一代码", async () => { 26 | expect(() => (new NeteaseApiCodeTransform()).HKTransform("STOCKAPI")) 27 | .toThrow(new Error(ERROR_API_CODE)); 28 | 29 | expect((new NeteaseApiCodeTransform()).HKTransform("hk000000")) 30 | .toBe("HK000000"); 31 | }); 32 | 33 | it("美交所股票代码转换统一代码", async () => { 34 | expect(() => (new NeteaseApiCodeTransform()).USTransform("STOCKAPI")) 35 | .toThrow(new Error(ERROR_API_CODE)); 36 | 37 | expect((new NeteaseApiCodeTransform()).USTransform("US_000000")) 38 | .toBe("US000000"); 39 | }); 40 | 41 | it("交易所股票代码转换统一代码", async () => { 42 | expect((new NeteaseApiCodeTransform()).transform("1000000")) 43 | .toBe("SZ000000"); 44 | 45 | expect((new NeteaseApiCodeTransform()).transform("0000000")) 46 | .toBe("SH000000"); 47 | 48 | expect((new NeteaseApiCodeTransform()).transform("hk000000")) 49 | .toBe("HK000000"); 50 | 51 | expect((new NeteaseApiCodeTransform()).transform("US_000000")) 52 | .toBe("US000000"); 53 | 54 | expect(() => (new NeteaseApiCodeTransform()).transform("STOCKAPI")) 55 | .toThrow(new Error(ERROR_API_CODE)); 56 | }); 57 | 58 | it("交易所股票代码组转换统一代码组", async () => { 59 | expect((new NeteaseApiCodeTransform()).transforms(["1000000"])) 60 | .toStrictEqual(["SZ000000"]); 61 | 62 | expect((new NeteaseApiCodeTransform()).transforms(["0000000"])) 63 | .toStrictEqual(["SH000000"]); 64 | 65 | expect((new NeteaseApiCodeTransform()).transforms(["hk000000"])) 66 | .toStrictEqual(["HK000000"]); 67 | 68 | expect((new NeteaseApiCodeTransform()).transforms(["US_000000"])) 69 | .toStrictEqual(["US000000"]); 70 | 71 | expect(() => (new NeteaseApiCodeTransform()).transforms(["STOCKAPI"])) 72 | .toThrow(new Error(ERROR_API_CODE)); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/stocks/netease/transforms/common-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const NeteaseCommonCodeTransform = require("stocks/netease/transforms/common-code").default; 3 | 4 | describe("【网易】统一代码转换股票代码", () => { 5 | const { 6 | ERROR_COMMON_CODE, 7 | } = require("utils/constant"); 8 | 9 | it("深交所统一代码转换股票代码", async () => { 10 | expect(() => (new NeteaseCommonCodeTransform()).SZTransform("STOCKAPI")) 11 | .toThrow(new Error(ERROR_COMMON_CODE)); 12 | 13 | expect((new NeteaseCommonCodeTransform()).SZTransform("SZ000000")) 14 | .toBe("1000000"); 15 | }); 16 | 17 | it("上交所统一代码转换股票代码", async () => { 18 | expect(() => (new NeteaseCommonCodeTransform()).SHTransform("STOCKAPI")) 19 | .toThrow(new Error(ERROR_COMMON_CODE)); 20 | 21 | expect((new NeteaseCommonCodeTransform()).SHTransform("SH000000")) 22 | .toBe("0000000"); 23 | }); 24 | 25 | it("港交所统一代码转换股票代码", async () => { 26 | expect(() => (new NeteaseCommonCodeTransform()).HKTransform("STOCKAPI")) 27 | .toThrow(new Error(ERROR_COMMON_CODE)); 28 | 29 | expect((new NeteaseCommonCodeTransform()).HKTransform("HK000000")) 30 | .toBe("hk000000"); 31 | }); 32 | 33 | it("美交所统一代码转换股票代码", async () => { 34 | expect(() => (new NeteaseCommonCodeTransform()).USTransform("STOCKAPI")) 35 | .toThrow(new Error(ERROR_COMMON_CODE)); 36 | 37 | expect((new NeteaseCommonCodeTransform()).USTransform("US000000")) 38 | .toBe("US_000000"); 39 | }); 40 | 41 | it("交易所统一代码转换股票代码", async () => { 42 | expect((new NeteaseCommonCodeTransform()).transform("SZ000000")) 43 | .toBe("1000000"); 44 | 45 | expect((new NeteaseCommonCodeTransform()).transform("SH000000")) 46 | .toBe("0000000"); 47 | 48 | expect((new NeteaseCommonCodeTransform()).transform("HK000000")) 49 | .toBe("hk000000"); 50 | 51 | expect((new NeteaseCommonCodeTransform()).transform("US000000")) 52 | .toBe("US_000000"); 53 | 54 | expect(() => (new NeteaseCommonCodeTransform()).transform("STOCKAPI")) 55 | .toThrow(new Error(ERROR_COMMON_CODE)); 56 | }); 57 | 58 | it("交易所统一代码组转换股票代码组", async () => { 59 | expect((new NeteaseCommonCodeTransform()).transforms(["SZ000000"])) 60 | .toStrictEqual(["1000000"]); 61 | 62 | expect((new NeteaseCommonCodeTransform()).transforms(["SH000000"])) 63 | .toStrictEqual(["0000000"]); 64 | 65 | expect((new NeteaseCommonCodeTransform()).transforms(["HK000000"])) 66 | .toStrictEqual(["hk000000"]); 67 | 68 | expect((new NeteaseCommonCodeTransform()).transforms(["US000000"])) 69 | .toStrictEqual(["US_000000"]); 70 | 71 | expect(() => (new NeteaseCommonCodeTransform()).transforms(["STOCKAPI"])) 72 | .toThrow(new Error(ERROR_COMMON_CODE)); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/stocks/netease/transforms/stock.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const NeteaseStockTransform = require("stocks/netease/transforms/stock").default; 3 | 4 | describe("【网易】股票数据转换测试", () => { 5 | const code = 'SH510500'; 6 | const body = `topstock({"0510500":{"code": "0510500", "precloseiopv": 7.152, "percent": 0.010491, "high": 7.28, "askvol3": 32300, "askvol2": 88400, "askvol5": 19000, "askvol4": 572800, "iopv": 7.226, "price": 7.224, "open": 7.147, "bid5": 7.22, "bid4": 7.221, "bid3": 7.222, "bid2": 7.223, "bid1": 7.224, "low": 7.085, "updown": 0.075, "type": "SH", "symbol": "510500", "status": 0, "ask4": 7.228, "bidvol3": 37000, "bidvol2": 32900, "bidvol1": 6300, "update": "2020/07/31 15:59:59", "bidvol5": 14300, "bidvol4": 28600, "yestclose": 7.149, "askvol1": 389300, "ask5": 7.229, "volume": 272478144, "ask1": 7.225, "name": "500ETF", "ask3": 7.227, "ask2": 7.226, "arrow": "\u2191", "time": "2020/07/31 15:59:58", "turnover": 1964297124} });`; 7 | const items = JSON.parse(body.replace(/\(|\)|;|(topstock)/g, '')); 8 | const params = items['0510500']; 9 | 10 | it("获取股票代码", async () => { 11 | expect(new NeteaseStockTransform(code, params).getCode()) 12 | .toBe('SH510500'); 13 | }); 14 | 15 | it("获取股票名称", async () => { 16 | expect(new NeteaseStockTransform(code, params).getName()) 17 | .toBe('500ETF'); 18 | }); 19 | 20 | it("获取股票现价", async () => { 21 | expect(new NeteaseStockTransform(code, params).getNow()) 22 | .toBe(7.224); 23 | }); 24 | 25 | it("获取股票最低价", async () => { 26 | expect(new NeteaseStockTransform(code, params).getLow()) 27 | .toBe(7.085); 28 | }); 29 | 30 | it("获取股票最高价", async () => { 31 | expect(new NeteaseStockTransform(code, params).getHigh()) 32 | .toBe(7.28); 33 | }); 34 | 35 | it("获取股票昨日收盘价", async () => { 36 | expect(new NeteaseStockTransform(code, params).getYesterday()) 37 | .toBe(7.149); 38 | }); 39 | 40 | it("获取股票涨跌", async () => { 41 | expect(new NeteaseStockTransform(code, params).getPercent()) 42 | .toBe(7.224 / 7.149 - 1); 43 | }); 44 | 45 | it("获取股票数据", async () => { 46 | expect(new NeteaseStockTransform(code, params).getStock()) 47 | .toStrictEqual({ "code": "SH510500", "high": 7.28, "low": 7.085, "name": "500ETF", "now": 7.224, "percent": 7.224 / 7.149 - 1, "yesterday": 7.149 }); 48 | }); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /test/stocks/sina/index.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const Sina = require("stocks/sina").default; 3 | 4 | describe("【新浪】股票代码接口", () => { 5 | it("基础测试", async () => { 6 | await expect(true).toEqual(true); 7 | }); 8 | 9 | // it("需要获取的股票代码", async () => { 10 | // await expect(Sina.getStock("SH510500")).resolves.toMatchObject({ 11 | // code: "SH510500", 12 | // name: "500ETF", 13 | // }); 14 | // await expect(Sina.getStock("SZ510500")).resolves.toMatchObject({ 15 | // code: "SZ510500", 16 | // name: "---", 17 | // }); 18 | // }); 19 | // it("需要获取的股票代码组", async () => { 20 | // await expect(Sina.getStocks(["SH510500"])) 21 | // .resolves 22 | // .toEqual(expect.arrayContaining([expect.objectContaining({ code: "SH510500", name: "500ETF" })])); 23 | // await expect(Sina.getStocks(["SZ510500"])) 24 | // .resolves 25 | // .toEqual(expect.arrayContaining([expect.objectContaining({ code: "SZ510500", name: "---" })])); 26 | // await expect(Sina.getStocks([])) 27 | // .resolves 28 | // .toEqual([]); 29 | // }); 30 | // it("搜索股票代码", async () => { 31 | // await expect(Sina.searchStocks("格力电器")) 32 | // .resolves 33 | // .toEqual(expect.arrayContaining([expect.objectContaining({ code: "SZ000651", name: "格力电器" })])); 34 | // await expect(Sina.searchStocks("贵州茅台")) 35 | // .resolves 36 | // .toEqual(expect.arrayContaining([expect.objectContaining({ code: "SH600519", name: "贵州茅台" })])); 37 | // await expect(Sina.searchStocks("安踏体育")) 38 | // .resolves 39 | // .toEqual(expect.arrayContaining([ 40 | // expect.objectContaining({ code: "HK02020", name: "安踏体育" }), 41 | // expect.objectContaining({ code: "USANPDF", name: "安踏" }), 42 | // ])); 43 | // }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/stocks/sina/transforms/api-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const SinaApiCodeTransform = require("stocks/sina/transforms/api-code").default; 3 | 4 | describe("【新浪】股票代码转换统一代码", () => { 5 | const { 6 | ERROR_API_CODE, 7 | } = require("utils/constant"); 8 | 9 | it("深交所股票代码转换统一代码", async () => { 10 | expect(() => (new SinaApiCodeTransform()).SZTransform("STOCKAPI")) 11 | .toThrow(new Error(ERROR_API_CODE)); 12 | 13 | expect((new SinaApiCodeTransform()).SZTransform("sz000000")) 14 | .toBe("SZ000000"); 15 | }); 16 | 17 | it("上交所股票代码转换统一代码", async () => { 18 | expect(() => (new SinaApiCodeTransform()).SHTransform("STOCKAPI")) 19 | .toThrow(new Error(ERROR_API_CODE)); 20 | 21 | expect((new SinaApiCodeTransform()).SHTransform("sh000000")) 22 | .toBe("SH000000"); 23 | }); 24 | 25 | it("港交所股票代码转换统一代码", async () => { 26 | expect(() => (new SinaApiCodeTransform()).HKTransform("STOCKAPI")) 27 | .toThrow(new Error(ERROR_API_CODE)); 28 | 29 | expect((new SinaApiCodeTransform()).HKTransform("hk000000")) 30 | .toBe("HK000000"); 31 | }); 32 | 33 | it("美交所股票代码转换统一代码", async () => { 34 | expect(() => (new SinaApiCodeTransform()).USTransform("STOCKAPI")) 35 | .toThrow(new Error(ERROR_API_CODE)); 36 | 37 | expect((new SinaApiCodeTransform()).USTransform("gb_000000")) 38 | .toBe("US000000"); 39 | }); 40 | 41 | it("交易所股票代码转换统一代码", async () => { 42 | expect((new SinaApiCodeTransform()).transform("sz000000")) 43 | .toBe("SZ000000"); 44 | 45 | expect((new SinaApiCodeTransform()).transform("sh000000")) 46 | .toBe("SH000000"); 47 | 48 | expect((new SinaApiCodeTransform()).transform("hk000000")) 49 | .toBe("HK000000"); 50 | 51 | expect((new SinaApiCodeTransform()).transform("gb_000000")) 52 | .toBe("US000000"); 53 | 54 | expect(() => (new SinaApiCodeTransform()).transform("STOCKAPI")) 55 | .toThrow(new Error(ERROR_API_CODE)); 56 | }); 57 | 58 | it("交易所股票代码转换统一代码", async () => { 59 | expect((new SinaApiCodeTransform()).transforms(["sz000000"])) 60 | .toStrictEqual(["SZ000000"]); 61 | 62 | expect((new SinaApiCodeTransform()).transforms(["sh000000"])) 63 | .toStrictEqual(["SH000000"]); 64 | 65 | expect((new SinaApiCodeTransform()).transforms(["hk000000"])) 66 | .toStrictEqual(["HK000000"]); 67 | 68 | expect((new SinaApiCodeTransform()).transforms(["gb_000000"])) 69 | .toStrictEqual(["US000000"]); 70 | 71 | expect(() => (new SinaApiCodeTransform()).transforms(["STOCKAPI"])) 72 | .toThrow(new Error(ERROR_API_CODE)); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/stocks/sina/transforms/common-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const SinaCommonCodeTransform = require("stocks/sina/transforms/common-code").default; 3 | 4 | describe("【新浪】统一代码转换股票代码", () => { 5 | const { 6 | ERROR_COMMON_CODE, 7 | } = require("utils/constant"); 8 | 9 | it("深交所统一代码转换股票代码", async () => { 10 | expect(() => (new SinaCommonCodeTransform()).SZTransform("000000")) 11 | .toThrow(new Error(ERROR_COMMON_CODE)); 12 | 13 | expect((new SinaCommonCodeTransform()).SZTransform("SZ000000")) 14 | .toBe("sz000000"); 15 | }); 16 | 17 | it("上交所统一代码转换股票代码", async () => { 18 | expect(() => (new SinaCommonCodeTransform()).SHTransform("000000")) 19 | .toThrow(new Error(ERROR_COMMON_CODE)); 20 | 21 | expect((new SinaCommonCodeTransform()).SHTransform("SH000000")) 22 | .toBe("sh000000"); 23 | }); 24 | 25 | it("港交所统一代码转换股票代码", async () => { 26 | expect(() => (new SinaCommonCodeTransform()).HKTransform("000000")) 27 | .toThrow(new Error(ERROR_COMMON_CODE)); 28 | 29 | expect((new SinaCommonCodeTransform()).HKTransform("HK000000")) 30 | .toBe("hk000000"); 31 | }); 32 | 33 | it("美交所统一代码转换股票代码", async () => { 34 | expect(() => (new SinaCommonCodeTransform()).USTransform("000000")) 35 | .toThrow(new Error(ERROR_COMMON_CODE)); 36 | 37 | expect((new SinaCommonCodeTransform()).USTransform("US000000")) 38 | .toBe("gb_000000"); 39 | }); 40 | 41 | it("交易所统一代码转换股票代码", async () => { 42 | expect((new SinaCommonCodeTransform()).transform("SZ000000")) 43 | .toBe("sz000000"); 44 | 45 | expect((new SinaCommonCodeTransform()).transform("SH000000")) 46 | .toBe("sh000000"); 47 | 48 | expect((new SinaCommonCodeTransform()).transform("HK000000")) 49 | .toBe("hk000000"); 50 | 51 | expect((new SinaCommonCodeTransform()).transform("US000000")) 52 | .toBe("gb_000000"); 53 | 54 | expect(() => (new SinaCommonCodeTransform()).transform("000000")) 55 | .toThrow(new Error(ERROR_COMMON_CODE)); 56 | }); 57 | 58 | it("交易所统一代码组转换股票代码组", async () => { 59 | expect((new SinaCommonCodeTransform()).transforms(["SZ000000"])) 60 | .toStrictEqual(["sz000000"]); 61 | 62 | expect((new SinaCommonCodeTransform()).transforms(["SH000000"])) 63 | .toStrictEqual(["sh000000"]); 64 | 65 | expect((new SinaCommonCodeTransform()).transforms(["HK000000"])) 66 | .toStrictEqual(["hk000000"]); 67 | 68 | expect((new SinaCommonCodeTransform()).transforms(["US000000"])) 69 | .toStrictEqual(["gb_000000"]); 70 | 71 | expect(() => (new SinaCommonCodeTransform()).transforms(["000000"])) 72 | .toThrow(new Error(ERROR_COMMON_CODE)); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/stocks/sina/transforms/stock.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const SinaStockTransform = require("stocks/sina/transforms/stock").default; 3 | 4 | describe("【新浪】股票数据转换测试", () => { 5 | // 上交所相关代码 6 | const SHCode = 'SH510500'; 7 | const SHBody = `var hq_str_sh510500="500ETF,7.147,7.149,7.224,7.280,7.085,7.224,7.225,272478144,1964297124.000,6300,7.224,32900,7.223,37000,7.222,28600,7.221,14300,7.220,389300,7.225,88400,7.226,32300,7.227,572800,7.228,19000,7.229,2020-07-31,15:00:00,00,";` 8 | const SHRow = SHBody.split(";\n")[0]; 9 | const SHParams = SHRow.split('=').reverse()[0].replace('"', '').split(","); 10 | 11 | // 深交所 12 | const SZCode = 'SZ399001'; 13 | const SZBody = `var hq_str_sz399001="深证成指,13464.207,13466.854,13637.883,13748.034,13399.859,0.000,0.000,48559923962,650202505377.656,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,2020-07-31,15:00:03,00";`; 14 | const SZRow = SZBody.split(";\n")[0]; 15 | const SZParams = SZRow.split('=').reverse()[0].replace('"', '').split(","); 16 | 17 | // 港交所 18 | const HKCode = 'HKHSI'; 19 | const HKBody = `var hq_str_hkHSI="Hang Seng Index,恒生指数,24747.29,24710.59,24938.85,24534.79,24595.35,-115.24,-0.47,0.000,0.000,119153830,0,0.000,0.00,29174.92,21139.26,2020/07/31,16:08";`; 20 | const HKRow = HKBody.split(";\n")[0]; 21 | const HKParams = HKRow.split('=').reverse()[0].replace('"', '').split(","); 22 | 23 | // 美交所 24 | const USCode = 'USDJI'; 25 | const USBody = `var hq_str_gb_dji="道琼斯,26428.3203,0.44,2020-08-01 05:07:40,114.6700,26409.3301,26440.0195,26013.5898,29568.5703,18213.6504,491372564,397524206,0,0.00,--,0.00,0.00,0.00,0.00,0,0,0.0000,0.00,0.0000,,Jul 31 05:07PM EDT,26313.6504,0,1,2020";`; 26 | const USRow = USBody.split(";\n")[0]; 27 | const USParams = USRow.split('=').reverse()[0].replace('"', '').split(","); 28 | 29 | it("获取股票代码", async () => { 30 | expect(new SinaStockTransform(SHCode, SHParams).getCode()).toBe("SH510500"); 31 | expect(new SinaStockTransform(SZCode, SZParams).getCode()).toBe("SZ399001"); 32 | expect(new SinaStockTransform(HKCode, HKParams).getCode()).toBe("HKHSI"); 33 | expect(new SinaStockTransform(USCode, USParams).getCode()).toBe("USDJI"); 34 | expect(new SinaStockTransform("000000", []).getCode()).toBe("000000"); 35 | }); 36 | 37 | it("获取股票名称", async () => { 38 | expect(new SinaStockTransform(SHCode, SHParams).getName()).toBe('500ETF'); 39 | expect(new SinaStockTransform(SZCode, SZParams).getName()).toBe('深证成指'); 40 | expect(new SinaStockTransform(HKCode, HKParams).getName()).toBe('恒生指数'); 41 | expect(new SinaStockTransform(USCode, USParams).getName()).toBe("道琼斯"); 42 | }); 43 | 44 | it("获取股票现价", async () => { 45 | expect(new SinaStockTransform(SHCode, SHParams).getNow()).toBe(7.224); 46 | expect(new SinaStockTransform(SZCode, SZParams).getNow()).toBe(13637.883); 47 | expect(new SinaStockTransform(HKCode, HKParams).getNow()).toBe(24595.35); 48 | expect(new SinaStockTransform(USCode, USParams).getNow()).toBe(26428.3203); 49 | }); 50 | 51 | it("获取股票最低价", async () => { 52 | expect(new SinaStockTransform(SHCode, SHParams).getLow()).toBe(7.085); 53 | expect(new SinaStockTransform(SZCode, SZParams).getLow()).toBe(13399.859); 54 | expect(new SinaStockTransform(HKCode, HKParams).getLow()).toBe(24534.79); 55 | expect(new SinaStockTransform(USCode, USParams).getLow()).toBe(26013.5898); 56 | }); 57 | 58 | it("获取股票最高价", async () => { 59 | expect(new SinaStockTransform(SHCode, SHParams).getHigh()).toBe(7.28); 60 | expect(new SinaStockTransform(SZCode, SZParams).getHigh()).toBe(13748.034); 61 | expect(new SinaStockTransform(HKCode, HKParams).getHigh()).toBe(24938.85); 62 | expect(new SinaStockTransform(USCode, USParams).getHigh()).toBe(26440.0195); 63 | }); 64 | 65 | it("获取股票昨日收盘价", async () => { 66 | expect(new SinaStockTransform(SHCode, SHParams).getYesterday()).toBe(7.149); 67 | expect(new SinaStockTransform(SZCode, SZParams).getYesterday()).toBe(13466.854); 68 | expect(new SinaStockTransform(HKCode, HKParams).getYesterday()).toBe(24710.59); 69 | expect(new SinaStockTransform(USCode, USParams).getYesterday()).toBe(26313.6504); 70 | }); 71 | 72 | it("获取股票涨跌", async () => { 73 | expect(new SinaStockTransform(SHCode, SHParams).getPercent()).toBe(7.224 / 7.149 - 1); 74 | expect(new SinaStockTransform(SZCode, SZParams).getPercent()).toBe(13637.883 / 13466.854 - 1); 75 | expect(new SinaStockTransform(HKCode, HKParams).getPercent()).toBe(24595.35 / 24710.59 - 1); 76 | expect(new SinaStockTransform(USCode, USParams).getPercent()).toBe(26428.3203 / 26313.6504 - 1); 77 | }); 78 | 79 | it("获取股票数据", async () => { 80 | expect(new SinaStockTransform(SHCode, SHParams).getStock()).toStrictEqual({ code: "SH510500", name: "500ETF", percent: 7.224 / 7.149 - 1, now: 7.224, low: 7.085, high: 7.28, yesterday: 7.149 }); 81 | expect(new SinaStockTransform(SZCode, SZParams).getStock()).toStrictEqual({ code: 'SZ399001', name: '深证成指', percent: 13637.883 / 13466.854 - 1, now: 13637.883, low: 13399.859, high: 13748.034, yesterday: 13466.854 }); 82 | expect(new SinaStockTransform(HKCode, HKParams).getStock()).toStrictEqual({ code: 'HKHSI', name: '恒生指数', percent: 24595.35 / 24710.59 - 1, now: 24595.35, low: 24534.79, high: 24938.85, yesterday: 24710.59 }); 83 | expect(new SinaStockTransform(USCode, USParams).getStock()).toStrictEqual({ code: 'USDJI', name: '道琼斯', percent: 26428.3203 / 26313.6504 - 1, now: 26428.3203, low: 26013.5898, high: 26440.0195, yesterday: 26313.6504 }); 84 | }); 85 | }); 86 | 87 | -------------------------------------------------------------------------------- /test/stocks/tencent/index.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const Tencent = require("stocks/tencent").default; 3 | 4 | describe("【腾讯】股票代码接口", () => { 5 | const { ERROR_UNDEFINED_SEARCH_STOCK } = require("utils/constant"); 6 | it("基础测试", async () => { 7 | await expect(true).toEqual(true); 8 | }); 9 | 10 | it("需要获取的股票代码", async () => { 11 | await expect(Tencent.getStock("SH510500")).resolves.toMatchObject({ 12 | code: "SH510500", 13 | name: "中证500ETF", 14 | }); 15 | 16 | await expect(Tencent.getStock("SZ510500")).resolves.toMatchObject({ 17 | code: "SZ510500", 18 | name: "---", 19 | }); 20 | }); 21 | 22 | it("需要获取的股票代码组", async () => { 23 | await expect(Tencent.getStocks(["SH510500"])).resolves.toEqual( 24 | expect.arrayContaining([ 25 | expect.objectContaining({ code: "SH510500", name: "中证500ETF" }), 26 | ]) 27 | ); 28 | 29 | await expect(Tencent.getStocks(["SZ510500"])).resolves.toEqual( 30 | expect.arrayContaining([ 31 | expect.objectContaining({ code: "SZ510500", name: "---" }), 32 | ]) 33 | ); 34 | 35 | await expect(Tencent.getStocks([])).resolves.toEqual([]); 36 | }); 37 | 38 | it("搜索股票代码", async () => { 39 | await expect(Tencent.searchStocks("格力电器")).resolves.toEqual( 40 | expect.arrayContaining([ 41 | expect.objectContaining({ code: "SZ000651", name: "格力电器" }), 42 | ]) 43 | ); 44 | 45 | await expect(Tencent.searchStocks("贵州茅台")).resolves.toEqual( 46 | expect.arrayContaining([ 47 | expect.objectContaining({ code: "SH600519", name: "贵州茅台" }), 48 | ]) 49 | ); 50 | 51 | await expect(Tencent.searchStocks("安踏体育")).resolves.toEqual( 52 | expect.arrayContaining([ 53 | expect.objectContaining({ code: "HK02020", name: "安踏体育" }), 54 | expect.objectContaining({ code: "USANPDY", name: "安踏体育" }), 55 | ]) 56 | ); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/stocks/tencent/transforms/api-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const TencentApiCodeTransform = require("stocks/tencent/transforms/api-code").default; 3 | 4 | describe("【腾讯】股票代码转换统一代码", () => { 5 | const { 6 | ERROR_API_CODE, 7 | } = require("utils/constant"); 8 | 9 | it("深交所股票代码转换统一代码", async () => { 10 | expect(() => (new TencentApiCodeTransform()).SZTransform("STOCKAPI")) 11 | .toThrow(new Error(ERROR_API_CODE)); 12 | 13 | expect((new TencentApiCodeTransform()).SZTransform("sz000000")) 14 | .toBe("SZ000000"); 15 | }); 16 | 17 | it("上交所股票代码转换统一代码", async () => { 18 | expect(() => (new TencentApiCodeTransform()).SHTransform("STOCKAPI")) 19 | .toThrow(new Error(ERROR_API_CODE)); 20 | 21 | expect((new TencentApiCodeTransform()).SHTransform("sh000000")) 22 | .toBe("SH000000"); 23 | }); 24 | 25 | it("港交所股票代码转换统一代码", async () => { 26 | expect(() => (new TencentApiCodeTransform()).HKTransform("STOCKAPI")) 27 | .toThrow(new Error(ERROR_API_CODE)); 28 | 29 | expect((new TencentApiCodeTransform()).HKTransform("hk000000")) 30 | .toBe("HK000000"); 31 | }); 32 | 33 | it("美交所股票代码转换统一代码", async () => { 34 | expect(() => (new TencentApiCodeTransform()).USTransform("STOCKAPI")) 35 | .toThrow(new Error(ERROR_API_CODE)); 36 | 37 | expect((new TencentApiCodeTransform()).USTransform("us000000")) 38 | .toBe("US000000"); 39 | }); 40 | 41 | it("交易所股票代码转换统一代码", async () => { 42 | expect((new TencentApiCodeTransform()).transform("sz000000")) 43 | .toBe("SZ000000"); 44 | 45 | expect((new TencentApiCodeTransform()).transform("sh000000")) 46 | .toBe("SH000000"); 47 | 48 | expect((new TencentApiCodeTransform()).transform("hk000000")) 49 | .toBe("HK000000"); 50 | 51 | expect((new TencentApiCodeTransform()).transform("us000000")) 52 | .toBe("US000000"); 53 | 54 | expect(() => (new TencentApiCodeTransform()).transform("STOCKAPI")) 55 | .toThrow(new Error(ERROR_API_CODE)); 56 | }); 57 | 58 | it("交易所股票代码组转换统一代码组", async () => { 59 | expect((new TencentApiCodeTransform()).transforms(["sz000000"])) 60 | .toStrictEqual(["SZ000000"]); 61 | 62 | expect((new TencentApiCodeTransform()).transforms(["sh000000"])) 63 | .toStrictEqual(["SH000000"]); 64 | 65 | expect((new TencentApiCodeTransform()).transforms(["hk000000"])) 66 | .toStrictEqual(["HK000000"]); 67 | 68 | expect((new TencentApiCodeTransform()).transforms(["us000000"])) 69 | .toStrictEqual(["US000000"]); 70 | 71 | expect(() => (new TencentApiCodeTransform()).transforms(["STOCKAPI"])) 72 | .toThrow(new Error(ERROR_API_CODE)); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/stocks/tencent/transforms/common-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const TencentCommonCodeTransform = require("stocks/tencent/transforms/common-code").default; 3 | 4 | describe("【腾讯】统一代码转换股票代码", () => { 5 | const { 6 | ERROR_COMMON_CODE, 7 | } = require("utils/constant"); 8 | 9 | it("深交所统一代码转换股票代码", async () => { 10 | expect(() => (new TencentCommonCodeTransform()).SZTransform("000000")) 11 | .toThrow(new Error(ERROR_COMMON_CODE)); 12 | 13 | expect((new TencentCommonCodeTransform()).SZTransform("SZ000000")) 14 | .toBe("sz000000"); 15 | }); 16 | 17 | it("上交所统一代码转换股票代码", async () => { 18 | expect(() => (new TencentCommonCodeTransform()).SHTransform("000000")) 19 | .toThrow(new Error(ERROR_COMMON_CODE)); 20 | 21 | expect((new TencentCommonCodeTransform()).SHTransform("SH000000")) 22 | .toBe("sh000000"); 23 | }); 24 | 25 | it("港交所统一代码转换股票代码", async () => { 26 | expect(() => (new TencentCommonCodeTransform()).HKTransform("000000")) 27 | .toThrow(new Error(ERROR_COMMON_CODE)); 28 | 29 | expect((new TencentCommonCodeTransform()).HKTransform("HK000000")) 30 | .toBe("hk000000"); 31 | }); 32 | 33 | it("美交所统一代码转换股票代码", async () => { 34 | expect(() => (new TencentCommonCodeTransform()).USTransform("000000")) 35 | .toThrow(new Error(ERROR_COMMON_CODE)); 36 | 37 | expect((new TencentCommonCodeTransform()).USTransform("US000000")) 38 | .toBe("us000000"); 39 | }); 40 | 41 | it("交易所统一代码转换股票代码", async () => { 42 | expect((new TencentCommonCodeTransform()).transform("SZ000000")) 43 | .toBe("sz000000"); 44 | 45 | expect((new TencentCommonCodeTransform()).transform("SH000000")) 46 | .toBe("sh000000"); 47 | 48 | expect((new TencentCommonCodeTransform()).transform("HK000000")) 49 | .toBe("hk000000"); 50 | 51 | expect((new TencentCommonCodeTransform()).transform("US000000")) 52 | .toBe("us000000"); 53 | 54 | expect(() => (new TencentCommonCodeTransform()).transform("000000")) 55 | .toThrow(new Error(ERROR_COMMON_CODE)); 56 | }); 57 | 58 | it("交易所统一代码组转换股票代码组", async () => { 59 | expect((new TencentCommonCodeTransform()).transforms(["SZ000000"])) 60 | .toStrictEqual(["sz000000"]); 61 | 62 | expect((new TencentCommonCodeTransform()).transforms(["SH000000"])) 63 | .toStrictEqual(["sh000000"]); 64 | 65 | expect((new TencentCommonCodeTransform()).transforms(["HK000000"])) 66 | .toStrictEqual(["hk000000"]); 67 | 68 | expect((new TencentCommonCodeTransform()).transforms(["US000000"])) 69 | .toStrictEqual(["us000000"]); 70 | 71 | expect(() => (new TencentCommonCodeTransform()).transforms(["000000"])) 72 | .toThrow(new Error(ERROR_COMMON_CODE)); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/stocks/tencent/transforms/stock.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const TencentStockTransform = require("stocks/tencent/transforms/stock").default; 3 | 4 | describe("【腾讯】股票数据转换测试", () => { 5 | const code = 'SH510500'; 6 | const body = `v_sh510500="1~500ETF~510500~7.224~7.149~7.147~2724781~1351032~1373749~7.224~63~7.223~329~7.222~370~7.221~286~7.220~143~7.225~3893~7.226~884~7.227~323~7.228~5728~7.229~190~~20200731154041~0.075~1.05~7.280~7.085~7.224/2724781/1964297124~2724781~196430~~~~7.280~7.085~2.73~~~0.00~7.864~6.434~1.20~-9827~7.209~~~~~~196429.7124~0.0000~0~ ~ETF~26.51~~~~";` 7 | 8 | const rows = body.split(";\n"); 9 | const row = rows[0]; 10 | 11 | const [_, paramsUnformat] = row.split('='); 12 | const params = paramsUnformat.replace('"', '').split("~"); 13 | 14 | it("获取股票代码", async () => { 15 | expect(new TencentStockTransform(code, params).getCode()) 16 | .toBe('SH510500'); 17 | }); 18 | 19 | it("获取股票名称", async () => { 20 | expect(new TencentStockTransform(code, params).getName()) 21 | .toBe('500ETF'); 22 | }); 23 | 24 | it("获取股票现价", async () => { 25 | expect(new TencentStockTransform(code, params).getNow()) 26 | .toBe(7.224); 27 | }); 28 | 29 | it("获取股票最低价", async () => { 30 | expect(new TencentStockTransform(code, params).getLow()) 31 | .toBe(7.085); 32 | }); 33 | 34 | it("获取股票最高价", async () => { 35 | expect(new TencentStockTransform(code, params).getHigh()) 36 | .toBe(7.28); 37 | }); 38 | 39 | it("获取股票昨日收盘价", async () => { 40 | expect(new TencentStockTransform(code, params).getYesterday()) 41 | .toBe(7.149); 42 | }); 43 | 44 | it("获取股票涨跌", async () => { 45 | expect(new TencentStockTransform(code, params).getPercent()) 46 | .toBe(7.224 / 7.149 - 1); 47 | }); 48 | 49 | it("获取股票数据", async () => { 50 | expect(new TencentStockTransform(code, params).getStock()) 51 | .toStrictEqual({ "code": "SH510500", "high": 7.28, "low": 7.085, "name": "500ETF", "now": 7.224, "percent": 7.224 / 7.149 - 1, "yesterday": 7.149 }); 52 | }); 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /test/stocks/xueqiu/index.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const Xueqiu = require("stocks/xueqiu").default; 3 | 4 | describe("【雪球】股票代码接口", () => { 5 | it("基础测试", async () => { 6 | await expect(true).toEqual(true); 7 | }); 8 | 9 | // it("获取 Token", async () => { 10 | // await expect(Xueqiu.getToken()) 11 | // .resolves 12 | // .toContain('xq_a_token'); 13 | // }); 14 | // it("需要获取的股票代码", async () => { 15 | // await expect(Xueqiu.getStock("SH510500")) 16 | // .resolves 17 | // .toMatchObject({ code: "SH510500", name: "中证500ETF" }); 18 | // await expect(Xueqiu.getStock("SZ510500")) 19 | // .resolves 20 | // .toMatchObject({ code: "SZ510500", name: "---" }); 21 | // }); 22 | // it("需要获取的股票代码组", async () => { 23 | // await expect(Xueqiu.getStocks(["SH510500"])) 24 | // .resolves 25 | // .toEqual(expect.arrayContaining([expect.objectContaining({ code: "SH510500", name: "中证500ETF" })])); 26 | // await expect(Xueqiu.getStocks(["SZ510500"])) 27 | // .resolves 28 | // .toEqual(expect.arrayContaining([expect.objectContaining({ code: "SZ510500", name: "---" })])); 29 | // await expect(Xueqiu.getStocks([])) 30 | // .resolves 31 | // .toEqual([]); 32 | // }); 33 | // it("搜索股票代码", async () => { 34 | // await expect(Xueqiu.searchStocks("格力电器")) 35 | // .resolves 36 | // .toEqual(expect.arrayContaining([expect.objectContaining({ code: "SZ000651", name: "格力电器" })])); 37 | // await expect(Xueqiu.searchStocks("贵州茅台")) 38 | // .resolves 39 | // .toEqual(expect.arrayContaining([expect.objectContaining({ code: "SH600519", name: "贵州茅台" })])); 40 | // await expect(Xueqiu.searchStocks("安踏体育")) 41 | // .resolves 42 | // .toEqual(expect.arrayContaining([ 43 | // expect.objectContaining({ code: "HK02020", name: "安踏体育" }), 44 | // expect.objectContaining({ code: "USANPDY", name: "安踏体育ADR" }), 45 | // ])); 46 | // }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/stocks/xueqiu/transforms/api-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const XueqiuApiCodeTransform = require("stocks/xueqiu/transforms/api-code").default; 3 | 4 | describe("【雪球】股票代码转换统一代码", () => { 5 | const { 6 | ERROR_API_CODE, 7 | } = require("utils/constant"); 8 | 9 | it("深交所股票代码转换统一代码", async () => { 10 | expect(() => (new XueqiuApiCodeTransform()).SZTransform("000000")) 11 | .toThrow(new Error(ERROR_API_CODE)); 12 | 13 | expect((new XueqiuApiCodeTransform()).SZTransform("SZ000000")) 14 | .toBe("SZ000000"); 15 | }); 16 | 17 | it("上交所股票代码转换统一代码", async () => { 18 | expect(() => (new XueqiuApiCodeTransform()).SHTransform("000000")) 19 | .toThrow(new Error(ERROR_API_CODE)); 20 | 21 | expect((new XueqiuApiCodeTransform()).SHTransform("SH000000")) 22 | .toBe("SH000000"); 23 | }); 24 | 25 | it("港交所股票代码转换统一代码", async () => { 26 | expect(() => (new XueqiuApiCodeTransform()).HKTransform("000000")) 27 | .toThrow(new Error(ERROR_API_CODE)); 28 | 29 | expect((new XueqiuApiCodeTransform()).HKTransform("HK000000")) 30 | .toBe("HK000000"); 31 | }); 32 | 33 | it("美交所股票代码转换统一代码", async () => { 34 | expect(() => (new XueqiuApiCodeTransform()).USTransform("SZ000000")) 35 | .toThrow(new Error(ERROR_API_CODE)); 36 | 37 | expect((new XueqiuApiCodeTransform()).USTransform("000000")) 38 | .toBe("US000000"); 39 | }); 40 | 41 | it("交易所股票代码转换统一代码", async () => { 42 | expect((new XueqiuApiCodeTransform()).transform("SZ000000")) 43 | .toBe("SZ000000"); 44 | 45 | expect((new XueqiuApiCodeTransform()).transform("SH000000")) 46 | .toBe("SH000000"); 47 | 48 | expect((new XueqiuApiCodeTransform()).transform("HK000000")) 49 | .toBe("HK000000"); 50 | 51 | expect((new XueqiuApiCodeTransform()).transform("000000")) 52 | .toBe("US000000"); 53 | }); 54 | 55 | it("交易所股票代码组转换统一代码组", async () => { 56 | expect((new XueqiuApiCodeTransform()).transforms(["SZ000000"])) 57 | .toStrictEqual(["SZ000000"]); 58 | 59 | expect((new XueqiuApiCodeTransform()).transforms(["SH000000"])) 60 | .toStrictEqual(["SH000000"]); 61 | 62 | expect((new XueqiuApiCodeTransform()).transforms(["HK000000"])) 63 | .toStrictEqual(["HK000000"]); 64 | 65 | expect((new XueqiuApiCodeTransform()).transforms(["000000"])) 66 | .toStrictEqual(["US000000"]); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/stocks/xueqiu/transforms/common-code.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const XueqiuCommonCodeTransform = require("stocks/xueqiu/transforms/common-code").default; 3 | 4 | describe("【雪球】统一代码转换股票代码", () => { 5 | const { 6 | ERROR_COMMON_CODE, 7 | } = require("utils/constant"); 8 | 9 | it("深交所统一代码转换股票代码", async () => { 10 | expect(() => (new XueqiuCommonCodeTransform()).SZTransform("000000")) 11 | .toThrow(new Error(ERROR_COMMON_CODE)); 12 | 13 | expect((new XueqiuCommonCodeTransform()).SZTransform("SZ000000")) 14 | .toBe("SZ000000"); 15 | }); 16 | 17 | it("上交所统一代码转换股票代码", async () => { 18 | expect(() => (new XueqiuCommonCodeTransform()).SHTransform("000000")) 19 | .toThrow(new Error(ERROR_COMMON_CODE)); 20 | 21 | expect((new XueqiuCommonCodeTransform()).SHTransform("SH000000")) 22 | .toBe("SH000000"); 23 | }); 24 | 25 | it("港交所统一代码转换股票代码", async () => { 26 | expect(() => (new XueqiuCommonCodeTransform()).HKTransform("000000")) 27 | .toThrow(new Error(ERROR_COMMON_CODE)); 28 | 29 | expect((new XueqiuCommonCodeTransform()).HKTransform("HK000000")) 30 | .toBe("HK000000"); 31 | }); 32 | 33 | it("美交所统一代码转换股票代码", async () => { 34 | expect(() => (new XueqiuCommonCodeTransform()).USTransform("000000")) 35 | .toThrow(new Error(ERROR_COMMON_CODE)); 36 | 37 | expect((new XueqiuCommonCodeTransform()).USTransform("US000000")) 38 | .toBe("000000"); 39 | }); 40 | 41 | it("交易所统一代码转换股票代码", async () => { 42 | expect((new XueqiuCommonCodeTransform()).transform("SZ000000")) 43 | .toBe("SZ000000"); 44 | 45 | expect((new XueqiuCommonCodeTransform()).transform("SH000000")) 46 | .toBe("SH000000"); 47 | 48 | expect((new XueqiuCommonCodeTransform()).transform("HK000000")) 49 | .toBe("HK000000"); 50 | 51 | expect((new XueqiuCommonCodeTransform()).transform("US000000")) 52 | .toBe("000000"); 53 | 54 | expect(() => (new XueqiuCommonCodeTransform()).transform("000000")) 55 | .toThrow(new Error(ERROR_COMMON_CODE)); 56 | }); 57 | 58 | it("交易所统一代码组转换股票代码组", async () => { 59 | expect((new XueqiuCommonCodeTransform()).transforms(["SZ000000"])) 60 | .toStrictEqual(["SZ000000"]); 61 | 62 | expect((new XueqiuCommonCodeTransform()).transforms(["SH000000"])) 63 | .toStrictEqual(["SH000000"]); 64 | 65 | expect((new XueqiuCommonCodeTransform()).transforms(["HK000000"])) 66 | .toStrictEqual(["HK000000"]); 67 | 68 | expect((new XueqiuCommonCodeTransform()).transforms(["US000000"])) 69 | .toStrictEqual(["000000"]); 70 | 71 | expect(() => (new XueqiuCommonCodeTransform()).transforms(["000000"])) 72 | .toThrow(new Error(ERROR_COMMON_CODE)); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/stocks/xueqiu/transforms/stock.test.ts: -------------------------------------------------------------------------------- 1 | // Stock 2 | const XueqiuStockTransform = require("stocks/xueqiu/transforms/stock").default; 3 | 4 | describe("【雪球】股票数据转换测试", () => { 5 | const code = 'SH510500'; 6 | const body = `{"data":{"market":{"status_id":8,"region":"CN","status":"休市","time_zone":"Asia/Shanghai","time_zone_desc":null},"quote":{"symbol":"SH510500","code":"510500","avg_price":6.982,"delayed":0,"type":13,"percent":0.5,"tick_size":0.001,"float_shares":null,"amplitude":null,"current":7.224,"high":7.28,"current_year_percent":22.38,"float_market_capital":null,"issue_date":1363276800000,"low": 7.085,"sub_type":"EBS","market_capital":4.25501735904E10,"currency":"CNY","lot_size":100,"lock_set":null,"timestamp":1595207715950,"amount":null,"chg":0.035,"last_close":7.149,"volume":null,"turnover_rate":null,"name":"中证500ETF","exchange":"SH","time":1595207715000,"total_shares":6094267200,"open":null,"status":1},"others":{"pankou_ratio":null,"cyb_switch":false},"tags":[{"description":"融","value":6},{"description":"空","value":7}]},"error_code":0,"error_description":""}` 7 | 8 | const params = JSON.parse(body).data.quote; 9 | 10 | it("获取股票代码", async () => { 11 | expect(new XueqiuStockTransform(code, params).getCode()) 12 | .toBe('SH510500'); 13 | }); 14 | 15 | it("获取股票名称", async () => { 16 | expect(new XueqiuStockTransform(code, params).getName()) 17 | .toBe('中证500ETF'); 18 | }); 19 | 20 | it("获取股票现价", async () => { 21 | expect(new XueqiuStockTransform(code, params).getNow()) 22 | .toBe(7.224); 23 | }); 24 | 25 | it("获取股票最低价", async () => { 26 | expect(new XueqiuStockTransform(code, params).getLow()) 27 | .toBe(7.085); 28 | }); 29 | 30 | it("获取股票最高价", async () => { 31 | expect(new XueqiuStockTransform(code, params).getHigh()) 32 | .toBe(7.28); 33 | }); 34 | 35 | it("获取股票昨日收盘价", async () => { 36 | expect(new XueqiuStockTransform(code, params).getYesterday()) 37 | .toBe(7.149); 38 | }); 39 | 40 | it("获取股票涨跌", async () => { 41 | expect(new XueqiuStockTransform(code, params).getPercent()) 42 | .toBe(7.224 / 7.149 - 1); 43 | }); 44 | 45 | it("获取股票数据", async () => { 46 | expect(new XueqiuStockTransform(code, params).getStock()) 47 | .toStrictEqual({ "code": "SH510500", "high": 7.28, "low": 7.085, "name": "中证500ETF", "now": 7.224, "percent": 7.224 / 7.149 - 1, "yesterday": 7.149 }); 48 | }); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es6" 7 | ], 8 | "declaration": true, 9 | "outDir": "./dist", 10 | "strict": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true 13 | }, 14 | "include": [ 15 | "src" 16 | ], 17 | "exclude": [ 18 | "node_modules", 19 | "**/__tests__/*" 20 | ] 21 | } --------------------------------------------------------------------------------