Full Code of waditu/tushare.js for AI

master 8668247bd766 cached
38 files
64.1 KB
23.3k tokens
16 symbols
1 requests
Download .txt
Repository: waditu/tushare.js
Branch: master
Commit: 8668247bd766
Files: 38
Total size: 64.1 KB

Directory structure:
gitextract_ojxgstif/

├── .babelrc
├── .eslintrc
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── README.md
├── example/
│   └── getkdata/
│       ├── getk.js
│       └── merge.js
├── package.json
├── src/
│   ├── index.js
│   ├── stock/
│   │   ├── billboard.js
│   │   ├── classifying.js
│   │   ├── cons.js
│   │   ├── index.js
│   │   ├── trading.js
│   │   ├── urls.js
│   │   └── util.js
│   └── utils/
│       ├── charset.js
│       ├── dateu.js
│       └── fetch.js
└── test/
    ├── billboardDZJYTest.js
    ├── billboardLHBTest.js
    ├── billboardRankTest.js
    ├── classifyingAllStocksTest.js
    ├── classifyingConceptsTest.js
    ├── classifyingDetailsTest.js
    ├── classifyingHS300Test.js
    ├── classifyingIndustryTest.js
    ├── classifyingSZ50Test.js
    ├── classifyingXSG.js
    ├── getkdata.js
    ├── stockHistoryTest.js
    ├── stockIndexTest.js
    ├── stockLiveDataTest.js
    ├── stockSinaDDTest.js
    ├── stockTickTest.js
    ├── stockTodayTest.js
    └── stockTodayTickTest.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": ["es2015", "stage-2"]
}



================================================
FILE: .eslintrc
================================================
{
  "extends": "airbnb",
  "parserOptions": {
    "ecmaVersion": 6,
    "sourceType": module,
    "ecmaFeatures": {
      "jsx": true,
      "experimentalObjectRestSpread": true
    },
  },
  "env": {
    "browser": true,
    "node": true
  },
  "rules": {
    "arrow-parens": ["error", "as-needed"],
    "comma-dangle": ["error", "always-multiline"],
    "dot-notation": 0,
    "guard-for-in": 0,
    "no-underscore-dangle": 0,
    "no-use-before-define": [2, "nofunc"],
    "space-before-function-paren": [2, "never"],
    "import/no-extraneous-dependencies": ["error", {"devDependencies": true, "optionalDependencies": false, "peerDependencies": false}],
    "import/prefer-default-export": 0
  }
}


================================================
FILE: .gitignore
================================================
node_modules
.env
dist
lib
libexample
.idea
*.iml
coverage
.nyc_output


================================================
FILE: .npmignore
================================================
src/


================================================
FILE: CHANGELOG.md
================================================
# v0.3.1

> 获取指定年月限售解禁股数据

```
stock.getXSGData()
```

# v0.3.0

> 目标同时支持browser和nodejs环境,因此改用fetch替换掉superagent.

### 这个比较大的改动:
使用fetch,因为接口返回改为promise,而不是之前的callback



================================================
FILE: README.md
================================================
# tushare.js

