Created
September 25, 2021 21:43
-
-
Save kangchihlun/b15d98b619223c2dc03ee0d7696fbd62 to your computer and use it in GitHub Desktop.
支撐壓力線計算-使用 Volume Profile 找過去一日一分鐘最大量區-10跳間隔
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const okex_api = require('../../libs/okex-api') | |
const constant = require('../../libs/constants') | |
const request = require('request') | |
const numgrid = 100 | |
var marketinfo = {} | |
const fetch_market_info = async () => { | |
let _mkt = await okex_api.fetch_perp_symbols(mode='all') | |
_mkt.forEach((inst) => { | |
marketinfo[inst.instrument_id] = inst | |
}) | |
} | |
// 返回最小跳動單位 * n | |
const snap_min_tick_size_mult = (inst1,price,numTick) =>{ | |
let tick_size = parseFloat(marketinfo[inst1].tick_size) | |
let tick_decimal = 0 | |
let ticke_decimal_spl = marketinfo[inst1].tick_size.toString().split('.') | |
if(ticke_decimal_spl.length>1){ | |
tick_decimal = ticke_decimal_spl[1].length | |
} | |
let mult = parseInt(price/tick_size) | |
return (mult*tick_size).toFixed(tick_decimal*numTick) | |
} | |
// price grouping | |
// 目前統一到 marketinfo 最小單位 * 10(10跳) | |
const snap_value_by_yd_grid = (symb,ydc,curval) => { | |
let single_sec = ydc / numgrid | |
let numsec_curval = parseInt(curval / single_sec) | |
return snap_min_tick_size(symb,numsec_curval*single_sec) | |
} | |
/** | |
* Volume Profile 過去一日一分鐘最大量區 | |
*/ | |
const fetch_candle_1M = async (symbol,since) => { | |
return new Promise((resolve,reject) => { | |
let requrl = `${okex_api.baseurl}/api/v5/market/history-candles?instId=${symbol}&bar=1m&after=${since}&limit=1440` | |
if(!(Number.isInteger(since))){ | |
resolve(`end`) | |
}else{ | |
request(requrl ,{ json: true }, async (err, res, body) => { | |
if (err) { return console.log(err) } | |
if(res.statusCode === 200){ | |
resolve(res.body.data) | |
} | |
}) | |
} | |
}) | |
} | |
const collect_history_candles_1m = async (symbol) => { | |
return new Promise(async (resolve, reject)=>{ | |
const day_1m_count = 1440 | |
let collector = new Promise(async (resolve, reject)=>{ | |
let now = Date.now() | |
let lastbartime = now | |
let allKLines = []; | |
(function loopIt() { | |
setTimeout(async()=>{ | |
let kldata = await fetch_candle_1M(symbol,lastbartime-1) | |
if(allKLines.length > day_1m_count){ | |
resolve(allKLines) | |
}else{ | |
allKLines = allKLines.concat(kldata) | |
let _lasttime = parseInt(kldata[kldata.length-1]) | |
lastbartime = _lasttime | |
loopIt() | |
} | |
}, 1000) | |
})() | |
}) | |
collector.then((klines_1min_day) => { | |
let price_vol = {} | |
let numtick = 2 | |
klines_1min_day.forEach((kline) => { | |
let vol = parseFloat(kline[5]) | |
let price = ( parseFloat(kline[2]) + parseFloat(kline[3]) )*0.5 | |
price = snap_min_tick_size_mult(symbol,price,numtick) | |
if(!(price in price_vol)){ | |
price_vol[price] = {price,volume:vol} | |
}else{ | |
price_vol[price][`volume`] += vol | |
} | |
}) | |
klines_1min_day_sorted = Object.values(price_vol).sort(function(a,b){return b.volume - a.volume}) | |
let gridlines = [ | |
parseFloat(klines_1min_day_sorted[0].price) , | |
parseFloat(klines_1min_day_sorted[1].price), | |
parseFloat(klines_1min_day_sorted[2].price), | |
parseFloat(klines_1min_day_sorted[3].price), | |
parseFloat(klines_1min_day_sorted[4].price), | |
] | |
resolve(gridlines) | |
}) | |
}) | |
} | |
const fetch_volume_profile_by_symbol = async (symbol) => { | |
await fetch_market_info() | |
return await collect_history_candles_1m(symbol) | |
} | |
/** | |
* 3分鐘k 21 ma | |
*/ | |
const fetch_candle_3M = async (symbol,since,limit) => { | |
return new Promise((resolve,reject) => { | |
let requrl = `${okex_api.baseurl}/api/v5/market/history-candles?instId=${symbol}&bar=3m&after=${since}&limit=${limit}` | |
if(!(Number.isInteger(since))){ | |
resolve(`end`) | |
}else{ | |
request(requrl ,{ json: true }, async (err, res, body) => { | |
if (err) { return console.log(err) } | |
if(res.statusCode === 200){ | |
resolve(res.body.data) | |
} | |
}) | |
} | |
}) | |
} | |
const getMean = (array) => { | |
const n = array.length | |
return array.reduce((a, b) => a + b) / n | |
} | |
const getStandardDeviation = (array) => { | |
const n = array.length | |
const mean = array.reduce((a, b) => a + b) / n | |
return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n) | |
} | |
const fetch_boll_3m = async (symbol) => { | |
const ma_count = 20 | |
const numtick = 2 | |
return new Promise(async (resolve, reject)=>{ | |
let now = Date.now() | |
let kldata = await fetch_candle_3M(symbol,now,ma_count) | |
let closearr = kldata.map((kl) => parseFloat(kl[4])) | |
let std = getStandardDeviation(closearr) | |
let mean = getMean(closearr) | |
upband = snap_min_tick_size_mult(symbol,mean+std*2,numtick) | |
lowband = snap_min_tick_size_mult(symbol,mean-std*2,numtick) | |
resolve( [ | |
parseFloat(upband), | |
parseFloat(lowband), | |
] ) | |
}) | |
} | |
/** | |
* 關卡價關鍵價 | |
*/ | |
const fetch_grids = async (symb) => { | |
let grids = await fetch_volume_profile_by_symbol(symb) | |
let bo_3m_20 = await fetch_boll_3m(symb) | |
grids = grids.concat(bo_3m_20) | |
grids_sorted = grids.sort((a,b) => {return (a-b)}) | |
return grids_sorted | |
} | |
module.exports = { | |
fetch_volume_profile_by_symbol, | |
fetch_boll_3m, | |
fetch_grids, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment