いなごフライヤーFutures Market Action Checkerのアラート履歴&売買ボリューム可視化ブックマークレットv3 (更新対応)
あいづです。
ホームページが更新されたので対応しました。
導入方法などは過去の記事を見てください。
ソースコード
javascript: class InagoFmacExtension {
constructor(document) {
this.document = document;
this.soundAttributes = {};
this.categories = ["bond", "fx", "stockindex", "stock", "gold", "oil", "agriculture"];
this.dtf = new Intl.DateTimeFormat("jp", {
"hourCycle": "h24",
"year": "numeric",
"month": "2-digit",
"day": "2-digit",
"hour": "2-digit",
"minute": "2-digit",
"second": "2-digit"
});
this.moneyVolumesByTS = {};
this.moneyVolumesSum = {};
this.mvRange = {
max: 0,
min: 0
};
for (const [i, cat] of this.categories.entries()) {
this.moneyVolumesByTS[cat] = {};
this.moneyVolumesSum[cat] = 0;
}
} /* 音声とそれにかかわる属性をまとめる。属性は カテゴリ(bond,fxなど)、上昇下降('up' or 'down') を指し、 音声のId(既存プログラム上では SoundNum )をキーにカテゴリと上昇下降の値を保持する。 */
setupSoundAttributes() {
const marketBoardDataList = [];
const soundIdList = Object.getOwnPropertyNames(inago_sound_setting.sound_data);
this.categories.forEach(cat => {
marketBoardDataList.push({
name: cat,
data: eval(`marketBoardData_${cat}`)
});
});
marketBoardDataList.forEach(mbd => {
for (const [, value] of Object.entries(mbd.data)) {
for (const soundId of soundIdList) {
if (value.alertUpSoundNum === soundId) {
this.soundAttributes[soundId] = {
category: mbd.name,
priceAction: "up"
};
} else if (value.alertDownSoundNum === soundId) {
this.soundAttributes[soundId] = {
category: mbd.name,
priceAction: "down"
};
}
}
}
});
}
createTableId(category) {
return `alert-history_table_${category}`
}
getRightEdgeX() {
var x = 0;
this.document.querySelectorAll(".contents > div:not([style*='word-wrap']):not([id='inago_bar_chart_setting'])").forEach(e => {
let r = e.getBoundingClientRect();
x = Math.max(r.x + e.offsetWidth, x);
});
return x;
} /* 表示枠を作る */
addAlertHistoryView() {
/* 右端に表示するためのX座標の取得。注意書きの領域は無視したいので word-wrap の有無でフィルタリングして X座標を取得している。 */
var x = this.getRightEdgeX(); /* スタイルの挿入 */
this.document.querySelector(".contents").insertAdjacentHTML('beforeBegin', `<style type="text/css"> div.alert-history{width:230px;height:144px;position:absolute;left:${x}px;background-color:#e2e2e2; font-size:10px;} div.alert-history_label{height:14px; background-color: #111111;font-size:10px} .alert-history_table,.alert-history_table td, .alert-history_table th {border-collapse:collapse;border:#9a9a9a 1px solid;} .alert-history_table{width:100%;font-size:10px;font-weight: lighter;background-color:#454545; color:#4a4a4a} .alert-history_table thead th{align:center;background-color:#111111;color:#fafafa} .alert-history_table tbody tr:nth-child(2n+1) td:nth-child(1){background-color:#f5f5f5} .alert-history_table tbody tr:nth-child(2n) td:nth-child(1){background-color:#e2e2e2} .alert-history_table tbody tr:nth-child(2n) td:nth-child(1){background-color:#e2e2e2} .alert-history_table td.up {background-color: #7bffa8;text-align:center} .alert-history_table td.down {background-color: #ffa3d3;text-align:center} </style> `);
for (const [i, cat] of this.categories.entries()) {
this.document.querySelector(".contents").insertAdjacentHTML("beforeEnd", `<div id="${this.createTableId(cat)}" class="alert-history" style="top:${i*144}px;"> <div class="alert-history_label">${cat}</div> <div class="alert-history_label" style="position:absolute;top:0px;right:0px;cursor: pointer;" onclick=" let last=''; let r=[]; document.querySelectorAll('#${this.createTableId(cat)} tbody tr').forEach(tr => { let tds = tr.querySelectorAll('td'); let dt = tds[0].innerText; r.push([dt, tds[1].innerText].join(',')); last= dt; }); if(r.length !== 0){ let l = document.createElement('a'); l.setAttribute('href','data:Application/octet-stream,' + encodeURIComponent(r.join('\\n'))); l.setAttribute('download', '${cat}.'+last+'.csv'); l.click(); }">Save as CSV </div> <div style="height:130px;overflow-y:scroll;"> <table id="${this.createTableId(cat)}" class="alert-history_table" > <thead><tr background-color:#555><th>DateTime</th><th>Up/Down</th></tr></thead> <tbody></tbody> </table> </div> </div>`);
}
} /* 音声は 既存のオブジェクト 'inago_sound' の関数 'play' で再生される。既存のオブジェクトの関数をオーバーライドする。 */
inject2InagoSoundPlay() {
var basePlay = inago_sound.play;
var self = this;
inago_sound.play = function (soundId) {
basePlay.apply(inago_sound, [soundId]);
let ud = self.soundAttributes[soundId].priceAction;
let dt = new Date(inago_sound.last_play_time[soundId]);
let tableId = self.document.querySelector(`#${self.createTableId(self.soundAttributes[soundId].category)} > tbody`).insertAdjacentHTML("afterbegin", `<tr> <td>${self.dtf.format(dt)}</td> <td class="${ud}">${ud}</td> </tr>`);
};
}
getMoneyVolume() {
this.mvRange.max = 0;
this.mvRange.min = 0;
for (const cat of this.categories) {
const d = eval(`inago_chart_${cat}.bar_data`);
this.moneyVolumesSum[cat] = 0;
for (const [ts, vs] of Object.entries(d)) {
this.moneyVolumesByTS[cat][ts] = 0;
for (const [k, v] of Object.entries(vs)) {
this.moneyVolumesByTS[cat][ts] += v[1] - v[0];
}
this.moneyVolumesSum[cat] += this.moneyVolumesByTS[cat][ts];
}
this.mvRange.max = Math.max(this.moneyVolumesSum[cat], this.mvRange.max);
this.mvRange.min = Math.min(this.moneyVolumesSum[cat], this.mvRange.min);
}
}
sweepMoneyVolumeData() {
/* 辞書の肥大化防止 */
const nowts = Date.now() - (1000 * 200);
for (const cat of this.categories) {
for (const [ts, v] of Object.entries(this.moneyVolumesByTS[cat])) {
let t = Number(ts);
if (t && t < nowts) {
delete this.moneyVolumesByTS[cat][ts];
}
}
}
}
addMoneyVolumeView() {
var x = this.getRightEdgeX(); /* スタイルの挿入 */
this.document.querySelector(".contents").insertAdjacentHTML('beforeBegin', ` <style type="text/css"> .mv-area{width: 230px;height: 144px;position:absolute;left:${x}px;background-color:#e2e2e2; font-size:10px;} .mv-bar {display: inline-block;margin: 4px;height: 136px;width: 102px;background:lightgray;} .mv-bar>div{display: flex;align-items: center;} .mv-bar.buy>div {position:absolute;top:0px;height: 136px;width: 102px;} .mv-bar.sell>div {position:absolute;top:0px;height: 136px;width: 102px;} .mv-bar span {position:absolute;top:0px;font-weight: bold;color: #010101;text-align: center;display: block;width: 102px;z-index:2;} </style> `);
for (const [i, cat] of this.categories.entries()) {
this.document.querySelector(".contents").insertAdjacentHTML("beforeEnd", ` <div class="mv-area ${cat}" style="top:${i*144}px;"> <div class="mv-bar sell"> <span></span> <div></div> </div> <div class="mv-bar buy"> <span></span> <div></div> </div> </div> `);
}
}
refreshMoneyVolumesView() {
for (const cat of this.categories) {
let val = this.moneyVolumesSum[cat];
let numLabel = this.formatNumber(val);
let isBuy = val > 0;
let r = isBuy ? Math.abs(val / this.mvRange.max) : Math.abs(val / this.mvRange.min);
let p = r * 100;
let stylep = p.toFixed(2);
let bl = this.document.querySelector(`.mv-area.${cat} .buy>span`);
bl.innerText = isBuy ? numLabel : '';
let b = this.document.querySelector(`.mv-area.${cat} .buy>div`);
b.style = isBuy ? `background: linear-gradient(to right, #7bffa8, #7bffa8 ${stylep}%, #d3d3d3 ${stylep}%)` : '';
let sl = this.document.querySelector(`.mv-area.${cat} .sell span`);
sl.innerText = isBuy ? '' : numLabel;
let s = this.document.querySelector(`.mv-area.${cat} .sell>div`);
s.style = isBuy ? '' : `background: linear-gradient(to left, #ffa3d3, #ffa3d3 ${stylep}%, #d3d3d3 ${stylep}%)`;
}
}
formatNumber(n) {
return Math.abs(n) >= 1.0e+9 ? (Math.abs(n) / 1.0e+9).toFixed(2) + "B" : Math.abs(n) >= 1.0e+6 ? (Math.abs(n) / 1.0e+6).toFixed(2) + "M" : Math.abs(n) >= 1.0e+3 ? (Math.abs(n) / 1.0e+3).toFixed(2) + "K" : (Math.abs(n)).toFixed(2);
}
}
var ife = new InagoFmacExtension(document);
ife.setupSoundAttributes();
ife.addAlertHistoryView();
ife.addMoneyVolumeView();
ife.inject2InagoSoundPlay();
setInterval(() => {
ife.getMoneyVolume();
ife.refreshMoneyVolumesView();
}, 500);
setInterval(() => {
ife.sweepMoneyVolumeData();
}, 18000);
おわりに
本記事は有料設定にしていますが、すべての内容が無料で閲覧できます。
投げ銭用として設定していますので、評価いただければ幸いです。
ここから先は
0字
¥ 100
この記事が気に入ったらチップで応援してみませんか?