这是[tushare](http://tushare.org/)的nodejs版,正在开发当中。如果有感兴趣的同学,非常欢迎一起完善该项目。

tushare的数据大多来自于各大网站股票金融频道的数据。本质上是去抓取这些网站的数据,以此方便二次使用。文档目前并不完善,使用方法(接口)
请参考`test/`目录内的单元测试

### 使用方法:
```
npm install tushare --save
npm run build
npm run test
```

然后:
```
import { stock } from 'tushare';

stock.getTodayAll().then(({ data }) => {
  console.log(data);
});

```

## 交易数据

## 1. 获取个股历史数据
```
const options = {
  code: '600848',
  ktype: 'week'
};
stock.getHistory(options).then(({ data }) => {
  console.log(data);
});

```

`options` 参数说明:
```
{
code: {String} 股票代码,6位数字代码
ktype: {String} 数据类型,day=日k线 week=周 month=月 5=5分钟 15=15分钟 30=30分钟 60=60分钟,默认为day
start: {String} 开始日期,格式YYYY-MM-DD
end: {String} 结束日期,格式YYYY-MM-DD
autype: {String} 复权,默认前复权(fq), 不复权(last)
}
```

## 2. 获取历史分笔数据
```
const options = {
  code: '600848',
  date: '2015-12-31'
};
stock.getTick(options).then(({ data }) => {
  console.log(data);
});
```

`options` 参数说明:
```
{
  date: {String} 历史分笔日期,格式YYYY-MM-DD
  code: {String} 股票代码,6位数字代码
}
```

### 获取历史K线数据
```
const options = {
  code: '000001',
  index: true,
  start : '2015-01-01',
  end: '2016-10-22',
  args: '000001'
};
stock.getKData(options).then(data => {
   console.log('code %s',options.code);
   console.log(data);  
  })
  .catch(err => {
    console.error('get %s error %s', options.code, err);
  });
```

`options` 参数说明:
```
{
  start: {string} 起始时间,格式YYYY-MM-DD
  end: {string} 结束时间,格式YYYY-MM-DD
  code: {string} 股票代码
  ktype: {string} K线类型 ('day','week','month','5','10','15','30','60')
  index: {bool}  表示是否是指数,默认是否
}
```

## 3. 实时行情
```
stock.getTodayAll().then(({ data }) => {
  console.log(data);
});
```

`options(可选)` 参数说明:
```
{
  pageSize: 设置单次返回股票的数量,默认10000,即全部股票
  pageNo: 页码,都懂得
}
```

## 4. 实时分笔
```
var options = {
  codes: [
    '600848',
    '600000'
  ]
};
stock.getLiveData(options).then(({ data }) => {
  console.log(data);
});
```

`options` 参数说明:
```
{
  codes: 股票代码数组
}
```

## 5. 当日历史分笔
>该方法返回指定时间前五分钟的分笔数据,如需获得所有数据,需要多次调用该方法

```
var options = {
  code: '600848',
  end: '15:00:00'
};
stock.getTodayTick(options).then(({ data }) => {
  console.log(data);
});
```

`options` 参数说明:
```
{
  code: 股票代码,6位数字
  end: 结束时间
}
```

## 6. 大盘指数行情数据
```
stock.getIndex().then(({ data }) => {
  console.log(data);
});
```

## 7. 大单交易数据
```
var options = {
  code: '600848',
  volume: 700
};
stock.getSinaDD(options).then(({ data }) => {
  console.log(data);
});
```

`options` 参数说明:
```
{
  code: 股票代码,6位数字
  volume: (手)默认400,返回大于xx手的大单数据
}
```

## 行业分类数据

## 1. 获取新浪行业分类信息
比如:房地产、电子信息、钢铁行业等等新浪行业分类信息
```
stock.getSinaIndustryClassified().then(({ data }) => {
  console.log(data);
});
```

## 2. 获取新浪某个行业分类的具体信息:所包含的股票及其交易信息
这里的tag是分类行业分类的tag,可以从上一个接口:tushare.stock.getSinaIndustryClassified获得
```
var options = {
  tag: 'new_jrhy'
};
stock.getSinaClassifyDetails(options).then(({ data }) => {
  console.log(data);
});
```

`options` 参数说明:
```
{
  tag: 新浪行业代码
}
```

## 3. 获取新浪概念分类信息
返回数据中的tag可用于上面(#2)的接口,用于获取某个概念分类的具体信息
```
stock.getSinaConceptsClassified().then(({ data }) => {
  console.log(data);
});
```

## 4. 获取所有上市公司股票基本信息
```
stock.getAllStocks().then(({ data }) => {
  console.log(data);
});
```

## 5. 获取沪深300股票信息
```
stock.getHS300().then(({ data }) => {
  console.log(data);
});
```

## 6. 获取上证50股票信息
```
stock.getSZ50().then(({ data }) => {
  console.log(data);
});
```

## 7. 获取指定年月限售解禁股数据
```
stock.getXSGData().then(({ data }) => {
  console.log(data);
});
```

## 龙虎榜

## 1. 龙虎榜单(来自网易财经)
```
  var options = {
    start: '2016-01-15',
    end: '2016-01-15',
    pageNo: 1,
    pageSize: 150
  };
  stock.lhb(options).then(({ data }) => {
    console.log(data);
  });
```

`options` 参数说明:
```
{
start: 开始日期
end: 结束日期
pageNo: (optional, default: 1)
pageSize: optional, default: 150)
}
```

## 2. 大宗交易(来自网易财经)
```
  var options = {
    start: '2016-01-15',
    end: '2016-01-15',
    pageNo: 1,
    pageSize: 150
  };
  stock.blockTeade(options).then(({ data }) => {
    console.log(data);
  });
```

`options` 参数说明:
```
{
start: 开始日期
end: 结束日期
pageNo: (optional, default: 1)
pageSize: optional, default: 150)
}
```


================================================
FILE: example/getkdata/getk.js
================================================
/* eslint-disable no-console */
import { stock } from '../../lib';
import * as extargsparse from 'extargsparse';

const util = require('util');
const strftime = require('strftime');

const commandfmt = `
{
	"ktype|k" : "day",
	"autype|a" : "hfq",
	"index|i" : false,
	"start|s" : "%s",
	"end|e" : "%s",
	"$" : "+"
}`;

const nowtime = new Date();
const etime = new Date(nowtime.getTime() - 24 * 1 * 3600 * 1000);
const stime = new Date(etime.getTime() - 24 * 365 * 3600 * 1000);

const sdate = strftime('%Y-%m-%d',stime);
const edate = strftime('%Y-%m-%d',etime);

const command=util.format(commandfmt,sdate,edate);
const parser = extargsparse.ExtArgsParse();
parser.load_command_line_string(command);
const args = parser.parse_command_line();


args.args.forEach(function(code) {
	let options = {};
	options.code = code;
	options.start = args.start;
	options.end = args.end;
	options.ktype = args.ktype;
	options.autype = args.autype;
	options.isIndex = args.index;
	stock.getKData(options).then(data => {
		console.log('code %s',code);
		data.forEach(function(d) {
			console.log('%s',d);
		});
	})
	.catch(err => {
		console.error('get %s error %s', code, err);
	});
});


================================================
FILE: example/getkdata/merge.js
================================================
/* eslint-disable no-console */
import * as extargsparse from 'extargsparse';

const util = require('util');
const fs = require('fs');


const _getTimeTick = ts => {
  const sarr = ts.split('-');
  let retval = 0;
  if (sarr.length >= 3) {
    retval += parseInt(sarr[0], 10) * 100 * 100;
    retval += parseInt(sarr[1], 10) * 100;
    retval += parseInt(sarr[2], 10);
    retval *= 10000;
  } else {
    retval += parseInt(ts, 10);
  }
  return retval;
};

const _findStoreIndex = (arr1,arr2) => {
  let idx = 0;
  let minidx = 0;
  let maxidx = arr1.length - 1;
  let curidx = Math.floor((minidx + maxidx) / 2);
  let v1min;
  let v1max;
  let v1cur;
  let v2;

  while( minidx < maxidx ) {
    v1min = _getTimeTick(arr1[minidx][0]);
    v1max = _getTimeTick(arr1[maxidx][0]);
    v1cur = _getTimeTick(arr1[curidx][0]);
    v2 = _getTimeTick(arr2[0][0]);
    if (v1min >= v2) {
      idx = 0;
      break;
    } else if (v1max <= v2) {
      idx = (maxidx + 1);
      break;
    }

    if ((minidx + 1) >= maxidx) {
      /*this is the smallest one*/
      if (v1min < v2 && v1max > v2) {
        idx = (minidx);
        break;
      } else {
        idx = (maxidx + 1);
        break;
      }
    }

    if (v1cur < v2) {
      minidx = curidx;
    } else if (v1cur > v2) {
      maxidx = curidx;
    } else if (v1cur == v2) {
      idx = curidx;
      break;
    }
    
    curidx = Math.floor((minidx + maxidx) / 2);
  }
  return idx;
};

const _mergeArray = (arr1,arr2) => {
	let _idx = 0;
	_idx = _findStoreIndex(arr1,arr2);
	arr2.forEach(function(d) {
		arr1.splice(_idx,0,d);
		_idx += 1;
	});
	return arr1;
};

const commandline = `
{
	"$" : 2
}
`;

const parser = extargsparse.ExtArgsParse();
parser.load_command_line_string(commandline);
const args = parser.parse_command_line();

fs.readFile(args.args[0],function(err1,data1) {
	if (err1 !== undefined && err1 !== null) {
		console.error('can not read [%s] error %s', args.args[0], err1);
		process.exit(4);
	}
	fs.readFile(args.args[1],function(err2,data2) {
		let json1;
		let json2;
		let idx;
		let lastval,curval;
		if (err2 !== undefined && err2 !== null) {
			console.error('can not read [%s] error %s', args.args[1], err2);
			process.exit(4);
		}

		// console.log('(%s)', data1);
		json1 = JSON.parse(data1);
		json2 = JSON.parse(data2);
		_mergeArray(json1,json2);
		lastval = 0;

		json1.forEach(function(d) {
			console.log('%s',d);
			curval = _getTimeTick(d[0]);
			if (curval < lastval) {
				console.log('%s not right for %s', d, lastval);
			}
			lastval = curval;
		});
	});
});


================================================
FILE: package.json
================================================
{
  "name": "tushare",
  "version": "0.3.1",
  "description": "port of tushare for nodejs",
  "main": "lib/index.js",
  "scripts": {
    "test": "node ./node_modules/ava/cli.js --verbose test/*",
    "build": "node ./node_modules/babel-cli/bin/babel.js src --out-dir lib",
    "build:watch": "node ./node_modules/babel-cli/bin/babel.js src --out-dir lib --watch",
    "prepublish": "npm run build",
    "buildexample": "node ./node_modules/babel-cli/bin/babel.js example --out-dir libexample",
    "lint": "node ./node_modules/eslint/bin/eslint.js src/"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/ruanyl/tushare.js.git"
  },
  "keywords": [
    "github",
    "api"
  ],
  "author": "Ruan Yulong <ruanyu1@gmail.com> (http://github.com/ruanyl)",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/ruanyl/tushare.js/issues"
  },
  "homepage": "https://github.com/ruanyl/tushare.js#readme",
  "dependencies": {
    "async": "^2.4.1",
    "iconv-lite": "^0.4.15",
    "js-base64": "^2.1.9",
    "no-fetch": "^1.6.2",
    "ramda": "^0.23.0",
    "strftime": "^0.10.0",
    "whatwg-fetch": "^2.0.2"
  },
  "devDependencies": {
    "ava": "^0.18.1",
    "babel-cli": "^6.23.0",
    "babel-core": "^6.23.1",
    "babel-loader": "^6.3.0",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-polyfill": "^6.23.0",
    "babel-preset-es2015": "^6.22.0",
    "babel-preset-stage-2": "^6.22.0",
    "babel-runtime": "^6.22.0",
    "eslint": "^3.15.0",
    "eslint-config-airbnb": "^14.1.0",
    "eslint-plugin-import": "^2.2.0",
    "eslint-plugin-jsx-a11y": "^4.0.0",
    "eslint-plugin-react": "^6.9.0",
    "extargsparse": "^0.2.2"
  },
  "ava": {
    "require": [
      "babel-register"
    ]
  }
}


================================================
FILE: src/index.js
================================================
import * as stock from './stock';

export { stock };


================================================
FILE: src/stock/billboard.js
================================================
import { lhbUrl, blockTradeUrl, longPeriodRankUrl } from './urls';
import { codeToSymbol, checkStatus } from './util';
import { DATE_NOW } from './cons';
import '../utils/fetch';

/**
 * lhb - 获取龙虎榜数据
 * [
 *  {
 *    symbol: 股票代码
 *    name: 股票名称
 *    price: 收盘价格
 *    date: 日期哦
 *    changePercent: 涨跌幅
 *    type: 上榜理由
 *    volume: 成交量(手)
 *    amount: 成交额(万)
 *  }
 * ]
 */
export const lhb = (query = {}) => {
  const defaults = {
    start: DATE_NOW,
    end: DATE_NOW,
    pageNo: 1,
    pageSize: 150,
  };
  const options = Object.assign({}, defaults, query);
  const url = lhbUrl(options.start, options.end, options.pageNo, options.pageSize);
  const mapData = data => {
    const result = {};
    result.page = data.page + 1;
    result.total = data.total;
    result.pageCount = data.pagecount;
    result.items = data.list.map(item => ({
      symbol: codeToSymbol(item.SYMBOL),
      name: item.SNAME,
      price: item.TCLOSE,
      date: item.TDATE,
      changePercent: item.PCHG,
      type: item.SMEBTSTOCK1,
      volume: item.VOTURNOVER * 100,
      amount: item.VATURNOVER * 100,
    }));
    return { data: result };
  };

  return fetch(url)
  .then(checkStatus)
  .then(res => res.json())
  .then(mapData)
  .catch(error => ({ error }));
};

/**
 * blockTrade - 大宗交易
 * 返回数据格式:
 * [
 *  {
 *    symbol: 股票代码
 *    name: 股票名称
 *    dealPrice: 成交价
 *    price: 收盘价
 *    date: 日期
 *    volumn: 成交量(手)
 *    amout: 成交额(万)
 *    buyer: 买方
 *    seller: 卖方
 *    dopRate: 折溢价率
 *  }
 * ]
 */
export const blockTrade = (query = {}) => {
  const defaults = {
    start: DATE_NOW,
    end: DATE_NOW,
    pageNo: 1,
    pageSize: 150,
  };
  const options = Object.assign({}, defaults, query);
  const url = blockTradeUrl(options.start, options.end, options.pageNo, options.pageSize);
  const mapData = data => {
    const result = {};
    result.page = data.page + 1;
    result.total = data.total;
    result.pageCount = data.pagecount;
    result.items = data.list.map(item => ({
      symbol: codeToSymbol(item.SYMBOL),
      name: item.SNAME,
      dealPrice: item.DZJY5,
      price: item.TCLOSE,
      date: item.PUBLISHDATE,
      volume: item.DZJY2 * 100,
      amount: item.DZJY6,
      buyer: item.DZJY9,
      seller: item.DZJY11,
      dopRate: item.DZJY55,
    }));
    return { data: result };
  };

  return fetch(url)
  .then(checkStatus)
  .then(res => res.json())
  .then(mapData)
  .catch(error => ({ error }));
};

/**
 * longPeriodRank - 长期阶段涨跌幅
 * 返回数据格式:
 * [
 *  {
 *    symbol: 股票代码
 *    name: 股票名称
 *    price: 股票价格
 *    date: 时间
 *    dayPercent: 单日涨跌幅
 *    weekPercent: 周涨跌幅
 *    monthPercent: 月涨跌幅
 *    quarterPercent: 季度涨跌幅
 *    halfYearPercent: 半年涨跌幅
 *    yearPercent: 年度涨跌幅
 *  }
 * ]
 */
export const longPeriodRank = (query = {}) => {
  const defaults = {
    period: 'month',
    pageNo: 1,
    pageSize: 100,
  };
  const options = Object.assign({}, defaults, query);
  const url = longPeriodRankUrl(options.period, options.pageNo, options.pageSize);
  const mapData = data => {
    const result = {};
    result.page = data.page + 1;
    result.total = data.total;
    result.pageCount = data.pagecount;
    result.items = data.list.map(item => ({
      symbol: codeToSymbol(item.CODE),
      name: item.NAME,
      price: item.PRICE,
      date: item.LONG_PERIOD_RANK.TIME,
      dayPercent: item['PERCENT'],
      weekPercent: item['LONG_PERIOD_RANK']['WEEK_PERCENT'],
      monthPercent: item['LONG_PERIOD_RANK']['MONTH_PERCENT'],
      quarterPercent: item['LONG_PERIOD_RANK']['QUARTER_PERCENT'],
      halfYearPercent: item['LONG_PERIOD_RANK']['HALF_YEAR_PERCENT'],
      yearPercent: item['LONG_PERIOD_RANK']['YEAR_PERCENT'],
    }));
    return { data: result };
  };

  return fetch(url)
  .then(checkStatus)
  .then(res => res.json())
  .then(mapData)
  .catch(error => ({ error }));
};


================================================
FILE: src/stock/classifying.js
================================================
import {
  sinaIndustryIndexUrl,
  sinaClassifyDetailUrl,
  sinaConceptsIndexUrl,
  allStockUrl,
  hs300Url,
  sz50Url,
  xsgUrl,
} from './urls';
import { csvToObject, arrayObjectMapping, checkStatus } from './util';
import { charset } from '../utils/charset';
import '../utils/fetch';

/**
 * getSinaIndustryClassified: 获取新浪行业板块数据
 * 返回数据格式 - 数组,包含:
 * tag: 新浪行业分类标识
 * name: 新浪行业分类名称
 * num: 行业包含股票数量
 * price: 平均价
 * changePrice: 涨跌额
 * changePercent: 涨跌幅
 * volume: 总成交量(手)
 * amount: 总成交额(万)
 * leadingSymbol: 领涨股票代码
 * leadingChangePercent: 领涨股涨跌幅
 * leadingPrice: 领涨股价格
 * leadingChangePrice: 领涨股涨跌额
 * leadingName: 领涨股名称
 */
export const getSinaIndustryClassified = () => {
  const url = sinaIndustryIndexUrl();
  const mapData = data => {
    const result = [];
    const json = JSON.parse(data.split('=')[1].trim());
    Object.keys(json).forEach(tag => {
      const industryArr = json[tag].split(',');
      result.push({
        tag: industryArr[0],
        name: industryArr[1],
        num: industryArr[2],
        price: industryArr[3],
        changePrice: industryArr[4],
        changePercent: industryArr[5],
        volume: industryArr[6] / 100,
        amount: industryArr[7] / 10000,
        leadingSymbol: industryArr[8],
        leadingChangePercent: industryArr[9],
        leadingPrice: industryArr[10],
        leadingChangePrice: industryArr[11],
        leadingName: industryArr[12],
      });
    });
    return { data: result };
  };

  return fetch(url, { disableDecoding: true })
    .then(checkStatus)
    .then(charset('GBK'))
    .then(mapData)
    .catch(error => ({ error }));
};

/**
 * getClassifyDetails - 获取新浪某个行业分类下的股票数据
 * 返回数组:
 * [
 *  {
 *    symbol: 股票代码
 *    name: 股票名称
 *    price: 当前价格
 *    changePrice: 涨跌额
 *    changePercent: 涨跌幅
 *    open: 开盘价
 *    high: 最高价
 *    low: 最低价
 *    volume: 成交量(手)
 *    amount: 成交额(万)
 *    tickTime: 数据时间
 *  }
 * ]
 *
 * @param {Object} options
 * @param {string} options.tag - 新浪行业代码,从getSinaIndustryClassified返回,例如new_jrhy: 金融行业
 * @param cb
 * @return {undefined}
 */
/* eslint-disable no-eval */
export const getSinaClassifyDetails = (query = {}) => {
  const defaults = {
    tag: 'new_jrhy', // 默认金融行业
  };
  const options = Object.assign({}, defaults, query);
  const url = sinaClassifyDetailUrl(options.tag);
  const mapData = data => {
    let result = [];
    result = eval(data);
    if (result) {
      result = result.map(ele => ({
        symbol: ele.symbol,
        name: ele.name,
        price: ele.trade,
        changePrice: ele.pricechange,
        changePercent: ele.changepercent,
        open: ele.open,
        high: ele.high,
        low: ele.low,
        volume: ele.volume / 100,
        amount: ele.amount / 10000,
        tickTime: ele.ticktime,
      }));
    }
    return { data: result };
  };

  return fetch(url, { disableDecoding: true })
    .then(checkStatus)
    .then(charset('GBK'))
    .then(mapData)
    .catch(error => ({ error }));
};

/**
 * getSinaConceptsClassified - 获取新浪概念板块分类数据
 * 返回数据格式 - 数组,包含:
 * tag: 新浪概念分类标识
 * name: 新浪概念分类名称
 * num: 概念包含股票数量
 * price: 平均价
 * changePrice: 涨跌额
 * changePercent: 涨跌幅
 * volume: 总成交量(手)
 * amount: 总成交额(万)
 * leadingSymbol: 领涨股票代码
 * leadingChangePercent: 领涨股涨跌幅
 * leadingPrice: 领涨股价格
 * leadingChangePrice: 领涨股涨跌额
 * leadingName: 领涨股名称
 *
 * @param cb
 * @returns {undefined}
 */
export const getSinaConceptsClassified = () => {
  const url = sinaConceptsIndexUrl();
  const mapData = data => {
    const json = JSON.parse(data.split('=')[1].trim());
    const result = Object.keys(json).map(tag => {
      const conceptsArr = json[tag].split(',');
      return {
        name: conceptsArr[1],
        num: conceptsArr[2],
        price: conceptsArr[3],
        changePrice: conceptsArr[4],
        changePercent: conceptsArr[5],
        volume: conceptsArr[6] / 100,
        amount: conceptsArr[7] / 10000,
        leadingSymbol: conceptsArr[8],
        leadingChangePercent: conceptsArr[9],
        leadingPrice: conceptsArr[10],
        leadingChangePrice: conceptsArr[11],
        leadingName: conceptsArr[12],
      };
    });
    return { data: result };
  };

  return fetch(url)
    .then(checkStatus)
    .then(charset('GBK'))
    .then(mapData)
    .catch(error => ({ error }));
};

/**
 * getAllStocks - 返回沪深上市公司基本情况
 * 返回数据格式:
 * [
 *  {
 *    code,代码
 *    name,名称
 *    industry,所属行业
 *    area,地区
 *    pe,市盈率
 *    outstanding,流通股本
 *    totals,总股本(万)
 *    totalAssets,总资产(万)
 *    liquidAssets,流动资产
 *    fixedAssets,固定资产
 *    reserved,公积金
 *    reservedPerShare,每股公积金
 *    eps,每股收益
 *    bvps,每股净资
 *    pb,市净率
 *    timeToMarket,上市日期
 *  }
 * ]
 *
 * @param cb
 * @returns {undefined}
 */
export const getAllStocks = () => {
  const url = allStockUrl();

  return fetch(url)
    .then(checkStatus)
    .then(charset('GBK'))
    .then(data => ({ data: csvToObject(data) }))
    .catch(error => ({ error }));
};

/**
 * getHS300 - 获取沪深300股票信息
 * 返回数据格式: 数组
 * [
 *   {
 *     symbol: 股票代码, 如:sh600000
 *     name: 股票名称
 *     trade: 最新价
 *     pricechange: 涨跌额
 *     changepercent: 涨跌幅
 *     buy: 买入价
 *     sell: 卖出价
 *     settlement: 昨收
 *     open: 开盘价
 *     high: 最高价
 *     low: 最低价
 *     volume: 成交量(手)
 *     amount: 成交额(万)
 *     code: 股票六位代码, 如: 600000
 *     ticktime:
 *     focus:
 *     fund:
 *   }
 * ]
 *
 * @param cb
 * @returns {undefined}
 */
export const getHS300 = () => {
  const url = hs300Url();

  return fetch(url)
    .then(checkStatus)
    .then(res => res.json())
    .then(json => ({ data: arrayObjectMapping(json[0].fields, json[0].items) }))
    .catch(error => ({ error }));
};

/**
 * getSZ50 - 获取上证50股票信息
 * 返回数据格式: 数组
 * [
 *   {
 *     symbol: 股票代码, 如:sh600000
 *     name: 股票名称
 *     trade: 最新价
 *     pricechange: 涨跌额
 *     changepercent: 涨跌幅
 *     buy: 买入价
 *     sell: 卖出价
 *     settlement: 昨收
 *     open: 开盘价
 *     high: 最高价
 *     low: 最低价
 *     volume: 成交量(手)
 *     amount: 成交额(万)
 *     code: 股票六位代码, 如: 600000
 *     ticktime:
 *     focus:
 *     fund:
 *   }
 * ]
 *
 * @param cb
 * @returns {undefined}
 */
export const getSZ50 = () => {
  const url = sz50Url();

  return fetch(url)
    .then(checkStatus)
    .then(res => res.json())
    .then(json => ({ data: arrayObjectMapping(json[0].fields, json[0].items) }))
    .catch(error => ({ error }));
};

/**
 * 获取指定年月限售解禁股数据
 * 返回数据格式: 数组
 * [
 *   {
 *     symbol: 股票代码
 *     name: 股票名称
 *     date: 解除限售日期
 *     percent: 占总股本比例
 *     count: 数量(万股)
 *     close: 最新收盘价(元)
 *     curTotalValue: 当前市值(亿元)
 *   }
 * ]
 * @param year
 * @param month
 * @returns {Promise.<TResult>}
 */
export const getXSGData = (year, month) => {
  const url = xsgUrl(year, month);
  const mapData = data => {
    let arr = JSON.parse(data.substring(1, data.length - 1));
    arr = arr.map(item => {
      const itemData = item.split(',');
      return {
        symbol: itemData[1],
        name: itemData[3],
        date: itemData[4],
        percent: itemData[6],
        count: (itemData[5] / 10000).toFixed(2),
        close: itemData[7],
        curTotalValue: (itemData[8] / 100000000).toFixed(4),
      };
    });
    return arr;
  };
  return fetch(url)
    .then(checkStatus)
    .then(res => res.text())
    .then(mapData)
    .catch(error => ({ error }));
};



================================================
FILE: src/stock/cons.js
================================================
import strftime from 'strftime';

export const K_TYPE = {
  day: 'akdaily',
  week: 'akweekly',
  month: 'akmonthly',
  minute: 'akmin',
};

export const INDEX_LABELS = ['sh', 'sz', 'hs300', 'sz50', 'cyb', 'zxb', 'zx300', 'zh500'];
export const K_LABELS = ['day', 'month', 'week'];
export const K_MIN_LABELS = ['1', '5', '15', '30', '30'];

export const INDEX_LIST = {
  sh: 'sh000001',
  sz: 'sz399001',
  hs300: 'sz399300',
  sz50: 'sh000016',
  zxb: 'sz399005',
  cyb: 'sz399006',
  zx300: 'sz399008',
  zh500: 'sh000905',
  399990: 'sz399990',
  '000006': 'sh000006',
  399998: 'sz399998',
  399436: 'sz399436',
  399678: 'sz399678',
  399804: 'sz399804',
  '000104': 'sh000104',
  '000070': 'sh000070',
  399613: 'sz399613',
  399690: 'sz399690',
  399928: 'sz399928',
  '000928': 'sh000928',
  '000986': 'sh000986',
  399806: 'sz399806',
  '000032': 'sh000032',
  '000005': 'sh000005',
  399381: 'sz399381',
  399908: 'sz399908',
  '000908': 'sh000908',
  399691: 'sz399691',
  '000139': 'sh000139',
  399427: 'sz399427',
  399248: 'sz399248',
  '000832': 'sh000832',
  399901: 'sz399901',
  399413: 'sz399413',
  '000901': 'sh000901',
  '000078': 'sh000078',
  '000944': 'sh000944',
  '000025': 'sh000025',
  399944: 'sz399944',
  399307: 'sz399307',
  '000052': 'sh000052',
  399680: 'sz399680',
  399232: 'sz399232',
  399993: 'sz399993',
  '000102': 'sh000102',
  '000950': 'sh000950',
  399950: 'sz399950',
  399244: 'sz399244',
  399925: 'sz399925',
  '000925': 'sh000925',
  '000003': 'sh000003',
  '000805': 'sh000805',
  '000133': 'sh000133',
  399677: 'sz399677',
  399319: 'sz399319',
  399397: 'sz399397',
  399983: 'sz399983',
  399654: 'sz399654',
  399440: 'sz399440',
  '000043': 'sh000043',
  '000012': 'sh000012',
  '000833': 'sh000833',
  '000145': 'sh000145',
  '000053': 'sh000053',
  '000013': 'sh000013',
  '000022': 'sh000022',
  '000094': 'sh000094',
  399299: 'sz399299',
  '000101': 'sh000101',
  399817: 'sz399817',
  399481: 'sz399481',
  399434: 'sz399434',
  399301: 'sz399301',
  '000029': 'sh000029',
  399812: 'sz399812',
  399441: 'sz399441',
  '000098': 'sh000098',
  399557: 'sz399557',
  '000068': 'sh000068',
  399298: 'sz399298',
  399302: 'sz399302',
  '000961': 'sh000961',
  '000959': 'sh000959',
  399961: 'sz399961',
  '000126': 'sh000126',
  '000036': 'sh000036',
  399305: 'sz399305',
  '000116': 'sh000116',
  399359: 'sz399359',
  399810: 'sz399810',
  '000062': 'sh000062',
  399618: 'sz399618',
  399435: 'sz399435',
  '000149': 'sh000149',
  '000819': 'sh000819',
  '000020': 'sh000020',
  '000061': 'sh000061',
  '000016': 'sh000016',
  '000028': 'sh000028',
  399809: 'sz399809',
  '000999': 'sh000999',
  399238: 'sz399238',
  '000100': 'sh000100',
  399979: 'sz399979',
  '000979': 'sh000979',
  399685: 'sz399685',
  '000152': 'sh000152',
  '000153': 'sh000153',
  399318: 'sz399318',
  '000853': 'sh000853',
  '000040': 'sh000040',
  399693: 'sz399693',
  '000076': 'sh000076',
  '000017': 'sh000017',
  '000134': 'sh000134',
  399989: 'sz399989',
  '000042': 'sh000042',
  '000066': 'sh000066',
  '000008': 'sh000008',
  '000002': 'sh000002',
  '000001': 'sh000001',
  '000011': 'sh000011',
  '000031': 'sh000031',
  399403: 'sz399403',
  '000951': 'sh000951',
  399951: 'sz399951',
  '000092': 'sh000092',
  399234: 'sz399234',
  '000823': 'sh000823',
  399986: 'sz399986',
  399647: 'sz399647',
  '000050': 'sh000050',
  '000073': 'sh000073',
  399357: 'sz399357',
  '000940': 'sh000940',
  '000107': 'sh000107',
  '000048': 'sh000048',
  399411: 'sz399411',
  399366: 'sz399366',
  399373: 'sz399373',
  '000015': 'sh000015',
  '000021': 'sh000021',
  '000151': 'sh000151',
  '000851': 'sh000851',
  '000058': 'sh000058',
  399404: 'sz399404',
  399102: 'sz399102',
  399431: 'sz399431',
  399971: 'sz399971',
  '000125': 'sh000125',
  '000069': 'sh000069',
  '000063': 'sh000063',
  399395: 'sz399395',
  '000038': 'sh000038',
  399240: 'sz399240',
  399903: 'sz399903',
  '000989': 'sh000989',
  399321: 'sz399321',
  399675: 'sz399675',
  399235: 'sz399235',
  '000057': 'sh000057',
  '000056': 'sh000056',
  '000903': 'sh000903',
  399310: 'sz399310',
  '000004': 'sh000004',
  '000019': 'sh000019',
  399919: 'sz399919',
  '000974': 'sh000974',
  '000919': 'sh000919',
  399635: 'sz399635',
  399663: 'sz399663',
  399106: 'sz399106',
  399107: 'sz399107',
  399555: 'sz399555',
  '000090': 'sh000090',
  '000155': 'sh000155',
  '000060': 'sh000060',
  399636: 'sz399636',
  '000816': 'sh000816',
  '000010': 'sh000010',
  399671: 'sz399671',
  '000035': 'sh000035',
  399352: 'sz399352',
  399683: 'sz399683',
  399554: 'sz399554',
  399409: 'sz399409',
  '000018': 'sh000018',
  399101: 'sz399101',
  '000992': 'sh000992',
  399416: 'sz399416',
  399918: 'sz399918',
  399379: 'sz399379',
  399674: 'sz399674',
  399239: 'sz399239',
  399384: 'sz399384',
  399367: 'sz399367',
  '000918': 'sh000918',
  '000914': 'sh000914',
  399914: 'sz399914',
  '000054': 'sh000054',
  '000806': 'sh000806',
  399619: 'sz399619',
  399015: 'sz399015',
  399393: 'sz399393',
  399313: 'sz399313',
  399231: 'sz399231',
  '000846': 'sh000846',
  '000854': 'sh000854',
  399010: 'sz399010',
  399666: 'sz399666',
  399387: 'sz399387',
  399399: 'sz399399',
  '000026': 'sh000026',
  399934: 'sz399934',
  '000150': 'sh000150',
  '000934': 'sh000934',
  399317: 'sz399317',
  '000138': 'sh000138',
  399371: 'sz399371',
  399394: 'sz399394',
  399659: 'sz399659',
  399665: 'sz399665',
  399931: 'sz399931',
  '000161': 'sh000161',
  399380: 'sz399380',
  '000931': 'sh000931',
  399704: 'sz399704',
  399616: 'sz399616',
  '000817': 'sh000817',
  399303: 'sz399303',
  399629: 'sz399629',
  399624: 'sz399624',
  399009: 'sz399009',
  399233: 'sz399233',
  399103: 'sz399103',
  399242: 'sz399242',
  399627: 'sz399627',
  '000971': 'sh000971',
  399679: 'sz399679',
  399912: 'sz399912',
  '000982': 'sh000982',
  399668: 'sz399668',
  '000096': 'sh000096',
  399982: 'sz399982',
  '000849': 'sh000849',
  '000148': 'sh000148',
  399364: 'sz399364',
  '000912': 'sh000912',
  '000129': 'sh000129',
  '000055': 'sh000055',
  '000047': 'sh000047',
  399355: 'sz399355',
  399622: 'sz399622',
  '000033': 'sh000033',
  399640: 'sz399640',
  '000852': 'sh000852',
  399966: 'sz399966',
  399615: 'sz399615',
  399802: 'sz399802',
  399602: 'sz399602',
  '000105': 'sh000105',
  399660: 'sz399660',
  399672: 'sz399672',
  399913: 'sz399913',
  399420: 'sz399420',
  '000159': 'sh000159',
  399314: 'sz399314',
  399652: 'sz399652',
  399369: 'sz399369',
  '000913': 'sh000913',
  '000065': 'sh000065',
  '000808': 'sh000808',
  399386: 'sz399386',
  399100: 'sz399100',
  '000997': 'sh000997',
  '000990': 'sh000990',
  '000093': 'sh000093',
  399637: 'sz399637',
  399439: 'sz399439',
  399306: 'sz399306',
  '000855': 'sh000855',
  '000123': 'sh000123',
  399623: 'sz399623',
  399312: 'sz399312',
  399249: 'sz399249',
  399311: 'sz399311',
  399975: 'sz399975',
  399356: 'sz399356',
  399400: 'sz399400',
  399676: 'sz399676',
  '000136': 'sh000136',
  399361: 'sz399361',
  399974: 'sz399974',
  399995: 'sz399995',
  399316: 'sz399316',
  399701: 'sz399701',
  '000300': 'sh000300',
  '000030': 'sh000030',
  '000976': 'sh000976',
  399686: 'sz399686',
  399108: 'sz399108',
  399374: 'sz399374',
  '000906': 'sh000906',
  399707: 'sz399707',
  '000064': 'sh000064',
  399633: 'sz399633',
  399300: 'sz399300',
  399628: 'sz399628',
  399398: 'sz399398',
  '000034': 'sh000034',
  399644: 'sz399644',
  399905: 'sz399905',
  399626: 'sz399626',
  399625: 'sz399625',
  '000978': 'sh000978',
  399664: 'sz399664',
  399682: 'sz399682',
  399322: 'sz399322',
  '000158': 'sh000158',
  '000842': 'sh000842',
  399550: 'sz399550',
  399423: 'sz399423',
  399978: 'sz399978',
  399996: 'sz399996',
  '000905': 'sh000905',
  '000007': 'sh000007',
  '000827': 'sh000827',
  399655: 'sz399655',
  399401: 'sz399401',
  399650: 'sz399650',
  '000963': 'sh000963',
  399661: 'sz399661',
  399922: 'sz399922',
  '000091': 'sh000091',
  399375: 'sz399375',
  '000922': 'sh000922',
  399702: 'sz399702',
  399963: 'sz399963',
  399011: 'sz399011',
  399012: 'sz399012',
  399383: 'sz399383',
  399657: 'sz399657',
  399910: 'sz399910',
  399351: 'sz399351',
  '000910': 'sh000910',
  '000051': 'sh000051',
  399376: 'sz399376',
  399639: 'sz399639',
  '000821': 'sh000821',
  399360: 'sz399360',
  399604: 'sz399604',
  399315: 'sz399315',
  399658: 'sz399658',
  '000135': 'sh000135',
  '000059': 'sh000059',
  399006: 'sz399006',
  399320: 'sz399320',
  '000991': 'sh000991',
  399606: 'sz399606',
  399428: 'sz399428',
  399406: 'sz399406',
  399630: 'sz399630',
  '000802': 'sh000802',
  399803: 'sz399803',
  '000071': 'sh000071',
  399358: 'sz399358',
  399013: 'sz399013',
  399385: 'sz399385',
  399008: 'sz399008',
  399649: 'sz399649',
  399673: 'sz399673',
  399418: 'sz399418',
  399370: 'sz399370',
  '000814': 'sh000814',
  399002: 'sz399002',
  399814: 'sz399814',
  399641: 'sz399641',
  399001: 'sz399001',
  399662: 'sz399662',
  399706: 'sz399706',
  399932: 'sz399932',
  '000095': 'sh000095',
  '000932': 'sh000932',
  399965: 'sz399965',
  399363: 'sz399363',
  399354: 'sz399354',
  399638: 'sz399638',
  399648: 'sz399648',
  399608: 'sz399608',
  '000939': 'sh000939',
  399939: 'sz399939',
  399365: 'sz399365',
  399382: 'sz399382',
  399631: 'sz399631',
  399612: 'sz399612',
  399611: 'sz399611',
  399645: 'sz399645',
  399324: 'sz399324',
  399552: 'sz399552',
  '000858': 'sh000858',
  '000045': 'sh000045',
  '000121': 'sh000121',
  399703: 'sz399703',
  399003: 'sz399003',
  399348: 'sz399348',
  399389: 'sz399389',
  399007: 'sz399007',
  399391: 'sz399391',
  '000973': 'sh000973',
  '000984': 'sh000984',
  '000969': 'sh000969',
  '000952': 'sh000952',
  399332: 'sz399332',
  399952: 'sz399952',
  399553: 'sz399553',
  '000856': 'sh000856',
  399969: 'sz399969',
  399643: 'sz399643',
  399402: 'sz399402',
  399372: 'sz399372',
  399632: 'sz399632',
  399344: 'sz399344',
  399808: 'sz399808',
  399620: 'sz399620',
  '000103': 'sh000103',
  399911: 'sz399911',
  '000993': 'sh000993',
  '000983': 'sh000983',
  399687: 'sz399687',
  399933: 'sz399933',
  '000933': 'sh000933',
  399437: 'sz399437',
  399433: 'sz399433',
  '000046': 'sh000046',
  '000911': 'sh000911',
  '000114': 'sh000114',
  '000049': 'sh000049',
  399392: 'sz399392',
  399653: 'sz399653',
  '000975': 'sh000975',
  '000044': 'sh000044',
  399378: 'sz399378',
  '000828': 'sh000828',
  399634: 'sz399634',
  399005: 'sz399005',
  '000162': 'sh000162',
  399333: 'sz399333',
  '000122': 'sh000122',
  399646: 'sz399646',
  '000077': 'sh000077',
  '000074': 'sh000074',
  399656: 'sz399656',
  399396: 'sz399396',
  399415: 'sz399415',
  399408: 'sz399408',
  '000115': 'sh000115',
  '000987': 'sh000987',
  399362: 'sz399362',
  '000841': 'sh000841',
  '000141': 'sh000141',
  '000120': 'sh000120',
  399992: 'sz399992',
  '000807': 'sh000807',
  399350: 'sz399350',
  '000009': 'sh000009',
  '000998': 'sh000998',
  399390: 'sz399390',
  399405: 'sz399405',
  '000099': 'sh000099',
  399337: 'sz399337',
  '000142': 'sh000142',
  399419: 'sz399419',
  399407: 'sz399407',
  '000909': 'sh000909',
  '000119': 'sh000119',
  399909: 'sz399909',
  399805: 'sz399805',
  '000996': 'sh000996',
  '000847': 'sh000847',
  '000130': 'sh000130',
  399377: 'sz399377',
  399388: 'sz399388',
  399610: 'sz399610',
  '000958': 'sh000958',
  399958: 'sz399958',
  '000075': 'sh000075',
  399346: 'sz399346',
  '000147': 'sh000147',
  '000132': 'sh000132',
  '000108': 'sh000108',
  399642: 'sz399642',
  '000977': 'sh000977',
  399689: 'sz399689',
  399335: 'sz399335',
  399977: 'sz399977',
  399972: 'sz399972',
  399970: 'sz399970',
  399004: 'sz399004',
  399341: 'sz399341',
  399330: 'sz399330',
  399917: 'sz399917',
  '000160': 'sh000160',
  399432: 'sz399432',
  399429: 'sz399429',
  '000917': 'sh000917',
  '000128': 'sh000128',
  '000067': 'sh000067',
  '000079': 'sh000079',
  399236: 'sz399236',
  399994: 'sz399994',
  399237: 'sz399237',
  '000966': 'sh000966',
  '000957': 'sh000957',
  399328: 'sz399328',
  399353: 'sz399353',
  399957: 'sz399957',
  399412: 'sz399412',
  '000904': 'sh000904',
  399904: 'sz399904',
  399410: 'sz399410',
  '000027': 'sh000027',
  399667: 'sz399667',
  '000857': 'sh000857',
  '000131': 'sh000131',
  '000964': 'sh000964',
  399339: 'sz399339',
  399964: 'sz399964',
  399991: 'sz399991',
  399417: 'sz399417',
  '000146': 'sh000146',
  399551: 'sz399551',
  '000137': 'sh000137',
  '000118': 'sh000118',
  399976: 'sz399976',
  '000109': 'sh000109',
  399681: 'sz399681',
  399438: 'sz399438',
  '000117': 'sh000117',
  399614: 'sz399614',
  399669: 'sz399669',
  '000111': 'sh000111',
  399670: 'sz399670',
  '000097': 'sh000097',
  '000106': 'sh000106',
  '000039': 'sh000039',
  399935: 'sz399935',
  '000935': 'sh000935',
  399813: 'sz399813',
  '000037': 'sh000037',
  399811: 'sz399811',
  399705: 'sz399705',
  399556: 'sz399556',
  '000113': 'sh000113',
  '000072': 'sh000072',
  399651: 'sz399651',
  399617: 'sz399617',
  399684: 'sz399684',
  '000041': 'sh000041',
  399807: 'sz399807',
  399959: 'sz399959',
  399967: 'sz399967',
  399326: 'sz399326',
  399688: 'sz399688',
  399368: 'sz399368',
  399241: 'sz399241',
  399696: 'sz399696',
  '000850': 'sh000850',
  '000110': 'sh000110',
  399621: 'sz399621',
  399243: 'sz399243',
  399973: 'sz399973',
  399987: 'sz399987',
  '000112': 'sh000112',
  399997: 'sz399997',
  hkHSI: 'hkHSI',
};

export const DATE_NOW = strftime('%Y-%m-%d', new Date());
export const CUR_YEAR = DATE_NOW.split('-')[0];
export const CUR_MONTH = DATE_NOW.split('-')[1];


================================================
FILE: src/stock/index.js
================================================
export * from './trading';
export * from './classifying';
export * from './billboard';


================================================
FILE: src/stock/trading.js
================================================
import { unnest } from 'ramda';
import util from 'util';

/* eslint-disable no-console */
import {
  priceUrl,
  tickUrl,
  todayAllUrl,
  liveDataUrl,
  todayTickUrl,
  indexUrl,
  sinaDDUrl,
  klineTTUrl,
  klineTTMinUrl,
} from './urls';

import * as cons from './cons';
import { codeToSymbol, checkStatus, DATE_NOW, randomString, runTasksInParallel, createFetchTasks } from './util';
import { charset } from '../utils/charset';
import '../utils/fetch';
import { ttDates } from '../utils/dateu';

/**
 * getHistory: 获取个股历史数据
 * 返回数据格式 - 日期 ,开盘价, 最高价, 收盘价, 最低价, 成交量, 价格变动 ,涨跌幅,5日均价,10日均价,20日均价,5日均量,10日均量,20日均量,换手率
 *
 * @param {Object} options = {} - options
 * @param {String} options.code - 股票代码, 例如: '600848'
 * @param {String} options.start - 开始日期 format:YYYY-MM-DD 为空时取到API所提供的最早日期数据
 * @param {String} options.end - 结束日期 format:YYYY-MM-DD 为空时取到最近一个交易日数据
 * @param {String} options.ktype - 数据类型,day=日k线 week=周 month=月 5=5分钟 15=15分钟 30=30分钟 60=60分钟,默认为day
 * @param {String} options.autype - 复权类型,默认前复权, fq=前复权, last=不复权
 * @return {undefined}
 */
export const getHistory = (query = {}) => {
  const defaults = {
    code: null,
    start: null,
    end: null,
    ktype: 'day',
    autype: 'fq',
  };
  const options = Object.assign({}, defaults, query);

  const symbol = codeToSymbol(options.code);
  const url = priceUrl(options.ktype, options.autype, symbol);

  return fetch(url)
  .then(checkStatus)
  .then(res => res.json())
  .then(json => ({ data: json }))
  .catch(error => ({ error }));
};

const getTimeTick = ts => {
  const sarr = ts.split('-');
  let retval = 0;
  if (sarr.length >= 3) {
    retval += parseInt(sarr[0], 10) * 100 * 100;
    retval += parseInt(sarr[1], 10) * 100;
    retval += parseInt(sarr[2], 10);
    retval *= 10000;
  } else {
    retval += parseInt(ts, 10);
  }
  return retval;
};

const getSymbol = ({ code, isIndex }) => {
  if (isIndex && code in cons.INDEX_LIST) {
    return cons.INDEX_LIST[code];
  }
  return codeToSymbol(code);
};

const getEndDate = ({ start, end }) => (start && !end ? cons.DATE_NOW : end);

const getFq = ({ autype, code, isIndex }) => {
  let fq = autype || '';
  if (code[0] === '1' || code[0] === '5' || isIndex) {
    fq = '';
  }
  return fq;
};

const getKline = ({ autype }) => (autype ? 'fq' : '');

const parseKData = (rawData, ktype, code) => {
  const rawDataArray = rawData.split('=');
  if (rawDataArray.length > 1) {
    const json = JSON.parse(rawDataArray[1].replace(/,\{"nd.*?\}/, ''));
    if ('data' in json && code in json['data'] && ktype in json['data'][code]) {
      return json['data'][code][ktype];
    }
  }
  return [];
};

const getKDataLong = (options = {}) => {
  if (!cons.K_LABELS.includes(options.ktype)) {
    throw new Error(util.format('unknown ktype %s', options.ktype));
  }

  const kline = getKline(options);
  const fq = getFq(options);
  const symbol = getSymbol(options);
  const sdate = options.start;
  const edate = getEndDate(options);
  let urls = [];

  if (!sdate && !edate) {
    const randomstr = randomString(17);
    const url = klineTTUrl(kline, fq, symbol, options.ktype, sdate, edate, fq, randomstr);
    urls = urls.concat(url);
  } else {
    const years = ttDates(sdate, edate);
    urls = years.map(year => {
      const startOfYear = util.format('%s-01-01', year);
      const endOfYear = util.format('%s-12-31', year);

      const randomstr = randomString(17);
      return klineTTUrl(kline, year, symbol, options.ktype, startOfYear,
        endOfYear, fq, randomstr);
    });
  }

  const tasks = createFetchTasks(urls);
  return runTasksInParallel(tasks)
    .then(results => results.map(dataStr => parseKData(dataStr, options.ktype, symbol)))
    .then(unnest);
};

const getKDataShort = (options = {}) => {
  if (!cons.K_MIN_LABELS.includes(options.ktype)) {
    throw new Error(util.format('unknown ktype %s', options.ktype));
  }

  const symbol = getSymbol(options);
  const sdate = options.start;
  const edate = getEndDate(options);
  const randomstr = randomString(16);
  const ktype = util.format('m%s', options.ktype);
  const url = klineTTMinUrl(symbol, options.ktype, randomstr);

  return fetch(url)
    .then(checkStatus)
    .then(res => res.text())
    .then(dataStr => parseKData(dataStr, ktype, symbol))
    .then(data => data.filter(tick => {
      const stick = getTimeTick(sdate);
      const etick = getTimeTick(edate);
      const curtick = getTimeTick(tick[0]);
      return curtick >= stick && curtick <= etick;
    }));
};


/**
 * getKData: 获取k线数据
 * 返回数据格式 - 日期 ,开盘价, 最高价, 收盘价, 最低价, 成交量, 价格变动 ,涨跌幅,5日均价,10日均价,20日均价,5日均量,10日均量,20日均量,换手率
 *
 * @param {Object} options = {} - options
 * @param {String} options.code - 股票代码, 例如: '600848'
 * @param {String} options.start - 开始日期 format:YYYY-MM-DD 为空时取到API所提供的最早日期数据
 * @param {String} options.end - 结束日期 format:YYYY-MM-DD 为空时取到最近一个交易日数据
 * @param {String} options.ktype - 数据类型,day=日k线 week=周 month=月 5=5分钟 15=15分钟 30=30分钟 60=60分钟,默认为day
 * @param {String} options.autype - 复权类型,默认前复权, fq=前复权, last=不复权
 * @param {Bool}   options.isIndex - 是否为指数,默认为false
 * @return {Promise object}      Promise Object 可以调用 then catch函数
 */
export const getKData = (query = {}) => {
  const defaults = {
    code: null,
    start: '',
    end: '',
    ktype: 'day',
    autype: 'fq',
    isIndex: false,
  };


  const options = Object.assign({}, defaults, query);


  if (cons.K_LABELS.includes(options.ktype)) {
    return getKDataLong(options);
  }

  if (cons.K_MIN_LABELS.includes(options.ktype)) {
    return getKDataShort(options);
  }

  throw new Error(util.format('not supported ktype %s', options.ktype));
};


/**
 * getTick - 获取历史分笔数据
 * 返回格式:成交时间 成交价  涨跌幅  价格变动  成交量(手)  成交额(元)  性质
 *
 * @param {Object} options
 * @param {string} options.code - 股票代码, 例如: '600848'
 * @param {string} options.date - 日期 格式:YYYY-MM-DD
 * @param cb
 * @return {undefined}
 */
export const getTick = (query = {}) => {
  const defaults = {
    code: null,
    date: null,
  };
  const options = Object.assign({}, defaults, query);

  const symbol = codeToSymbol(options.code);
  const url = tickUrl(options.date, symbol);
  const mapData = data => {
    const result = [];
    data.split('\n').forEach((line, i) => {
      if (i !== 0 && line !== '') {
        result.push(line.split('\t'));
      }
    });
    return { data: result };
  };

  return fetch(url)
  .then(checkStatus)
  .then(charset('GBK'))
  .then(mapData)
  .catch(error => ({ error }));
};

/**
 * getTodayAll - 一次性获取最近一个日交易日所有股票的交易数据
 * 返回数据格式:代码,名称,涨跌幅,现价,开盘价,最高价,最低价,最日收盘价,成交量,换手率
 *
 * @param options - (可选) 若为空,则返回A股市场今日所有数据
 * @param {Number} options.pageSize - 分页的大小,如:80, 默认10000,即返回所有数据
 * @param {Number} options.pageNo - 分页页码,默认1
 * @param cb
 * @return {undefined}
 */
/* eslint-disable no-eval */
export const getTodayAll = (query = {}) => {
  const defaults = {
    pageSize: 10000,
    pageNo: 1,
  };
  const options = Object.assign({}, defaults, query);
  const url = todayAllUrl(options.pageSize, options.pageNo);

  return fetch(url)
  .then(checkStatus)
  .then(res => res.text())
  .then(data => ({ data: eval(data) }))
  .catch(error => ({ error }));
};

/**
 * getLiveData - 获取实时交易数据
 * 返回数据:{Array}
 * 0:股票代码
 * 1:股票名字
 * 2:今日开盘价
 * 3:昨日收盘价
 * 4:当前价格
 * 5:今日最高价
 * 6:今日最低价
 * 7:竞买价,即“买一”报价
 * 8:竞卖价,即“卖一”报价
 * 9:成交量 maybe you need do volume/100
 * 10:成交金额(元 CNY)
 * 11:委买一(笔数 bid volume)
 * 12:委买一(价格 bid price)
 * 13:“买二”
 * 14:“买二”
 * 15:“买三”
 * 16:“买三”
 * 17:“买四”
 * 18:“买四”
 * 19:“买五”
 * 20:“买五”
 * 21:委卖一(笔数 ask volume)
 * 22:委卖一(价格 ask price)
 * ...
 * 31:日期;
 * 32:时间;
 *
 * @param {Object} options
 * @param {Array} options.codes - 股票代码数组,例如['600848', '600000', '600343']
 * @param cb
 * @return {undefined}
 */
export const getLiveData = (query = {}) => {
  const defaults = { codes: ['600000'] };
  const options = Object.assign({}, defaults, query);
  const codes = options.codes.map(code => codeToSymbol(code));
  const url = liveDataUrl(codes);
  const mapData = data => {
    const result = data.split('\n')
    .filter(item => item !== '')
    .map(item => {
      const matches = item.match(/(sz|sh)(\d{6}).*"(.*)"/i);
      const symbol = matches[1] + matches[2];
      const records = matches[3].split(',');
      return [symbol, ...records];
    });

    return { data: result };
  };

  return fetch(url)
  .then(checkStatus)
  .then(charset('GBK'))
  .then(mapData)
  .catch(error => ({ error }));
};

/**
 * getTodayTick - 获取当日分笔明细数据,用于在交易进行的时候获取
 * 返回数据:
 * {
 *  begin: 开始时间,
 *  end: 结束时间,
 *  zhubi_list: [
 *    {
 *      TRADE_TYPE: 交易类型, 1: 买盘,0:中性盘,-1:卖盘
 *      PRICE_PRE: 上一档价格
 *      PRICE: 当前价格
 *      VOLUME_INC: 成交量(股)
 *      TURNOVER_INC: 成交额
 *      TRADE_TYPE_STR: 交易类型:买盘、卖盘、中性盘
 *      DATE_STR: 时间
 *    }
 *  ]
 * }
 *
 * @param {Object} options
 * @param {String} options.code - 六位股票代码
 * @param {String} options.end - 结束时间。例如:15:00:00, 那么就会获取14:55:00 - 15:00:00之间的分笔数据,也就是end指定时间之前的五分钟
 * @param cb
 * @return {undefined}
 */
export const getTodayTick = (query = {}) => {
  const defaults = {
    code: '600000',
    end: '15:00:00',
  };
  const options = Object.assign({}, defaults, query);
  const url = todayTickUrl(options.code, options.end);

  return fetch(url)
  .then(checkStatus)
  .then(res => res.json())
  .then(json => ({ data: json }))
  .catch(error => ({ error }));
};

export const getIndex = () => {
  const url = indexUrl();
  const mapData = data => {
    const result = data.split('\n')
      .filter(item => item !== '')
      .map(item => {
        const matches = item.match(/(sz|sh)(\d{6}).*"(.*)"/i);
        const symbol = matches[1] + matches[2];
        const records = matches[3].split(',');
        return {
          code: symbol,
          name: records[0],
          open: records[1],
          preclose: records[2],
          close: records[3],
          high: records[4],
          low: records[5],
          volume: records[8],
          amount: records[9],
        };
      });
    return { data: result };
  };

  return fetch(url)
  .then(checkStatus)
  .then(charset('GBK'))
  .then(mapData)
  .catch(error => ({ error }));
};

/**
 * getSinaDD - 获取新浪大单数据
 * 返回数组:
 * [
 *  {
 *    symbol: 股票代码
 *    name: 股票名字
 *    time: 时间
 *    price: 成交价格
 *    volume: 成交量(手)
 *    preprice: 前一价格
 *    type: 类型,买盘、卖盘、中性盘
 *  }
 * ]
 *
 * @param {Object} options
 * @param {String} options.code - 六位股票代码
 * @param {String} options.volume - 设置多少手以上算大单,例如: 400,则返回400手以上交易量的大单
 * @param {String} options.date - 日期,格式YYYY-MM-DD, 默认当日日期
 * @param cb
 * @return {undefined}
 */
export const getSinaDD = (query = {}) => {
  const defaults = {
    code: '600000',
    volume: 400,
    date: DATE_NOW,
  };
  const options = Object.assign({}, defaults, query);
  const url = sinaDDUrl(codeToSymbol(options.code), options.volume * 100, options.date);
  const mapData = data => {
    const result = data.split('\n')
      .filter((item, idx) => item !== '' && idx !== 0)
      .map(item => {
        const ddArr = item.split(',');
        return {
          symbol: ddArr[0],
          name: ddArr[1],
          time: ddArr[2],
          price: ddArr[3],
          volume: ddArr[4] / 100,
          preprice: ddArr[5],
          type: ddArr[6],
        };
      });
    return { data: result };
  };

  return fetch(url)
  .then(checkStatus)
  .then(charset('GBK'))
  .then(mapData)
  .catch(error => ({ error }));
};


================================================
FILE: src/stock/urls.js
================================================
import { K_TYPE, CUR_YEAR, CUR_MONTH } from './cons';

export const priceUrl = (ktype, autype, symbol) => {
  const _ktype = K_TYPE[ktype] ? K_TYPE[ktype] : K_TYPE.minute;
  const type = _ktype === K_TYPE.minute ? ktype : autype;
  const codeStr = _ktype === K_TYPE.minute ? 'scode' : 'code';
  return `http://api.finance.ifeng.com/${_ktype}/?${codeStr}=${symbol}&type=${type}`;
};

export const tickUrl = (date, symbol) => `http://market.finance.sina.com.cn/downxls.php?date=${date}&symbol=${symbol}`;

export const todayAllUrl = (pageSize, pageNo) =>
  `http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?num=${pageSize}&sort=changepercent&asc=0&node=hs_a&symbol=&_s_r_a=page&page=${pageNo}`;

export const liveDataUrl = symbols => `http://hq.sinajs.cn/list=${symbols.join(',')}`;

export const todayTickUrl = (code, end = '15:00:00') => `http://quotes.money.163.com/service/zhubi_ajax.html?symbol=${code}&end=${end}`;

export const indexUrl = () =>
  'http://hq.sinajs.cn/rn=xppzh&list=sh000001,sh000002,sh000003,sh000008,sh000009,sh000010,sh000011,sh000012,sh000016,sh000017,sh000300,sz399001,sz399002,sz399003,sz399004,sz399005,sz399006,sz399100,sz399101,sz399106,sz399107,sz399108,sz399333,sz399606';

export const klineTTUrl = (kline, fq, symbol, ktype, start, end, fq2, randomcode) =>
      `http://web.ifzq.gtimg.cn/appstock/app/${kline}kline/get?_var=kline_day${fq}&param=${symbol},${ktype},${start},${end},640,${fq2}&r=0.${randomcode}`;

export const klineTTMinUrl = (symbol, ktype, randomcode) =>
      `http://ifzq.gtimg.cn/appstock/app/kline/mkline?param=${symbol},m${ktype},,640&_var=m${ktype}_today&r=0.${randomcode}`;

export const sinaDDUrl = (symbol, volume, date) =>
  `http://vip.stock.finance.sina.com.cn/quotes_service/view/cn_bill_download.php?symbol=${symbol}&num=60&page=1&sort=ticktime&asc=0&volume=${volume}&amount=0&type=0&day=${date}`;

export const sinaIndustryIndexUrl = () => 'http://vip.stock.finance.sina.com.cn/q/view/newSinaHy.php';

export const sinaClassifyDetailUrl = tag =>
  `http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?page=1&num=400&sort=symbol&asc=1&node=${tag}&symbol=&_s_r_a=page`;

export const sinaConceptsIndexUrl = () => 'http://money.finance.sina.com.cn/q/view/newFLJK.php?param=class';

export const allStockUrl = () => 'http://218.244.146.57/static/all.csv';

export const hs300Url = (pageNo = 1, pageSize = 300) =>
  `http://money.finance.sina.com.cn/d/api/openapi_proxy.php/?__s=[["jjhq",${pageNo},${pageSize},"",0,"hs300"]]`;

export const sz50Url = (pageNo = 1, pageSize = 50) =>
  `http://money.finance.sina.com.cn/d/api/openapi_proxy.php/?__s=[["jjhq",${pageNo},${pageSize},"",0,"zhishu_000016"]]`;

export function lhbUrl(start, end, pageNo = 1, pageSize = 150) {
  const url = `http://quotes.money.163.com/hs/marketdata/service/lhb.php?page=${pageNo - 1}&query=start:${start};end:${end}&sort=TDATE&order=desc&count=${pageSize}`;
  return url;
}

export function blockTradeUrl(start, end, pageNo = 1, pageSize = 150) {
  const url = `http://quotes.money.163.com/hs/marketdata/service/dzjy.php?page=${pageNo - 1}&query=start:${start};end:${end};&order=desc&count=${pageSize}&sort=PUBLISHDATE`;
  return url;
}

export function longPeriodRankUrl(period = 'month', pageNo = 1, pageSize = 100) {
  let rankBy = '';
  switch (period) {
    case 'week':
      rankBy = 'WEEK_PERCENT';
      break;
    case 'month':
      rankBy = 'MONTH_PERCENT';
      break;
    case 'quarter':
      rankBy = 'QUARTER_PERCENT';
      break;
    case 'year':
      rankBy = 'YEAR_PERCENT';
      break;
    default:
      rankBy = 'PERCENT';
  }
  const url = `http://quotes.money.163.com/hs/realtimedata/service/rank.php?page=${pageNo - 1}&query=LONG_PERIOD_RANK:_exists_&fields=RN,CODE,SYMBOL,NAME,PRICE,LONG_PERIOD_RANK,PERCENT&sort=LONG_PERIOD_RANK.${rankBy}&order=desc&count=${pageSize}`;
  return url;
}

export const xsgUrl = (year = CUR_YEAR, month = CUR_MONTH) => `http://datainterface.eastmoney.com/EM_DataCenter/JS.aspx?type=FD&sty=BST&st=3&sr=true&fd=${year}&stat=${month}`;


================================================
FILE: src/stock/util.js
================================================
import parallel from 'async/parallel';
import util from 'util';

import { INDEX_LABELS, INDEX_LIST } from './cons';


export function codeToSymbol(code) {
  let symbol = '';
  if (INDEX_LABELS.indexOf(code) >= 0) {
    symbol = INDEX_LIST[code];
  } else if (code.length === 6) {
    symbol = ['5', '6', '9'].indexOf(code.charAt(0)) >= 0 ? `sh${code}` : `sz${code}`;
  } else {
    symbol = code;
  }

  return symbol;
}

export function csvToObject(csv) {
  let csvArr = csv.trim().split('\r\n');
  let headers = csvArr.splice(0, 1);

  headers = headers[0].split(',');
  csvArr = csvArr.map(ele => {
    const obj = {};
    ele.split(',').forEach((s, i) => {
      obj[headers[i]] = s;
    });
    return obj;
  });

  return csvArr;
}

export function arrayObjectMapping(fields, items) {
  return items.map(ele => {
    const obj = {};
    ele.forEach((s, i) => {
      const field = fields[i];
      if (field === 'volume') {
        obj[field] = s / 100;
      } else if (field === 'amount') {
        obj[field] = s / 10000;
      } else {
        obj[field] = s;
      }
    });
    return obj;
  });
}

const _pow = (base, exp) => {
  let _b = base;
  for (let _i = 0; _i < exp; _i += 1) {
    _b *= base;
  }
  return _b;
};

export function randomString(num) {
  const lower = _pow(10, (num - 1));
  const upper = _pow(10, num) - 1;
  const rnum = lower + (Math.random() * (upper - lower));
  return util.format('%s', rnum);
}

export const checkStatus = response => {
  if (response.status >= 200 && response.status < 300) {
    return response;
  }

  const error = new Error(response.statusText);
  error.response = response;
  throw error;
};

/**
 * create a list of fetch tasks
 * return [promise, promise, ...]
 */
export const createFetchTasks = urls =>
  urls.map(url =>
    fetch(url)
    .then(checkStatus)
    .then(res => res.text())
  );

/**
 * @return [ [obj, obj, ...], [obj, obj, ...], ... ]
 */
export const runTasksInParallel = tasks =>
  new Promise((resolve, reject) => {
    parallel(
      tasks.map(task => callback => task.then(data => callback(null, data))),
      (err, results) => {
        if (err) {
          reject(err);
        } else {
          resolve(results);
        }
      }
    );
  });



================================================
FILE: src/utils/charset.js
================================================
/* eslint-disable global-require */
/* eslint-disable import/newline-after-import */
export const charset = enc => res => {
  // if under nodejs environment
  if (typeof module !== undefined && module.exports) {
    const iconv = require('iconv-lite');
    return res.buffer().then(buffer => iconv.decode(buffer, enc));
  }
  return res.blob().then(blob => {
    const reader = new FileReader();
    reader.readAsText(blob, enc);
    return new Promise(
      resolve => {
        reader.onloadend(() => resolve(reader.result));
      }
    );
  });
};


================================================
FILE: src/utils/dateu.js
================================================
export function ttDates(sdate, edate) {
  const retyears = [];
  let iyear;
  let sarr = sdate.split('-');
  const syear = parseInt(sarr[0], 10);
  sarr = edate.split('-');
  const eyear = parseInt(sarr[0], 10);
  iyear = syear;
  while (iyear < eyear) {
    retyears.push(iyear);
    iyear += 1;
  }
  if (eyear !== syear) {
    retyears.push(eyear);
  } else if (syear === eyear) {
    retyears.push(syear);
  }
  return retyears;
}


================================================
FILE: src/utils/fetch.js
================================================
/* eslint-disable */
if (typeof module !== undefined && module.exports) {
  var realFetch = require('no-fetch');
  module.exports = function(url, options) {
    if (/^\/\//.test(url)) {
      url = 'https:' + url;
    }
    return realFetch.call(this, url, options);
  };

  if (!global.fetch) {
    global.fetch = module.exports;
    global.Response = realFetch.Response;
    global.Headers = realFetch.Headers;
    global.Request = realFetch.Request;
  }
} else {
  require('whatwg-fetch');
  module.exports = self.fetch.bind(self);
}


================================================
FILE: test/billboardDZJYTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get All Stock Data', t => {
  const options = {
    start: '2016-01-15',
    end: '2016-01-15',
  };
  return stock.blockTrade(options).then(({ data }) => {
    t.truthy(data.items.length > 0, 'It should return more than one stocks');
  });
});


================================================
FILE: test/billboardLHBTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get All Stock Data', t => {
  const options = {
    start: '2016-01-15',
    end: '2016-01-15',
  };
  return stock.lhb(options).then(({ data }) => {
    t.truthy(data.items.length > 0, 'It should return more than one stocks');
  });
});


================================================
FILE: test/billboardRankTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Long Period Rank Data by month', t => {
  const options = {
    period: 'month',
    pageNo: 1,
    pageSize: 100,
  };
  return stock.longPeriodRank(options).then(({ data }) => {
    t.truthy(data.items.length === 100, 'It should return 100 records');
  });
});

test('Get Long Period Rank Data by month', t => {
  const options = {
    period: 'quarter',
    pageNo: 1,
    pageSize: 100,
  };
  return stock.longPeriodRank(options).then(({ data }) => {
    t.truthy(data.items.length === 100, 'It should return 100 records');
  });
});


================================================
FILE: test/classifyingAllStocksTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get All Stock Data', t => {
  t.plan(2);
  return stock.getAllStocks().then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array of stocks');
    t.truthy(data.length > 0, 'It should return more than one stocks');
  });
});


================================================
FILE: test/classifyingConceptsTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Concepts Classify', t => {
  t.plan(2);
  return stock.getSinaConceptsClassified().then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array concepts classified data');
    t.truthy(data.length > 0, 'It should return more than one concepts classified data');
  });
});


================================================
FILE: test/classifyingDetailsTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get default classifying Details', t => {
  t.plan(2);
  return stock.getSinaClassifyDetails().then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array of stocks of an industry');
    t.truthy(data.length > 0, 'It should return more than one stocks in an industry');
  });
});

test('Get specified classifying Details', t => {
  t.plan(2);
  const options = {
    tag: 'gn_zndw',
  };
  return stock.getSinaClassifyDetails(options).then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array of stocks of an industry');
    t.truthy(data.length > 0, 'It should return more than one stocks in an industry');
  });
});


================================================
FILE: test/classifyingHS300Test.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get HS300 Stock Data', t => {
  t.plan(2);
  return stock.getHS300().then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array of stocks');
    t.truthy(data.length === 300, 'It should return 300 records');
  });
});


================================================
FILE: test/classifyingIndustryTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Day Price', t => {
  t.plan(2);
  return stock.getSinaIndustryClassified().then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array industry data');
    t.truthy(data.length > 0, 'It should return more than one industry data');
  });
});


================================================
FILE: test/classifyingSZ50Test.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get SZ50 Stock Data', t => {
  t.plan(2);
  return stock.getSZ50().then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array of stocks');
    t.truthy(data.length === 50, 'It should return 50 records');
  });
});


================================================
FILE: test/classifyingXSG.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get XSG Stock Data', t => stock.getXSGData().then(data => {
  t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
    'It should return an array of stocks');
}));


================================================
FILE: test/getkdata.js
================================================
/* eslint no-unused-vars: ["error", {"args" : "none"}]*/
import test from 'ava';
import { stock } from '../src';

const strftime = require('strftime');
const util = require('util');

test.cb('Get Special case for long time ago', t => {
  t.plan(1);
  const opt = {};
  opt.code = '000002';
  opt.start = '2002-01-01';
  opt.end = '2002-04-06';
  const callback = function cb(data) {
    t.truthy(data.length >= 20, 'get data length 20');
    t.end();
  };
  opt.cb = callback;
  opt.args = null;
  stock.getKData(opt).then(callback).catch(err => {
    t.truthy(false, util.format('long time getkdata error %s', err));
  });
});

const _getTimeTick = ts => {
  const sarr = ts.split('-');
  let retval = 0;
  if (sarr.length >= 3) {
    retval += parseInt(sarr[0], 10) * 100 * 100;
    retval += parseInt(sarr[1], 10) * 100;
    retval += parseInt(sarr[2], 10);
    retval *= 10000;
  } else {
    retval += parseInt(ts, 10);
  }
  return retval;
};

const _getTimeTickShort = ts => {
  let retval = 0;
  retval += parseInt(ts, 10);
  return retval;
};


test.cb('Get short time index', t => {
  const opt = {};
  const etime = new Date();
  const stime = new Date(etime.getTime() - (24 * 365 * 3600 * 1000));
  const sdate = strftime('%Y-%d-%m', stime);
  const edate = strftime('%Y-%d-%m', etime);
  opt.code = '000001';
  opt.index = true;
  opt.ktype = '5';
  opt.start = sdate;
  opt.end = edate;
  const callback = function cb(data) {
    let curtick;
    const stick = _getTimeTick(sdate);
    const etick = _getTimeTick(edate);
    let idx = 0;
    t.truthy(data.length >= 20, 'get data for index');
    while (idx < data.length) {
      const d = data[idx];
      curtick = _getTimeTickShort(d[0]);
      t.truthy(curtick >= stick && curtick <= etick, util.format('%s >= %s && %s <= %s', curtick, stick, curtick, etick));
      idx += 1;
    }
    t.end();
  };
  opt.cb = callback;
  opt.args = null;
  stock.getKData(opt).then(callback).catch( err => {
    t.truthy(false, util.format('short time index error %s', err));
    t.end();
  });
});

test.cb('get data for sequence', t => {
  const opt = {};
  const etime = new Date();
  const stime = new Date(etime.getTime() - (24 * 365 * 3600 * 1000));
  const sdate = strftime('%Y-%d-%m', stime);
  const edate = strftime('%Y-%d-%m', etime);
  opt.code = '000001';
  opt.index = true;
  opt.ktype = '5';
  opt.start = sdate;
  opt.end = edate;
  const callback = function cb(data) {
    let curtick;
    let idx = 0;
    let lastval = 0;
    let curval = 0;
    let lastd;
    t.truthy(data.length >= 20, 'get data for index');
    lastd = '';
    data.forEach(function(d) {
      curval = _getTimeTick(d[0]);
      t.truthy(curval >= lastval, util.format('%s for %s not sequence', lastd, d));
      lastval = curval;
      lastd = d;
    });

    t.end();
  };
  opt.cb = callback;
  opt.args = null;
  stock.getKData(opt).then(callback).catch(err => {
      t.truthy( false , util.format('get sequence error %s', err));
      t.end();
  });
});


================================================
FILE: test/stockHistoryTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Day Price', t => {
  t.plan(3);
  const query = { code: '600848' };
  return stock.getHistory(query).then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Object]',
      'It should return the object of day price');
    t.truthy(data.record, 'The returned data should have a key with name `record`');
    t.truthy(data.record.length, 'It should not return an empty array');
  });
});

test('Get Week Price', t => {
  t.plan(3);
  const query = {
    code: '600848',
    ktype: 'week',
  };
  return stock.getHistory(query).then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Object]',
      'It should return the object of day price');
    t.truthy(data.record, 'The returned data should have a key with name `record`');
    t.truthy(data.record.length, 'It should not return an empty array');
  });
});

test('Get Minute Price', t => {
  t.plan(3);
  const query = {
    code: '600848',
    ktype: '15',
  };
  return stock.getHistory(query).then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Object]',
      'It should return the object of day price');
    t.truthy(data.record, 'The returned data should have a key with name `record`');
    t.truthy(data.record.length, 'It should not return an empty array');
  });
});


================================================
FILE: test/stockIndexTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Index Data', t => {
  t.plan(1);
  return stock.getIndex().then(({ data }) => {
    t.truthy(data.length > 0, 'It should return an array of index data');
  });
});


================================================
FILE: test/stockLiveDataTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Live Data', t => {
  t.plan(2);
  const query = {
    codes: [
      '600848',
      '600000',
    ],
  };
  return stock.getLiveData(query).then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array of live data for the specified stock symbol');

    t.truthy(data.length === 2,
      'It should return the same number of stock symbols which passed in');
  });
});


================================================
FILE: test/stockSinaDDTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Sina Highballing Data', t => {
  const query = {
    code: '600848',
    volume: 70,
    date: '2016-08-26',
  };
  return stock.getSinaDD(query).then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array of da dan data');
  });
});


================================================
FILE: test/stockTickTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Tick Data', t => {
  t.plan(1);
  const query = {
    code: '600848',
    date: '2015-12-31',
  };
  return stock.getTick(query).then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return the object of day price');
  });
});


================================================
FILE: test/stockTodayTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Tick Data', t => {
  t.plan(1);
  return stock.getTodayAll().then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Array]',
      'It should return an array of today stock data');
  });
});

test('Get Tick Data', t => {
  t.plan(1);
  const query = {
    pageSize: 80,
    pageNo: 1,
  };
  return stock.getTodayAll(query).then(({ data }) => {
    t.truthy(data.length, 80, 'It should return 80 results');
  });
});


================================================
FILE: test/stockTodayTickTest.js
================================================
import test from 'ava';
import { stock } from '../src';

test('Get Tick Data', t => {
  t.plan(2);
  const query = {
    code: '600848',
    end: '15:00:00',
  };
  return stock.getTodayTick(query).then(({ data }) => {
    t.truthy(Object.prototype.toString.apply(data) === '[object Object]',
      'It should return the object of today tock price');
    t.truthy(Object.prototype.toString.apply(data.zhubi_list) === '[object Array]',
      'It should return an array of ticks');
  });
});
Download .txt
gitextract_ojxgstif/

├── .babelrc
├── .eslintrc
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── README.md
├── example/
│   └── getkdata/
│       ├── getk.js
│       └── merge.js
├── package.json
├── src/
│   ├── index.js
│   ├── stock/
│   │   ├── billboard.js
│   │   ├── classifying.js
│   │   ├── cons.js
│   │   ├── index.js
│   │   ├── trading.js
│   │   ├── urls.js
│   │   └── util.js
│   └── utils/
│       ├── charset.js
│       ├── dateu.js
│       └── fetch.js
└── test/
    ├── billboardDZJYTest.js
    ├── billboardLHBTest.js
    ├── billboardRankTest.js
    ├── classifyingAllStocksTest.js
    ├── classifyingConceptsTest.js
    ├── classifyingDetailsTest.js
    ├── classifyingHS300Test.js
    ├── classifyingIndustryTest.js
    ├── classifyingSZ50Test.js
    ├── classifyingXSG.js
    ├── getkdata.js
    ├── stockHistoryTest.js
    ├── stockIndexTest.js
    ├── stockLiveDataTest.js
    ├── stockSinaDDTest.js
    ├── stockTickTest.js
    ├── stockTodayTest.js
    └── stockTodayTickTest.js
Download .txt
SYMBOL INDEX (16 symbols across 4 files)

FILE: src/stock/cons.js
  constant K_TYPE (line 3) | const K_TYPE = {
  constant INDEX_LABELS (line 10) | const INDEX_LABELS = ['sh', 'sz', 'hs300', 'sz50', 'cyb', 'zxb', 'zx300'...
  constant K_LABELS (line 11) | const K_LABELS = ['day', 'month', 'week'];
  constant K_MIN_LABELS (line 12) | const K_MIN_LABELS = ['1', '5', '15', '30', '30'];
  constant INDEX_LIST (line 14) | const INDEX_LIST = {
  constant DATE_NOW (line 592) | const DATE_NOW = strftime('%Y-%m-%d', new Date());
  constant CUR_YEAR (line 593) | const CUR_YEAR = DATE_NOW.split('-')[0];
  constant CUR_MONTH (line 594) | const CUR_MONTH = DATE_NOW.split('-')[1];

FILE: src/stock/urls.js
  function lhbUrl (line 46) | function lhbUrl(start, end, pageNo = 1, pageSize = 150) {
  function blockTradeUrl (line 51) | function blockTradeUrl(start, end, pageNo = 1, pageSize = 150) {
  function longPeriodRankUrl (line 56) | function longPeriodRankUrl(period = 'month', pageNo = 1, pageSize = 100) {

FILE: src/stock/util.js
  function codeToSymbol (line 7) | function codeToSymbol(code) {
  function csvToObject (line 20) | function csvToObject(csv) {
  function arrayObjectMapping (line 36) | function arrayObjectMapping(fields, items) {
  function randomString (line 61) | function randomString(num) {

FILE: src/utils/dateu.js
  function ttDates (line 1) | function ttDates(sdate, edate) {
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (75K chars).
[
  {
    "path": ".babelrc",
    "chars": 40,
    "preview": "{\n  \"presets\": [\"es2015\", \"stage-2\"]\n}\n\n"
  },
  {
    "path": ".eslintrc",
    "chars": 702,
    "preview": "{\n  \"extends\": \"airbnb\",\n  \"parserOptions\": {\n    \"ecmaVersion\": 6,\n    \"sourceType\": module,\n    \"ecmaFeatures\": {\n    "
  },
  {
    "path": ".gitignore",
    "chars": 71,
    "preview": "node_modules\n.env\ndist\nlib\nlibexample\n.idea\n*.iml\ncoverage\n.nyc_output\n"
  },
  {
    "path": ".npmignore",
    "chars": 5,
    "preview": "src/\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 169,
    "preview": "# v0.3.1\n\n> 获取指定年月限售解禁股数据\n\n```\nstock.getXSGData()\n```\n\n# v0.3.0\n\n> 目标同时支持browser和nodejs环境,因此改用fetch替换掉superagent.\n\n### 这"
  },
  {
    "path": "README.md",
    "chars": 4084,
    "preview": "# tushare.js\n\n这是[tushare](http://tushare.org/)的nodejs版,正在开发当中。如果有感兴趣的同学,非常欢迎一起完善该项目。\n\ntushare的数据大多来自于各大网站股票金融频道的数据。本质上是去"
  },
  {
    "path": "example/getkdata/getk.js",
    "chars": 1174,
    "preview": "/* eslint-disable no-console */\nimport { stock } from '../../lib';\nimport * as extargsparse from 'extargsparse';\n\nconst "
  },
  {
    "path": "example/getkdata/merge.js",
    "chars": 2561,
    "preview": "/* eslint-disable no-console */\nimport * as extargsparse from 'extargsparse';\n\nconst util = require('util');\nconst fs = "
  },
  {
    "path": "package.json",
    "chars": 1742,
    "preview": "{\n  \"name\": \"tushare\",\n  \"version\": \"0.3.1\",\n  \"description\": \"port of tushare for nodejs\",\n  \"main\": \"lib/index.js\",\n  "
  },
  {
    "path": "src/index.js",
    "chars": 53,
    "preview": "import * as stock from './stock';\n\nexport { stock };\n"
  },
  {
    "path": "src/stock/billboard.js",
    "chars": 3860,
    "preview": "import { lhbUrl, blockTradeUrl, longPeriodRankUrl } from './urls';\nimport { codeToSymbol, checkStatus } from './util';\ni"
  },
  {
    "path": "src/stock/classifying.js",
    "chars": 7273,
    "preview": "import {\n  sinaIndustryIndexUrl,\n  sinaClassifyDetailUrl,\n  sinaConceptsIndexUrl,\n  allStockUrl,\n  hs300Url,\n  sz50Url,\n"
  },
  {
    "path": "src/stock/cons.js",
    "chars": 13615,
    "preview": "import strftime from 'strftime';\n\nexport const K_TYPE = {\n  day: 'akdaily',\n  week: 'akweekly',\n  month: 'akmonthly',\n  "
  },
  {
    "path": "src/stock/index.js",
    "chars": 87,
    "preview": "export * from './trading';\nexport * from './classifying';\nexport * from './billboard';\n"
  },
  {
    "path": "src/stock/trading.js",
    "chars": 11374,
    "preview": "import { unnest } from 'ramda';\nimport util from 'util';\n\n/* eslint-disable no-console */\nimport {\n  priceUrl,\n  tickUrl"
  },
  {
    "path": "src/stock/urls.js",
    "chars": 4125,
    "preview": "import { K_TYPE, CUR_YEAR, CUR_MONTH } from './cons';\n\nexport const priceUrl = (ktype, autype, symbol) => {\n  const _kty"
  },
  {
    "path": "src/stock/util.js",
    "chars": 2241,
    "preview": "import parallel from 'async/parallel';\nimport util from 'util';\n\nimport { INDEX_LABELS, INDEX_LIST } from './cons';\n\n\nex"
  },
  {
    "path": "src/utils/charset.js",
    "chars": 553,
    "preview": "/* eslint-disable global-require */\n/* eslint-disable import/newline-after-import */\nexport const charset = enc => res ="
  },
  {
    "path": "src/utils/dateu.js",
    "chars": 435,
    "preview": "export function ttDates(sdate, edate) {\n  const retyears = [];\n  let iyear;\n  let sarr = sdate.split('-');\n  const syear"
  },
  {
    "path": "src/utils/fetch.js",
    "chars": 537,
    "preview": "/* eslint-disable */\nif (typeof module !== undefined && module.exports) {\n  var realFetch = require('no-fetch');\n  modul"
  },
  {
    "path": "test/billboardDZJYTest.js",
    "chars": 308,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get All Stock Data', t => {\n  const options = {\n    start"
  },
  {
    "path": "test/billboardLHBTest.js",
    "chars": 301,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get All Stock Data', t => {\n  const options = {\n    start"
  },
  {
    "path": "test/billboardRankTest.js",
    "chars": 606,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Long Period Rank Data by month', t => {\n  const optio"
  },
  {
    "path": "test/classifyingAllStocksTest.js",
    "chars": 356,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get All Stock Data', t => {\n  t.plan(2);\n  return stock.g"
  },
  {
    "path": "test/classifyingConceptsTest.js",
    "chars": 405,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Concepts Classify', t => {\n  t.plan(2);\n  return stoc"
  },
  {
    "path": "test/classifyingDetailsTest.js",
    "chars": 816,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get default classifying Details', t => {\n  t.plan(2);\n  r"
  },
  {
    "path": "test/classifyingHS300Test.js",
    "chars": 349,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get HS300 Stock Data', t => {\n  t.plan(2);\n  return stock"
  },
  {
    "path": "test/classifyingIndustryTest.js",
    "chars": 375,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Day Price', t => {\n  t.plan(2);\n  return stock.getSin"
  },
  {
    "path": "test/classifyingSZ50Test.js",
    "chars": 345,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get SZ50 Stock Data', t => {\n  t.plan(2);\n  return stock."
  },
  {
    "path": "test/classifyingXSG.js",
    "chars": 243,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get XSG Stock Data', t => stock.getXSGData().then(data =>"
  },
  {
    "path": "test/getkdata.js",
    "chars": 3006,
    "preview": "/* eslint no-unused-vars: [\"error\", {\"args\" : \"none\"}]*/\nimport test from 'ava';\nimport { stock } from '../src';\n\nconst "
  },
  {
    "path": "test/stockHistoryTest.js",
    "chars": 1388,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Day Price', t => {\n  t.plan(3);\n  const query = { cod"
  },
  {
    "path": "test/stockIndexTest.js",
    "chars": 231,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Index Data', t => {\n  t.plan(1);\n  return stock.getIn"
  },
  {
    "path": "test/stockLiveDataTest.js",
    "chars": 501,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Live Data', t => {\n  t.plan(2);\n  const query = {\n   "
  },
  {
    "path": "test/stockSinaDDTest.js",
    "chars": 368,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Sina Highballing Data', t => {\n  const query = {\n    "
  },
  {
    "path": "test/stockTickTest.js",
    "chars": 351,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Tick Data', t => {\n  t.plan(1);\n  const query = {\n   "
  },
  {
    "path": "test/stockTodayTest.js",
    "chars": 514,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Tick Data', t => {\n  t.plan(1);\n  return stock.getTod"
  },
  {
    "path": "test/stockTodayTickTest.js",
    "chars": 490,
    "preview": "import test from 'ava';\nimport { stock } from '../src';\n\ntest('Get Tick Data', t => {\n  t.plan(2);\n  const query = {\n   "
  }
]

About this extraction

This page contains the full source code of the waditu/tushare.js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (64.1 KB), approximately 23.3k tokens, and a symbol index with 16 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!